Skip to content

Commit

Permalink
quorum_cop: added quorum cop and service node keys to lokid
Browse files Browse the repository at this point in the history
Submit deregistration autonomously, prune uptime_proofs

Move pruner to relayer, fix inf. loop, quorum participation bool return

find_if, fix reorg logic, make 2hrs check a variable

Add iterator assign on erase, adjust detached check

qacop: review iteration, changed warnings and std::find call

Accept proofs excluding the added time buffer
  • Loading branch information
jcktm committed Aug 2, 2018
1 parent 9309f8a commit ace26fc
Show file tree
Hide file tree
Showing 16 changed files with 438 additions and 33 deletions.
5 changes: 4 additions & 1 deletion src/cryptonote_config.h
Expand Up @@ -60,6 +60,10 @@

#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW 11

#define UPTIME_PROOF_BUFFER_IN_SECONDS (5*60)
#define UPTIME_PROOF_FREQUENCY_IN_SECONDS (60*60)
#define UPTIME_PROOF_MAX_TIME_IN_SECONDS (UPTIME_PROOF_FREQUENCY_IN_SECONDS + (2 * UPTIME_PROOF_BUFFER_IN_SECONDS))

// MONEY_SUPPLY - total number coins to be generated
#define MONEY_SUPPLY ((uint64_t)(-1))
#define EMISSION_SPEED_FACTOR_PER_MINUTE (20)
Expand Down Expand Up @@ -87,7 +91,6 @@

#define ORPHANED_BLOCKS_MAX_COUNT 100


#define DIFFICULTY_TARGET_V2 120 // seconds
#define DIFFICULTY_WINDOW_V2 60
#define DIFFICULTY_LAG 15 // !!!
Expand Down
2 changes: 2 additions & 0 deletions src/cryptonote_core/CMakeLists.txt
Expand Up @@ -32,6 +32,7 @@ set(cryptonote_core_sources
cryptonote_core.cpp
service_node_list.cpp
service_node_deregister.cpp
quorum_cop.cpp
tx_pool.cpp
cryptonote_tx_utils.cpp)

Expand All @@ -41,6 +42,7 @@ set(cryptonote_core_private_headers
blockchain_storage_boost_serialization.h
blockchain.h
service_node_list.h
quorum_cop.h
cryptonote_core.h
service_node_deregister.h
tx_pool.h
Expand Down
35 changes: 35 additions & 0 deletions src/cryptonote_core/cryptonote_core.cpp
Expand Up @@ -172,6 +172,7 @@ namespace cryptonote
m_mempool(m_blockchain_storage),
m_service_node_list(m_blockchain_storage),
m_blockchain_storage(m_mempool, m_service_node_list, m_deregister_vote_pool),
m_quorum_cop(*this, m_service_node_list),
m_miner(this),
m_miner_address(boost::value_initialized<account_public_address>()),
m_starter_message_showed(false),
Expand Down Expand Up @@ -527,6 +528,8 @@ namespace cryptonote
m_blockchain_storage.set_user_options(blocks_threads,
blocks_per_sync, sync_mode, fast_sync);

// NOTE: Hooks must be registered before blockchain is initialised for the blockchain init hook to occur.
m_service_node_list.register_hooks(m_quorum_cop);
r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, test_options);

