dncurrency/nano/node/vote_router.cpp
2025-01-09 19:40:21 +01:00

217 lines
No EOL
5.4 KiB
C++

#include <nano/lib/enum_util.hpp>
#include <nano/lib/thread_roles.hpp>
#include <nano/lib/utility.hpp>
#include <nano/node/active_elections.hpp>
#include <nano/node/election.hpp>
#include <nano/node/vote_cache.hpp>
#include <nano/node/vote_router.hpp>
#include <nano/secure/vote.hpp>
#include <chrono>
using namespace std::chrono_literals;
nano::vote_router::vote_router (nano::vote_cache & vote_cache_a, nano::recently_confirmed_cache & recently_confirmed_a) :
vote_cache{ vote_cache_a },
recently_confirmed{ recently_confirmed_a }
{
}
nano::vote_router::~vote_router ()
{
// Thread must be stopped before destruction
debug_assert (!thread.joinable ());
}
void nano::vote_router::connect (nano::block_hash const & hash, std::weak_ptr<nano::election> election)
{
std::unique_lock lock{ mutex };
elections.insert_or_assign (hash, election);
}
void nano::vote_router::disconnect (nano::election const & election)
{
std::unique_lock lock{ mutex };
for (auto const & [hash, _] : election.blocks ())
{
elections.erase (hash);
}
}
void nano::vote_router::disconnect (nano::block_hash const & hash)
{
std::unique_lock lock{ mutex };
[[maybe_unused]] auto erased = elections.erase (hash);
debug_assert (erased == 1);
}
// Validate a vote and apply it to the current election if one exists
std::unordered_map<nano::block_hash, nano::vote_code> nano::vote_router::vote (std::shared_ptr<nano::vote> const & vote, nano::vote_source source, nano::block_hash filter)
{
debug_assert (!vote->validate ()); // false => valid vote
// If present, filter should be set to one of the hashes in the vote
debug_assert (filter.is_zero () || std::any_of (vote->hashes.begin (), vote->hashes.end (), [&filter] (auto const & hash) {
return hash == filter;
}));
std::unordered_map<nano::block_hash, nano::vote_code> results;
std::unordered_map<nano::block_hash, std::shared_ptr<nano::election>> process;
{
std::shared_lock lock{ mutex };
for (auto const & hash : vote->hashes)
{
// Ignore votes for other hashes if a filter is set
if (!filter.is_zero () && hash != filter)
{
continue;
}
// Ignore duplicate hashes (should not happen with a well-behaved voting node)
if (results.find (hash) != results.end ())
{
continue;
}
auto find_election = [this] (auto const & hash) -> std::shared_ptr<nano::election> {
if (auto existing = elections.find (hash); existing != elections.end ())
{
return existing->second.lock ();
}
return {};
};
if (auto election = find_election (hash))
{
process[hash] = election;
}
else
{
if (!recently_confirmed.exists (hash))
{
results[hash] = nano::vote_code::indeterminate;
}
else
{
results[hash] = nano::vote_code::replay;
}
}
}
}
for (auto const & [block_hash, election] : process)
{
auto const vote_result = election->vote (vote->account, vote->timestamp (), block_hash, source);
results[block_hash] = vote_result;
}
// All hashes should have their result set
debug_assert (!filter.is_zero () || std::all_of (vote->hashes.begin (), vote->hashes.end (), [&results] (auto const & hash) {
return results.find (hash) != results.end ();
}));
// Cache the votes that didn't match any election
if (source != nano::vote_source::cache)
{
vote_cache.insert (vote, results);
}
vote_processed.notify (vote, source, results);
return results;
}
bool nano::vote_router::active (nano::block_hash const & hash) const
{
std::shared_lock lock{ mutex };
if (auto existing = elections.find (hash); existing != elections.end ())
{
if (auto election = existing->second.lock (); election != nullptr)
{
return true;
}
}
return false;
}
std::shared_ptr<nano::election> nano::vote_router::election (nano::block_hash const & hash) const
{
std::shared_lock lock{ mutex };
if (auto existing = elections.find (hash); existing != elections.end ())
{
if (auto election = existing->second.lock (); election != nullptr)
{
return election;
}
}
return nullptr;
}
// This is meant to be a fast check and may return false positives if weak pointers have expired, but we don't care about that here
bool nano::vote_router::contains (nano::block_hash const & hash) const
{
std::shared_lock lock{ mutex };
return elections.contains (hash);
}
void nano::vote_router::start ()
{
thread = std::thread{ [this] () {
nano::thread_role::set (nano::thread_role::name::vote_router);
run ();
} };
}
void nano::vote_router::stop ()
{
std::unique_lock lock{ mutex };
stopped = true;
lock.unlock ();
condition.notify_all ();
if (thread.joinable ())
{
thread.join ();
}
}
void nano::vote_router::run ()
{
std::unique_lock lock{ mutex };
while (!stopped)
{
std::erase_if (elections, [] (auto const & pair) { return pair.second.lock () == nullptr; });
condition.wait_for (lock, 15s, [&] () { return stopped; });
}
}
nano::container_info nano::vote_router::container_info () const
{
std::shared_lock lock{ mutex };
nano::container_info info;
info.put ("elections", elections);
return info;
}
/*
*
*/
nano::stat::detail nano::to_stat_detail (nano::vote_code code)
{
return nano::enum_util::cast<nano::stat::detail> (code);
}
std::string_view nano::to_string (nano::vote_code code)
{
return nano::enum_util::name (code);
}
nano::stat::detail nano::to_stat_detail (nano::vote_source source)
{
return nano::enum_util::cast<nano::stat::detail> (source);
}
std::string_view nano::to_string (nano::vote_source source)
{
return nano::enum_util::name (source);
}