Skip to content

Commit

Permalink
Dedicated rep_tiers class (#4471)
Browse files Browse the repository at this point in the history
* Vote processor tiers

* Unused function cleanup

* Use online weight for representative tier calculations

* Simplify testcase

* Dedicated `rep_tiers` class

* Use trended stake

* Fix test
  • Loading branch information
pwojcikdev authored Mar 10, 2024
1 parent 2de5816 commit 8dd0bf9
Show file tree
Hide file tree
Showing 13 changed files with 268 additions and 110 deletions.
35 changes: 12 additions & 23 deletions nano/core_test/vote_processor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,19 +126,17 @@ TEST (vote_processor, overflow)
ASSERT_LT (std::chrono::system_clock::now () - start_time, 10s);
}

namespace nano
{
TEST (vote_processor, weights)
{
nano::test::system system (4);
auto & node (*system.nodes[0]);

// Create representatives of different weight levels
// The online stake will be the minimum configurable due to online_reps sampling in tests
auto const online = node.config.online_weight_minimum.number ();
auto const level0 = online / 5000; // 0.02%
auto const level1 = online / 500; // 0.2%
auto const level2 = online / 50; // 2%
// FIXME: Using `online_weight_minimum` because calculation of trended and online weight is broken when running tests
auto const stake = node.config.online_weight_minimum.number ();
auto const level0 = stake / 5000; // 0.02%
auto const level1 = stake / 500; // 0.2%
auto const level2 = stake / 50; // 2%

nano::keypair key0;
nano::keypair key1;
Expand All @@ -157,24 +155,15 @@ TEST (vote_processor, weights)

// Wait for representatives
ASSERT_TIMELY_EQ (10s, node.ledger.cache.rep_weights.get_rep_amounts ().size (), 4);
node.vote_processor.calculate_weights ();

ASSERT_EQ (node.vote_processor.representatives_1.end (), node.vote_processor.representatives_1.find (key0.pub));
ASSERT_EQ (node.vote_processor.representatives_2.end (), node.vote_processor.representatives_2.find (key0.pub));
ASSERT_EQ (node.vote_processor.representatives_3.end (), node.vote_processor.representatives_3.find (key0.pub));

ASSERT_NE (node.vote_processor.representatives_1.end (), node.vote_processor.representatives_1.find (key1.pub));
ASSERT_EQ (node.vote_processor.representatives_2.end (), node.vote_processor.representatives_2.find (key1.pub));
ASSERT_EQ (node.vote_processor.representatives_3.end (), node.vote_processor.representatives_3.find (key1.pub));
// Wait for rep tiers to be updated
node.stats.clear ();
ASSERT_TIMELY (5s, node.stats.count (nano::stat::type::rep_tiers, nano::stat::detail::updated) >= 2);

ASSERT_NE (node.vote_processor.representatives_1.end (), node.vote_processor.representatives_1.find (key2.pub));
ASSERT_NE (node.vote_processor.representatives_2.end (), node.vote_processor.representatives_2.find (key2.pub));
ASSERT_EQ (node.vote_processor.representatives_3.end (), node.vote_processor.representatives_3.find (key2.pub));

ASSERT_NE (node.vote_processor.representatives_1.end (), node.vote_processor.representatives_1.find (nano::dev::genesis_key.pub));
ASSERT_NE (node.vote_processor.representatives_2.end (), node.vote_processor.representatives_2.find (nano::dev::genesis_key.pub));
ASSERT_NE (node.vote_processor.representatives_3.end (), node.vote_processor.representatives_3.find (nano::dev::genesis_key.pub));
}
ASSERT_EQ (node.rep_tiers.tier (key0.pub), nano::rep_tier::none);
ASSERT_EQ (node.rep_tiers.tier (key1.pub), nano::rep_tier::tier_1);
ASSERT_EQ (node.rep_tiers.tier (key2.pub), nano::rep_tier::tier_2);
ASSERT_EQ (node.rep_tiers.tier (nano::dev::genesis_key.pub), nano::rep_tier::tier_3);
}

// Issue that tracks last changes on this test: https://github.com/nanocurrency/nano-node/issues/3485
Expand Down
1 change: 1 addition & 0 deletions nano/lib/logging_enums.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ enum class type
vote_processor,
election_scheduler,
vote_generator,
rep_tiers,

// bootstrap
bulk_pull_client,
Expand Down
4 changes: 4 additions & 0 deletions nano/lib/stats_enums.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ enum class type : uint8_t
handshake,
rep_crawler,
local_block_broadcaster,
rep_tiers,