r = m_mempool.init(max_txpool_size);
Expand Down Expand Up @@ -1090,6 +1093,23 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::submit_uptime_proof()
{
if (m_service_node)
{
cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context);
NOTIFY_UPTIME_PROOF::request r;
m_quorum_cop.generate_uptime_proof_request(m_service_node_pubkey, m_service_node_key, r);
get_protocol()->relay_uptime_proof(r, fake_context);
}
return true;
}
//-----------------------------------------------------------------------------------------------
bool core::handle_uptime_proof(uint64_t timestamp, const crypto::public_key& pubkey, const crypto::signature& sig)
{
return m_quorum_cop.handle_uptime_proof(timestamp, pubkey, sig);
}
//-----------------------------------------------------------------------------------------------
void core::on_transaction_relayed(const cryptonote::blobdata& tx_blob)
{
std::list<std::pair<crypto::hash, cryptonote::blobdata>> txs;
Expand Down Expand Up @@ -1439,6 +1459,12 @@ namespace cryptonote
m_deregisters_auto_relayer.do_call(boost::bind(&core::relay_deregister_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));
if (m_service_node && m_service_node_list.is_service_node(m_service_node_pubkey))
{
m_submit_uptime_proof_interval.do_call(boost::bind(&core::submit_uptime_proof, this));
m_uptime_proof_pruner.do_call(boost::bind(&service_nodes::quorum_cop::prune_uptime_proof, &m_quorum_cop));
}

m_miner.on_idle();
m_mempool.on_idle();
return true;
Expand Down Expand Up @@ -1702,6 +1728,15 @@ namespace cryptonote

return result;
}
bool core::get_service_node_keys(crypto::public_key &pub_key, crypto::secret_key &sec_key) const
{
if (m_service_node)
{
pub_key = m_service_node_pubkey;
sec_key = m_service_node_key;
}
return m_service_node;
}
//-----------------------------------------------------------------------------------------------
std::string core::prepare_registration(const std::vector<std::string>& args)
{
Expand Down
31 changes: 31 additions & 0 deletions src/cryptonote_core/cryptonote_core.h
Expand Up @@ -45,6 +45,7 @@
#include "tx_pool.h"
#include "blockchain.h"
#include "service_node_list.h"
#include "quorum_cop.h"
#include "cryptonote_basic/miner.h"
#include "cryptonote_basic/connection_context.h"
#include "cryptonote_basic/cryptonote_stat_info.h"
Expand Down Expand Up @@ -106,6 +107,15 @@ namespace cryptonote
*/
bool on_idle();

/**
* @brief handles an incoming uptime proof
*
* Parses an incoming uptime proof
*
* @return true if we haven't seen it before and thus need to relay.
*/
bool handle_uptime_proof(uint64_t timestamp, const crypto::public_key& pubkey, const crypto::signature& sig);

/**
* @brief handles an incoming transaction
*
Expand Down Expand Up @@ -808,6 +818,24 @@ namespace cryptonote
*/
bool cmd_prepare_registration(const boost::program_options::variables_map& vm, const std::vector<std::string>& args);

/**
* @brief Return the account associated to this service node.
* @param pub_key The public key for the service node, unmodified if not a service node
* @param sec_key The secret key for the service node, unmodified if not a service node
* @return True if we are a service node
*/
bool get_service_node_keys(crypto::public_key &pub_key, crypto::secret_key &sec_key) const;

/**
* @brief attempts to submit an uptime proof to the network, if this is running in service node mode
*
* @return true
*/
bool submit_uptime_proof();

private:

/**
Expand Down Expand Up @@ -1006,6 +1034,7 @@ namespace cryptonote
tx_memory_pool m_mempool; //!< transaction pool instance
Blockchain m_blockchain_storage; //!< Blockchain instance
service_nodes::service_node_list m_service_node_list;
service_nodes::quorum_cop m_quorum_cop;

i_cryptonote_protocol* m_pprotocol; //!< cryptonote protocol instance

Expand All @@ -1025,6 +1054,8 @@ namespace cryptonote
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_FREQUENCY_IN_SECONDS, true> m_submit_uptime_proof_interval; //!< interval for submitting uptime proof
epee::math_helper::once_a_time_seconds<30, true> m_uptime_proof_pruner;

std::atomic<bool> m_starter_message_showed; //!< has the "daemon will sync now" message been shown?

Expand Down
191 changes: 191 additions & 0 deletions src/cryptonote_core/quorum_cop.cpp
@@ -0,0 +1,191 @@
// Copyright (c) 2018, The Loki Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "service_node_deregister.h"
#include "service_node_list.h"
#include "cryptonote_config.h"
#include "cryptonote_core.h"
#include "quorum_cop.h"

namespace service_nodes
{
quorum_cop::quorum_cop(cryptonote::core& core, service_nodes::service_node_list& service_node_list)
: m_core(core), m_service_node_list(service_node_list), m_last_height(0)
{
}

void quorum_cop::blockchain_detached(uint64_t height)
{
if (m_last_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("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;
}
}

void quorum_cop::block_added(const cryptonote::block& block, const std::vector<cryptonote::transaction>& txs)
{
crypto::public_key my_pubkey;
crypto::secret_key my_seckey;
if (!m_core.get_service_node_keys(my_pubkey, my_seckey))
return;

time_t const now = time(nullptr);
time_t const min_lifetime = 60 * 60 * 2;
bool alive_for_min_time = (now - m_core.get_start_time()) >= min_lifetime;
if (!alive_for_min_time)
{
return;
}

uint64_t const height = cryptonote::get_block_height(block);
uint64_t const latest_height = m_core.get_current_blockchain_height();

if (latest_height < loki::service_node_deregister::VOTE_LIFETIME_BY_HEIGHT)
return;

uint64_t const execute_justice_from_height = latest_height - loki::service_node_deregister::VOTE_LIFETIME_BY_HEIGHT;
if (height < execute_justice_from_height)
return;

if (m_last_height < execute_justice_from_height)
m_last_height = execute_justice_from_height;


for (;m_last_height < (height - REORG_SAFETY_BUFFER_IN_BLOCKS); m_last_height++)
{
const std::shared_ptr<quorum_state> state = m_core.get_quorum_state(m_last_height);
if (!state)
{
// TODO(loki): Fatal error
LOG_ERROR("Quorum state for height: " << m_last_height << "was not cached in daemon!");
continue;
}

auto it = std::find(state->quorum_nodes.begin(), state->quorum_nodes.end(), my_pubkey);
if (it == state->quorum_nodes.end())
continue;

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)
{
const crypto::public_key &node_key = state->nodes_to_test[node_index];

CRITICAL_REGION_LOCAL(m_lock);
bool vote_off_node = (m_uptime_proof_seen.find(node_key) != m_uptime_proof_seen.end());

if (!vote_off_node)
continue;

loki::service_node_deregister::vote vote = {};
vote.block_height = m_last_height;
vote.service_node_index = node_index;
vote.voters_quorum_index = my_index_in_quorum;
vote.signature = loki::service_node_deregister::sign_vote(vote.block_height, vote.service_node_index, my_pubkey, my_seckey);

cryptonote::vote_verification_context vvc = {};
if (!m_core.add_deregister_vote(vote, vvc))
{
if (vvc.m_invalid_block_height)
LOG_ERROR("block height was invalid: " << vote.block_height);

if (vvc.m_voters_quorum_index_out_of_bounds)
LOG_ERROR("voters quorum index specified out of bounds: " << vote.voters_quorum_index);

if (vvc.m_service_node_index_out_of_bounds)
LOG_ERROR("service node index specified out of bounds: " << vote.service_node_index);

if (vvc.m_signature_not_valid)
LOG_ERROR("signature was not valid, was the signature signed properly?");
}
}
}
}

static crypto::hash make_hash(crypto::public_key const &pubkey, uint64_t timestamp)
{
char buf[44] = "SUP"; // Meaningless magic bytes
crypto::hash result;
memcpy(buf + 4, reinterpret_cast<const void *>(&pubkey), sizeof(pubkey));
memcpy(buf + 4 + sizeof(pubkey), reinterpret_cast<const void *>(&timestamp), sizeof(timestamp));
crypto::cn_fast_hash(buf, sizeof(buf), result);

return result;
}

bool quorum_cop::handle_uptime_proof(uint64_t timestamp, const crypto::public_key& pubkey, const crypto::signature& sig)
{
uint64_t now = time(nullptr);

if ((timestamp < now - UPTIME_PROOF_BUFFER_IN_SECONDS) || (timestamp > now + UPTIME_PROOF_BUFFER_IN_SECONDS))
return false;

// TODO(doyle): the only dependency on m_service_node_lists which could be
// replaced by the lists stored in db when that is implemented - 2018-07-24
if (!m_service_node_list.is_service_node(pubkey))
return false;

CRITICAL_REGION_LOCAL(m_lock);
if (m_uptime_proof_seen[pubkey] >= now - (UPTIME_PROOF_FREQUENCY_IN_SECONDS / 2))
return false; // already received one uptime proof for this node recently.

crypto::hash hash = make_hash(pubkey, timestamp);
if (!crypto::check_signature(hash, pubkey, sig))
return false;

m_uptime_proof_seen[pubkey] = timestamp;
return true;
}

void quorum_cop::generate_uptime_proof_request(const crypto::public_key& pubkey, const crypto::secret_key& seckey, cryptonote::NOTIFY_UPTIME_PROOF::request& req) const
{
req.timestamp = time(nullptr);
req.pubkey = pubkey;

crypto::hash hash = make_hash(req.pubkey, req.timestamp);
crypto::generate_signature(hash, pubkey, seckey, req.sig);
}

bool quorum_cop::prune_uptime_proof()
{
uint64_t now = time(nullptr);
const uint64_t prune_from_timestamp = now - UPTIME_PROOF_MAX_TIME_IN_SECONDS;
CRITICAL_REGION_LOCAL(m_lock);

for (auto it = m_uptime_proof_seen.begin(); it != m_uptime_proof_seen.end();)
{
if (it->second < prune_from_timestamp)
it = m_uptime_proof_seen.erase(it);
else
it++;
}

return true;
}
}

0 comments on commit ace26fc

Please sign in to comment.