dncurrency/nano/node/voting.hpp
2023-09-20 18:33:05 +01:00

179 lines
5.8 KiB
C++

#pragma once
#include <nano/lib/locks.hpp>
#include <nano/lib/numbers.hpp>
#include <nano/lib/processing_queue.hpp>
#include <nano/lib/utility.hpp>
#include <nano/node/wallet.hpp>
#include <nano/secure/common.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index_container.hpp>
#include <condition_variable>
#include <deque>
#include <thread>
namespace mi = boost::multi_index;
namespace nano
{
class ledger;
class network;
class node_config;
class stats;
class vote_processor;
class wallets;
namespace transport
{
class channel;
}
class vote_spacing final
{
class entry
{
public:
nano::root root;
std::chrono::steady_clock::time_point time;
nano::block_hash hash;
};
boost::multi_index_container<entry,
mi::indexed_by<
mi::hashed_non_unique<mi::tag<class tag_root>,
mi::member<entry, nano::root, &entry::root>>,
mi::ordered_non_unique<mi::tag<class tag_time>,
mi::member<entry, std::chrono::steady_clock::time_point, &entry::time>>>>
recent;
std::chrono::milliseconds const delay;
void trim ();
public:
vote_spacing (std::chrono::milliseconds const & delay) :
delay{ delay }
{
}
bool votable (nano::root const & root_a, nano::block_hash const & hash_a) const;
void flag (nano::root const & root_a, nano::block_hash const & hash_a);
std::size_t size () const;
};
class local_vote_history final
{
class local_vote final
{
public:
local_vote (nano::root const & root_a, nano::block_hash const & hash_a, std::shared_ptr<nano::vote> const & vote_a) :
root (root_a),
hash (hash_a),
vote (vote_a)
{
}
nano::root root;
nano::block_hash hash;
std::shared_ptr<nano::vote> vote;
};
public:
local_vote_history (nano::voting_constants const & constants) :
constants{ constants }
{
}
void add (nano::root const & root_a, nano::block_hash const & hash_a, std::shared_ptr<nano::vote> const & vote_a);
void erase (nano::root const & root_a);
std::vector<std::shared_ptr<nano::vote>> votes (nano::root const & root_a, nano::block_hash const & hash_a, bool const is_final_a = false) const;
bool exists (nano::root const &) const;
std::size_t size () const;
private:
// clang-format off
boost::multi_index_container<local_vote,
mi::indexed_by<
mi::hashed_non_unique<mi::tag<class tag_root>,
mi::member<local_vote, nano::root, &local_vote::root>>,
mi::sequenced<mi::tag<class tag_sequence>>>>
history;
// clang-format on
nano::voting_constants const & constants;
void clean ();
std::vector<std::shared_ptr<nano::vote>> votes (nano::root const & root_a) const;
// Only used in Debug
bool consistency_check (nano::root const &) const;
mutable nano::mutex mutex;
friend std::unique_ptr<container_info_component> collect_container_info (local_vote_history & history, std::string const & name);
friend class local_vote_history_basic_Test;
};
std::unique_ptr<container_info_component> collect_container_info (local_vote_history & history, std::string const & name);
class vote_generator final
{
private:
using candidate_t = std::pair<nano::root, nano::block_hash>;
using request_t = std::pair<std::vector<candidate_t>, std::shared_ptr<nano::transport::channel>>;
using queue_entry_t = std::pair<nano::root, nano::block_hash>;
public:
vote_generator (nano::node_config const & config_a, nano::ledger & ledger_a, nano::wallets & wallets_a, nano::vote_processor & vote_processor_a, nano::local_vote_history & history_a, nano::network & network_a, nano::stats & stats_a, bool is_final_a);
~vote_generator ();
/** Queue items for vote generation, or broadcast votes already in cache */
void add (nano::root const &, nano::block_hash const &);
/** Queue blocks for vote generation, returning the number of successful candidates.*/
std::size_t generate (std::vector<std::shared_ptr<nano::block>> const & blocks_a, std::shared_ptr<nano::transport::channel> const & channel_a);
void set_reply_action (std::function<void (std::shared_ptr<nano::vote> const &, std::shared_ptr<nano::transport::channel> const &)>);
void start ();
void stop ();
private:
void run ();
void broadcast (nano::unique_lock<nano::mutex> &);
void reply (nano::unique_lock<nano::mutex> &, request_t &&);
void vote (std::vector<nano::block_hash> const &, std::vector<nano::root> const &, std::function<void (std::shared_ptr<nano::vote> const &)> const &);
void broadcast_action (std::shared_ptr<nano::vote> const &) const;
void process_batch (std::deque<queue_entry_t> & batch);
/**
* Check if block is eligible for vote generation, then generates a vote or broadcasts votes already in cache
* @param transaction : needs `tables::final_votes` lock
*/
void process (store::write_transaction const &, nano::root const &, nano::block_hash const &);
private:
std::function<void (std::shared_ptr<nano::vote> const &, std::shared_ptr<nano::transport::channel> &)> reply_action; // must be set only during initialization by using set_reply_action
private: // Dependencies
nano::node_config const & config;
nano::ledger & ledger;
nano::wallets & wallets;
nano::vote_processor & vote_processor;
nano::local_vote_history & history;
nano::vote_spacing spacing;
nano::network & network;
nano::stats & stats;
private:
processing_queue<queue_entry_t> vote_generation_queue;
private:
const bool is_final;
mutable nano::mutex mutex;
nano::condition_variable condition;
static std::size_t constexpr max_requests{ 2048 };
std::deque<request_t> requests;
std::deque<candidate_t> candidates;
std::atomic<bool> stopped{ false };
std::thread thread;
friend std::unique_ptr<container_info_component> collect_container_info (vote_generator & vote_generator, std::string const & name);
};
std::unique_ptr<container_info_component> collect_container_info (vote_generator & generator, std::string const & name);
}