dncurrency/nano/node/vote_cache.hpp
2024-11-26 18:33:34 +01:00

192 lines
5 KiB
C++

#pragma once
#include <nano/lib/interval.hpp>
#include <nano/lib/locks.hpp>
#include <nano/lib/numbers.hpp>
#include <nano/lib/utility.hpp>
#include <nano/node/fwd.hpp>
#include <nano/secure/common.hpp>
#include <nano/secure/fwd.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/mem_fun.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 <deque>
#include <functional>
#include <memory>
#include <optional>
#include <unordered_set>
#include <vector>
namespace mi = boost::multi_index;
namespace nano
{
class vote_cache_config final
{
public:
nano::error deserialize (nano::tomlconfig & toml);
nano::error serialize (nano::tomlconfig & toml) const;
public:
std::size_t max_size{ 1024 * 64 };
std::size_t max_voters{ 64 };
std::chrono::seconds age_cutoff{ 15 * 60 };
};
/**
* Stores votes associated with a single block hash
*/
class vote_cache_entry final
{
private:
struct voter_entry
{
nano::account representative;
nano::uint128_t weight;
std::shared_ptr<nano::vote> vote;
};
public:
explicit vote_cache_entry (nano::block_hash const & hash);
/**
* Adds a vote into a list, checks for duplicates and updates timestamp if new one is greater
* @return true if current tally changed, false otherwise
*/
bool vote (std::shared_ptr<nano::vote> const & vote, nano::uint128_t const & rep_weight, std::size_t max_voters);
std::size_t size () const;
std::vector<std::shared_ptr<nano::vote>> votes () const;
public: // Keep accessors inlined
nano::block_hash hash () const
{
return hash_m;
}
std::chrono::steady_clock::time_point last_vote () const
{
return last_vote_m;
}
nano::uint128_t tally () const
{
return tally_m;
}
nano::uint128_t final_tally () const
{
return final_tally_m;
}
private:
bool vote_impl (std::shared_ptr<nano::vote> const & vote, nano::uint128_t const & rep_weight, std::size_t max_voters);
std::pair<nano::uint128_t, nano::uint128_t> calculate_tally () const; // <tally, final_tally>
// clang-format off
class tag_representative {};
class tag_weight {};
// clang-format on
// clang-format off
using ordered_voters = boost::multi_index_container<voter_entry,
mi::indexed_by<
mi::hashed_unique<mi::tag<tag_representative>,
mi::member<voter_entry, nano::account, &voter_entry::representative>>,
mi::ordered_non_unique<mi::tag<tag_weight>,
mi::member<voter_entry, nano::uint128_t, &voter_entry::weight>>
>>;
// clang-format on
ordered_voters voters;
nano::block_hash const hash_m;
std::chrono::steady_clock::time_point last_vote_m{};
nano::uint128_t tally_m{ 0 };
nano::uint128_t final_tally_m{ 0 };
};
class vote_cache final
{
public:
using entry = vote_cache_entry;
public:
explicit vote_cache (vote_cache_config const &, nano::stats &);
/**
* Adds a new vote to cache
*/
void insert (
std::shared_ptr<nano::vote> const & vote,
std::unordered_map<nano::block_hash, nano::vote_code> const & results = {});
/**
* Tries to find an entry associated with block hash
*/
std::vector<std::shared_ptr<nano::vote>> find (nano::block_hash const & hash) const;
bool contains (nano::block_hash const & hash) const;
/**
* Removes an entry associated with block hash, does nothing if entry does not exist
* @return true if hash existed and was erased, false otherwise
*/
bool erase (nano::block_hash const & hash);
void clear ();
std::size_t size () const;
bool empty () const;
struct top_entry
{
nano::block_hash hash;
nano::uint128_t tally;
nano::uint128_t final_tally;
};
/**
* Returns blocks with highest observed tally
* The blocks are sorted in descending order by final tally, then by tally
* @param min_tally minimum tally threshold, entries below with their voting weight below this will be ignored
*/
std::deque<top_entry> top (nano::uint128_t const & min_tally);
nano::container_info container_info () const;
public:
/**
* Function used to query rep weight for tally calculation
*/
std::function<nano::uint128_t (nano::account const &)> rep_weight_query{ [] (nano::account const & rep) { debug_assert (false); return 0; } };
private: // Dependencies
vote_cache_config const & config;
nano::stats & stats;
private:
void insert_impl (std::shared_ptr<nano::vote> const &, nano::block_hash const & hash, nano::uint128_t const & rep_weight);
void cleanup ();
// clang-format off
class tag_sequenced {};
class tag_hash {};
class tag_tally {};
// clang-format on
// clang-format off
using ordered_cache = boost::multi_index_container<entry,
mi::indexed_by<
mi::hashed_unique<mi::tag<tag_hash>,
mi::const_mem_fun<entry, nano::block_hash, &entry::hash>>,
mi::sequenced<mi::tag<tag_sequenced>>,
mi::ordered_non_unique<mi::tag<tag_tally>,
mi::const_mem_fun<entry, nano::uint128_t, &entry::tally>, std::greater<>> // DESC
>>;
// clang-format on
ordered_cache cache;
mutable nano::mutex mutex;
nano::interval cleanup_interval;
};
}