bootstrap_ascending,
bootstrap_ascending_accounts,
Expand All @@ -68,7 +69,10 @@ enum class detail : uint8_t
loop,
total,
process,
processed,
ignored,
update,
updated,
request,
broadcast,
cleanup,
Expand Down
3 changes: 3 additions & 0 deletions nano/lib/thread_roles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ std::string nano::thread_role::get_string (nano::thread_role::name role)
case nano::thread_role::name::local_block_broadcasting:
thread_role_name_string = "Local broadcast";
break;
case nano::thread_role::name::rep_tiers:
thread_role_name_string = "Rep tiers";
break;
default:
debug_assert (false && "nano::thread_role::get_string unhandled thread role");
}
Expand Down
1 change: 1 addition & 0 deletions nano/lib/thread_roles.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ enum class name
scheduler_priority,
rep_crawler,
local_block_broadcasting,
rep_tiers,
};

/*
Expand Down
2 changes: 2 additions & 0 deletions nano/node/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ add_library(
process_live_dispatcher.hpp
repcrawler.hpp
repcrawler.cpp
rep_tiers.hpp
rep_tiers.cpp
request_aggregator.hpp
request_aggregator.cpp
scheduler/bucket.cpp
Expand Down
20 changes: 5 additions & 15 deletions nano/node/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@ nano::node::node (boost::asio::io_context & io_ctx_a, std::filesystem::path cons
application_path (application_path_a),
port_mapping (*this),
rep_crawler (config.rep_crawler, *this),
vote_processor (active, observers, stats, config, flags, logger, online_reps, rep_crawler, ledger, network_params),
rep_tiers{ ledger, network_params, online_reps, stats, logger },
vote_processor{ active, observers, stats, config, flags, logger, online_reps, rep_crawler, ledger, network_params, rep_tiers },
warmed_up (0),
block_processor (*this, write_database_queue),
online_reps (ledger, config),
Expand Down Expand Up @@ -541,6 +542,7 @@ std::unique_ptr<nano::container_info_component> nano::collect_container_info (no
composite->add_component (node.ascendboot.collect_container_info ("bootstrap_ascending"));
composite->add_component (node.unchecked.collect_container_info ("unchecked"));
composite->add_component (node.local_block_broadcaster.collect_container_info ("local_block_broadcaster"));
composite->add_component (node.rep_tiers.collect_container_info ("rep_tiers"));
return composite;
}

Expand Down Expand Up @@ -590,7 +592,6 @@ void nano::node::start ()
{
rep_crawler.start ();
}
ongoing_rep_calculation ();
ongoing_peer_store ();
ongoing_online_weight_calculation_queue ();

Expand Down Expand Up @@ -636,6 +637,7 @@ void nano::node::start ()
port_mapping.start ();
}
wallets.start ();
rep_tiers.start ();
vote_processor.start ();
block_processor.start ();
active.start ();
Expand Down Expand Up @@ -676,6 +678,7 @@ void nano::node::stop ()
block_processor.stop ();
aggregator.stop ();
vote_processor.stop ();
rep_tiers.stop ();
scheduler.stop ();
active.stop ();
generator.stop ();
Expand Down Expand Up @@ -763,19 +766,6 @@ void nano::node::long_inactivity_cleanup ()
}
}

void nano::node::ongoing_rep_calculation ()
{
auto now (std::chrono::steady_clock::now ());
vote_processor.calculate_weights ();
std::weak_ptr<nano::node> node_w (shared_from_this ());
workers.add_timed_task (now + std::chrono::minutes (10), [node_w] () {
if (auto node_l = node_w.lock ())
{
node_l->ongoing_rep_calculation ();
}
});
}

void nano::node::ongoing_bootstrap ()
{
auto next_wakeup = network_params.network.bootstrap_interval;
Expand Down
3 changes: 2 additions & 1 deletion nano/node/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <nano/node/online_reps.hpp>
#include <nano/node/portmapping.hpp>
#include <nano/node/process_live_dispatcher.hpp>
#include <nano/node/rep_tiers.hpp>
#include <nano/node/repcrawler.hpp>
#include <nano/node/request_aggregator.hpp>
#include <nano/node/telemetry.hpp>
Expand Down Expand Up @@ -92,7 +93,6 @@ class node final : public std::enable_shared_from_this<nano::node>
std::pair<nano::uint128_t, nano::uint128_t> balance_pending (nano::account const &, bool only_confirmed);
nano::uint128_t weight (nano::account const &);
nano::uint128_t minimum_principal_weight ();
void ongoing_rep_calculation ();
void ongoing_bootstrap ();
void ongoing_peer_store ();
void backup_wallet ();
Expand Down Expand Up @@ -163,6 +163,7 @@ class node final : public std::enable_shared_from_this<nano::node>
nano::port_mapping port_mapping;
nano::online_reps online_reps;
nano::rep_crawler rep_crawler;
nano::rep_tiers rep_tiers;
nano::vote_processor vote_processor;
unsigned warmed_up;
nano::block_processor block_processor;
Expand Down
5 changes: 5 additions & 0 deletions nano/node/online_reps.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <nano/lib/numbers.hpp>
#include <nano/lib/utility.hpp>
#include <nano/secure/common.hpp>

#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
Expand All @@ -15,6 +16,10 @@ namespace nano
{
class ledger;
class node_config;
namespace store
{
class transaction;
}

/** Track online representatives and trend online weight */
class online_reps final
Expand Down
144 changes: 144 additions & 0 deletions nano/node/rep_tiers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#include <nano/lib/logging.hpp>
#include <nano/lib/thread_roles.hpp>
#include <nano/node/online_reps.hpp>
#include <nano/node/rep_tiers.hpp>
#include <nano/secure/common.hpp>
#include <nano/secure/ledger.hpp>

