diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index 03c05211..f139d752 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/core_test/request_aggregator.cpp b/nano/core_test/request_aggregator.cpp index 3851fa82..3bb865f9 100644 --- a/nano/core_test/request_aggregator.cpp +++ b/nano/core_test/request_aggregator.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/core_test/voting.cpp b/nano/core_test/voting.cpp index d6d64429..c1122bc8 100644 --- a/nano/core_test/voting.cpp +++ b/nano/core_test/voting.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/nano/lib/numbers.hpp b/nano/lib/numbers.hpp index 3021524c..7396de3a 100644 --- a/nano/lib/numbers.hpp +++ b/nano/lib/numbers.hpp @@ -389,4 +389,12 @@ struct hash> return hash (hash_a); } }; +template <> +struct hash<::nano::root> +{ + size_t operator() (::nano::root const & value_a) const + { + return std::hash<::nano::root> () (value_a); + } +}; } diff --git a/nano/node/CMakeLists.txt b/nano/node/CMakeLists.txt index aee2e628..f3283f22 100644 --- a/nano/node/CMakeLists.txt +++ b/nano/node/CMakeLists.txt @@ -98,6 +98,8 @@ add_library( json_handler.cpp local_block_broadcaster.cpp local_block_broadcaster.hpp + local_vote_history.cpp + local_vote_history.hpp make_store.hpp make_store.cpp network.hpp diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index 8d966dbb..0ef5dcbd 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -53,8 +53,9 @@ public: // Tests private: // clang-format off - class tag_root {}; class tag_hash {}; + class tag_root {}; + class tag_sequence {}; using ordered_recent_confirmations = boost::multi_index_container #include #include +#include #include #include #include diff --git a/nano/node/election.cpp b/nano/node/election.cpp index a41136e4..3349d016 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/node/local_vote_history.cpp b/nano/node/local_vote_history.cpp new file mode 100644 index 00000000..8569c225 --- /dev/null +++ b/nano/node/local_vote_history.cpp @@ -0,0 +1,115 @@ +#include +#include +#include + +bool nano::local_vote_history::consistency_check (nano::root const & root_a) const +{ + auto & history_by_root (history.get ()); + auto const range (history_by_root.equal_range (root_a)); + // All cached votes for a root must be for the same hash, this is actively enforced in local_vote_history::add + auto consistent_same = std::all_of (range.first, range.second, [hash = range.first->hash] (auto const & info_a) { return info_a.hash == hash; }); + std::vector accounts; + std::transform (range.first, range.second, std::back_inserter (accounts), [] (auto const & info_a) { return info_a.vote->account; }); + std::sort (accounts.begin (), accounts.end ()); + // All cached votes must be unique by account, this is actively enforced in local_vote_history::add + auto consistent_unique = accounts.size () == std::unique (accounts.begin (), accounts.end ()) - accounts.begin (); + auto result = consistent_same && consistent_unique; + debug_assert (result); + return result; +} + +void nano::local_vote_history::add (nano::root const & root_a, nano::block_hash const & hash_a, std::shared_ptr const & vote_a) +{ + nano::lock_guard guard{ mutex }; + clean (); + auto add_vote (true); + auto & history_by_root (history.get ()); + // Erase any vote that is not for this hash, or duplicate by account, and if new timestamp is higher + auto range (history_by_root.equal_range (root_a)); + for (auto i (range.first); i != range.second;) + { + if (i->hash != hash_a || (vote_a->account == i->vote->account && i->vote->timestamp () <= vote_a->timestamp ())) + { + i = history_by_root.erase (i); + } + else if (vote_a->account == i->vote->account && i->vote->timestamp () > vote_a->timestamp ()) + { + add_vote = false; + ++i; + } + else + { + ++i; + } + } + // Do not add new vote to cache if representative account is same and timestamp is lower + if (add_vote) + { + auto result (history_by_root.emplace (root_a, hash_a, vote_a)); + (void)result; + debug_assert (result.second); + } + debug_assert (consistency_check (root_a)); +} + +void nano::local_vote_history::erase (nano::root const & root_a) +{ + nano::lock_guard guard{ mutex }; + auto & history_by_root (history.get ()); + auto range (history_by_root.equal_range (root_a)); + history_by_root.erase (range.first, range.second); +} + +std::vector> nano::local_vote_history::votes (nano::root const & root_a) const +{ + nano::lock_guard guard{ mutex }; + std::vector> result; + auto range (history.get ().equal_range (root_a)); + std::transform (range.first, range.second, std::back_inserter (result), [] (auto const & entry) { return entry.vote; }); + return result; +} + +std::vector> nano::local_vote_history::votes (nano::root const & root_a, nano::block_hash const & hash_a, bool const is_final_a) const +{ + nano::lock_guard guard{ mutex }; + std::vector> result; + auto range (history.get ().equal_range (root_a)); + // clang-format off + nano::transform_if (range.first, range.second, std::back_inserter (result), + [&hash_a, is_final_a](auto const & entry) { return entry.hash == hash_a && (!is_final_a || entry.vote->timestamp () == std::numeric_limits::max ()); }, + [](auto const & entry) { return entry.vote; }); + // clang-format on + return result; +} + +bool nano::local_vote_history::exists (nano::root const & root_a) const +{ + nano::lock_guard guard{ mutex }; + return history.get ().find (root_a) != history.get ().end (); +} + +void nano::local_vote_history::clean () +{ + debug_assert (constants.max_cache > 0); + auto & history_by_sequence (history.get ()); + while (history_by_sequence.size () > constants.max_cache) + { + history_by_sequence.erase (history_by_sequence.begin ()); + } +} + +std::size_t nano::local_vote_history::size () const +{ + nano::lock_guard guard{ mutex }; + return history.size (); +} + +std::unique_ptr nano::local_vote_history::collect_container_info (std::string const & name) const +{ + std::size_t history_count = size (); + auto sizeof_element = sizeof (decltype (history)::value_type); + auto composite = std::make_unique (name); + /* This does not currently loop over each element inside the cache to get the sizes of the votes inside history*/ + composite->add_component (std::make_unique (container_info{ "history", history_count, sizeof_element })); + return composite; +} diff --git a/nano/node/local_vote_history.hpp b/nano/node/local_vote_history.hpp new file mode 100644 index 00000000..2cf603ec --- /dev/null +++ b/nano/node/local_vote_history.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace mi = boost::multi_index; + +namespace nano +{ +class container_info_component; +class vote; +class voting_constants; +} + +namespace nano +{ +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 const & vote_a) : + root (root_a), + hash (hash_a), + vote (vote_a) + { + } + nano::root root; + nano::block_hash hash; + std::shared_ptr 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 const & vote_a); + void erase (nano::root const & root_a); + + std::vector> 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; + + std::unique_ptr collect_container_info (std::string const & name) const; + +private: + // clang-format off + boost::multi_index_container, + mi::member>, + mi::sequenced>>> + history; + // clang-format on + + nano::voting_constants const & constants; + void clean (); + std::vector> votes (nano::root const & root_a) const; + // Only used in Debug + bool consistency_check (nano::root const &) const; + mutable nano::mutex mutex; + + friend class local_vote_history_basic_Test; +}; +} diff --git a/nano/node/node.cpp b/nano/node/node.cpp index a7babb79..6a3dbd8a 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -171,7 +172,8 @@ nano::node::node (std::shared_ptr io_ctx_a, std::filesy warmed_up (0), block_processor (*this, write_database_queue), online_reps (ledger, config), - history{ config.network_params.voting }, + history_impl{ std::make_unique (config.network_params.voting) }, + history{ *history_impl }, vote_uniquer{}, confirmation_height_processor (ledger, write_database_queue, config.conf_height_processor_batch_min_time, logger, node_initialized_latch, flags.confirmation_height_processor_mode), vote_cache{ config.vote_cache, stats }, @@ -537,7 +539,7 @@ std::unique_ptr nano::collect_container_info (no composite->add_component (node.rep_crawler.collect_container_info ("rep_crawler")); composite->add_component (node.block_processor.collect_container_info ("block_processor")); composite->add_component (collect_container_info (node.online_reps, "online_reps")); - composite->add_component (collect_container_info (node.history, "history")); + composite->add_component (node.history.collect_container_info ("history")); composite->add_component (node.block_uniquer.collect_container_info ("block_uniquer")); composite->add_component (node.vote_uniquer.collect_container_info ("vote_uniquer")); composite->add_component (collect_container_info (node.confirmation_height_processor, "confirmation_height_processor")); diff --git a/nano/node/node.hpp b/nano/node/node.hpp index 5c0e705a..5f62ba54 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -166,7 +166,8 @@ public: nano::vote_processor vote_processor; unsigned warmed_up; nano::block_processor block_processor; - nano::local_vote_history history; + std::unique_ptr history_impl; + nano::local_vote_history & history; nano::block_uniquer block_uniquer; nano::vote_uniquer vote_uniquer; nano::confirmation_height_processor confirmation_height_processor; diff --git a/nano/node/request_aggregator.cpp b/nano/node/request_aggregator.cpp index 32ad38d6..62d37146 100644 --- a/nano/node/request_aggregator.cpp +++ b/nano/node/request_aggregator.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/node/voting.cpp b/nano/node/voting.cpp index 13a66ea9..5101ca51 100644 --- a/nano/node/voting.cpp +++ b/nano/node/voting.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -50,118 +51,6 @@ std::size_t nano::vote_spacing::size () const return recent.size (); } -bool nano::local_vote_history::consistency_check (nano::root const & root_a) const -{ - auto & history_by_root (history.get ()); - auto const range (history_by_root.equal_range (root_a)); - // All cached votes for a root must be for the same hash, this is actively enforced in local_vote_history::add - auto consistent_same = std::all_of (range.first, range.second, [hash = range.first->hash] (auto const & info_a) { return info_a.hash == hash; }); - std::vector accounts; - std::transform (range.first, range.second, std::back_inserter (accounts), [] (auto const & info_a) { return info_a.vote->account; }); - std::sort (accounts.begin (), accounts.end ()); - // All cached votes must be unique by account, this is actively enforced in local_vote_history::add - auto consistent_unique = accounts.size () == std::unique (accounts.begin (), accounts.end ()) - accounts.begin (); - auto result = consistent_same && consistent_unique; - debug_assert (result); - return result; -} - -void nano::local_vote_history::add (nano::root const & root_a, nano::block_hash const & hash_a, std::shared_ptr const & vote_a) -{ - nano::lock_guard guard{ mutex }; - clean (); - auto add_vote (true); - auto & history_by_root (history.get ()); - // Erase any vote that is not for this hash, or duplicate by account, and if new timestamp is higher - auto range (history_by_root.equal_range (root_a)); - for (auto i (range.first); i != range.second;) - { - if (i->hash != hash_a || (vote_a->account == i->vote->account && i->vote->timestamp () <= vote_a->timestamp ())) - { - i = history_by_root.erase (i); - } - else if (vote_a->account == i->vote->account && i->vote->timestamp () > vote_a->timestamp ()) - { - add_vote = false; - ++i; - } - else - { - ++i; - } - } - // Do not add new vote to cache if representative account is same and timestamp is lower - if (add_vote) - { - auto result (history_by_root.emplace (root_a, hash_a, vote_a)); - (void)result; - debug_assert (result.second); - } - debug_assert (consistency_check (root_a)); -} - -void nano::local_vote_history::erase (nano::root const & root_a) -{ - nano::lock_guard guard{ mutex }; - auto & history_by_root (history.get ()); - auto range (history_by_root.equal_range (root_a)); - history_by_root.erase (range.first, range.second); -} - -std::vector> nano::local_vote_history::votes (nano::root const & root_a) const -{ - nano::lock_guard guard{ mutex }; - std::vector> result; - auto range (history.get ().equal_range (root_a)); - std::transform (range.first, range.second, std::back_inserter (result), [] (auto const & entry) { return entry.vote; }); - return result; -} - -std::vector> nano::local_vote_history::votes (nano::root const & root_a, nano::block_hash const & hash_a, bool const is_final_a) const -{ - nano::lock_guard guard{ mutex }; - std::vector> result; - auto range (history.get ().equal_range (root_a)); - // clang-format off - nano::transform_if (range.first, range.second, std::back_inserter (result), - [&hash_a, is_final_a](auto const & entry) { return entry.hash == hash_a && (!is_final_a || entry.vote->timestamp () == std::numeric_limits::max ()); }, - [](auto const & entry) { return entry.vote; }); - // clang-format on - return result; -} - -bool nano::local_vote_history::exists (nano::root const & root_a) const -{ - nano::lock_guard guard{ mutex }; - return history.get ().find (root_a) != history.get ().end (); -} - -void nano::local_vote_history::clean () -{ - debug_assert (constants.max_cache > 0); - auto & history_by_sequence (history.get ()); - while (history_by_sequence.size () > constants.max_cache) - { - history_by_sequence.erase (history_by_sequence.begin ()); - } -} - -std::size_t nano::local_vote_history::size () const -{ - nano::lock_guard guard{ mutex }; - return history.size (); -} - -std::unique_ptr nano::collect_container_info (nano::local_vote_history & history, std::string const & name) -{ - std::size_t history_count = history.size (); - auto sizeof_element = sizeof (decltype (history.history)::value_type); - auto composite = std::make_unique (name); - /* This does not currently loop over each element inside the cache to get the sizes of the votes inside history*/ - composite->add_component (std::make_unique (container_info{ "history", history_count, sizeof_element })); - return composite; -} - nano::vote_generator::vote_generator (nano::node_config const & config_a, nano::node & node_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, nano::logger & logger_a, bool is_final_a) : config (config_a), node (node_a), diff --git a/nano/node/voting.hpp b/nano/node/voting.hpp index 24c5813d..0ed940e3 100644 --- a/nano/node/voting.hpp +++ b/nano/node/voting.hpp @@ -22,9 +22,10 @@ namespace mi = boost::multi_index; namespace nano { -class node; class ledger; +class local_vote_history; class network; +class node; class node_config; class stats; class vote_processor; @@ -64,57 +65,6 @@ public: 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 const & vote_a) : - root (root_a), - hash (hash_a), - vote (vote_a) - { - } - nano::root root; - nano::block_hash hash; - std::shared_ptr 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 const & vote_a); - void erase (nano::root const & root_a); - - std::vector> 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, - mi::member>, - mi::sequenced>>> - history; - // clang-format on - - nano::voting_constants const & constants; - void clean (); - std::vector> 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 collect_container_info (local_vote_history & history, std::string const & name); - friend class local_vote_history_basic_Test; -}; - -std::unique_ptr collect_container_info (local_vote_history & history, std::string const & name); - class vote_generator final { private: diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index 9ce8c55e..aab26e8b 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -73,14 +73,6 @@ struct hash<::nano::qualified_root> return std::hash<::nano::qualified_root> () (value_a); } }; -template <> -struct hash<::nano::root> -{ - size_t operator() (::nano::root const & value_a) const - { - return std::hash<::nano::root> () (value_a); - } -}; } namespace nano { diff --git a/nano/secure/vote.hpp b/nano/secure/vote.hpp index 9ae7b068..7c29d538 100644 --- a/nano/secure/vote.hpp +++ b/nano/secure/vote.hpp @@ -10,6 +10,11 @@ #include +namespace nano +{ +class object_stream; +} + namespace nano { class vote final @@ -77,4 +82,4 @@ public: // Logging }; using vote_uniquer = nano::uniquer; -} \ No newline at end of file +}