using namespace std::chrono_literals;

nano::rep_tiers::rep_tiers (nano::ledger & ledger_a, nano::network_params & network_params_a, nano::online_reps & online_reps_a, nano::stats & stats_a, nano::logger & logger_a) :
ledger{ ledger_a },
network_params{ network_params_a },
online_reps{ online_reps_a },
stats{ stats_a },
logger{ logger_a }
{
}

nano::rep_tiers::~rep_tiers ()
{
// Thread must be stopped before destruction
debug_assert (!thread.joinable ());
}

void nano::rep_tiers::start ()
{
debug_assert (!thread.joinable ());

thread = std::thread{ [this] () {
nano::thread_role::set (nano::thread_role::name::rep_tiers);
run ();
} };
}

void nano::rep_tiers::stop ()
{
{
nano::lock_guard<nano::mutex> lock{ mutex };
stopped = true;
}
condition.notify_all ();
if (thread.joinable ())
{
thread.join ();
}
}

nano::rep_tier nano::rep_tiers::tier (const nano::account & representative) const
{
nano::lock_guard<nano::mutex> lock{ mutex };
if (representatives_3.find (representative) != representatives_3.end ())
{
return nano::rep_tier::tier_3;
}
if (representatives_2.find (representative) != representatives_2.end ())
{
return nano::rep_tier::tier_2;
}
if (representatives_1.find (representative) != representatives_1.end ())
{
return nano::rep_tier::tier_1;
}
return nano::rep_tier::none;
}

void nano::rep_tiers::run ()
{
nano::unique_lock<nano::mutex> lock{ mutex };
while (!stopped)
{
stats.inc (nano::stat::type::rep_tiers, nano::stat::detail::loop);

lock.unlock ();

calculate_tiers ();

lock.lock ();

std::chrono::milliseconds interval = network_params.network.is_dev_network () ? 500ms : 10min;
condition.wait_for (lock, interval);
}
}

void nano::rep_tiers::calculate_tiers ()
{
auto stake = online_reps.trended ();
auto rep_amounts = ledger.cache.rep_weights.get_rep_amounts ();

decltype (representatives_1) representatives_1_l;
decltype (representatives_2) representatives_2_l;
decltype (representatives_3) representatives_3_l;

int ignored = 0;
for (auto const & rep_amount : rep_amounts)
{
nano::account const & representative = rep_amount.first;

// Using ledger weight here because it takes preconfigured bootstrap weights into account
auto weight = ledger.weight (representative);
if (weight > stake / 1000) // 0.1% or above (level 1)
{
representatives_1_l.insert (representative);
if (weight > stake / 100) // 1% or above (level 2)
{
representatives_2_l.insert (representative);
if (weight > stake / 20) // 5% or above (level 3)
{
representatives_3_l.insert (representative);
}
}
}
else
{
++ignored;
}
}

stats.add (nano::stat::type::rep_tiers, nano::stat::detail::processed, nano::stat::dir::in, rep_amounts.size ());
stats.add (nano::stat::type::rep_tiers, nano::stat::detail::ignored, nano::stat::dir::in, ignored);
logger.debug (nano::log::type::rep_tiers, "Representative tiers updated, tier 1: {}, tier 2: {}, tier 3: {} ({} ignored)",
representatives_1_l.size (),
representatives_2_l.size (),
representatives_3_l.size (),
ignored);

{
nano::lock_guard<nano::mutex> guard{ mutex };
representatives_1 = std::move (representatives_1_l);
representatives_2 = std::move (representatives_2_l);
representatives_3 = std::move (representatives_3_l);
}

stats.inc (nano::stat::type::rep_tiers, nano::stat::detail::updated);
}

std::unique_ptr<nano::container_info_component> nano::rep_tiers::collect_container_info (const std::string & name)
{
nano::lock_guard<nano::mutex> lock{ mutex };
auto composite = std::make_unique<container_info_composite> (name);
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "representatives_1", representatives_1.size (), sizeof (decltype (representatives_1)::value_type) }));
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "representatives_2", representatives_2.size (), sizeof (decltype (representatives_2)::value_type) }));
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "representatives_3", representatives_3.size (), sizeof (decltype (representatives_3)::value_type) }));
return composite;
}
Loading

0 comments on commit 8dd0bf9

Please sign in to comment.