From dc783a79716682ee52bbc7050387d2b490970bbc Mon Sep 17 00:00:00 2001 From: Gustav Schauwecker Date: Tue, 12 Mar 2024 13:32:49 +0100 Subject: [PATCH 1/3] Add min weight to reps_cache --- nano/core_test/ledger.cpp | 30 ++++++++++++++++++++++++++++++ nano/secure/rep_weights.cpp | 8 +++++--- nano/secure/rep_weights.hpp | 3 ++- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index df55af7c1d..22e41d9de8 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -17,6 +17,8 @@ #include +#include + using namespace std::chrono_literals; // Init returns an error if it can't open files at the path @@ -690,6 +692,34 @@ TEST (ledger, delete_rep_weight_of_zero) ASSERT_EQ (0, store->rep_weight.count (txn)); } +TEST (ledger, rep_cache_min_weight) +{ + auto store{ nano::test::make_store () }; + nano::uint128_t min_weight{ 10 }; + nano::rep_weights rep_weights{ store->rep_weight, min_weight }; + auto txn{ store->tx_begin_write () }; + + // one below min weight + rep_weights.representation_add (txn, 1, 9); + ASSERT_EQ (0, rep_weights.size ()); + ASSERT_EQ (1, store->rep_weight.count (txn)); + + // exactly min weight + rep_weights.representation_add (txn, 1, 1); + ASSERT_EQ (1, rep_weights.size ()); + ASSERT_EQ (1, store->rep_weight.count (txn)); + + // above min weight + rep_weights.representation_add (txn, 1, 1); + ASSERT_EQ (1, rep_weights.size ()); + ASSERT_EQ (1, store->rep_weight.count (txn)); + + // fall blow min weight + rep_weights.representation_add (txn, 1, nano::uint128_t{ 0 } - 5); + ASSERT_EQ (0, rep_weights.size ()); + ASSERT_EQ (1, store->rep_weight.count (txn)); +} + TEST (ledger, representation) { auto ctx = nano::test::context::ledger_empty (); diff --git a/nano/secure/rep_weights.cpp b/nano/secure/rep_weights.cpp index 7be8915cfb..632ace73a2 100644 --- a/nano/secure/rep_weights.cpp +++ b/nano/secure/rep_weights.cpp @@ -1,9 +1,11 @@ +#include #include #include #include -nano::rep_weights::rep_weights (nano::store::rep_weight & rep_weight_store_a) : - rep_weight_store{ rep_weight_store_a } +nano::rep_weights::rep_weights (nano::store::rep_weight & rep_weight_store_a, nano::uint128_t min_weight_a) : + rep_weight_store{ rep_weight_store_a }, + min_weight{ min_weight_a } { } @@ -69,7 +71,7 @@ void nano::rep_weights::copy_from (nano::rep_weights & other_a) void nano::rep_weights::put_cache (nano::account const & account_a, nano::uint128_union const & representation_a) { auto it = rep_amounts.find (account_a); - if (representation_a.is_zero ()) + if (representation_a < min_weight || representation_a.is_zero ()) { if (it != rep_amounts.end ()) { diff --git a/nano/secure/rep_weights.hpp b/nano/secure/rep_weights.hpp index e29f4e0bac..214e22b8e6 100644 --- a/nano/secure/rep_weights.hpp +++ b/nano/secure/rep_weights.hpp @@ -19,7 +19,7 @@ namespace store class rep_weights { public: - explicit rep_weights (nano::store::rep_weight & rep_weight_store_a); + explicit rep_weights (nano::store::rep_weight & rep_weight_store_a, nano::uint128_t min_weight_a = 0); void representation_add (store::write_transaction const & txn_a, nano::account const & source_rep_a, nano::uint128_t const & amount_a); void representation_add_dual (store::write_transaction const & txn_a, nano::account const & source_rep_1, nano::uint128_t const & amount_1, nano::account const & source_rep_2, nano::uint128_t const & amount_2); nano::uint128_t representation_get (nano::account const & account_a) const; @@ -35,6 +35,7 @@ class rep_weights mutable nano::mutex mutex; std::unordered_map rep_amounts; nano::store::rep_weight & rep_weight_store; + nano::uint128_t min_weight; void put_cache (nano::account const & account_a, nano::uint128_union const & representation_a); void put_store (store::write_transaction const & txn_a, nano::account const & rep_a, nano::uint128_t const & previous_weight_a, nano::uint128_t const & new_weight_a); nano::uint128_t get (nano::account const & account_a) const; From 5be0924c8242d12a83a04fd02edbf5c0592b5675 Mon Sep 17 00:00:00 2001 From: Gustav Schauwecker Date: Tue, 12 Mar 2024 14:19:30 +0100 Subject: [PATCH 2/3] Make minimum representative weight configurable --- nano/core_test/toml.cpp | 3 +++ nano/core_test/vote_processor.cpp | 4 ++++ nano/node/node.cpp | 2 +- nano/node/nodeconfig.cpp | 11 +++++++++++ nano/node/nodeconfig.hpp | 5 +++++ nano/secure/ledger.cpp | 4 ++-- nano/secure/ledger.hpp | 2 +- nano/secure/ledger_cache.cpp | 4 ++-- nano/secure/ledger_cache.hpp | 3 ++- nano/test_common/system.cpp | 1 + 10 files changed, 32 insertions(+), 7 deletions(-) diff --git a/nano/core_test/toml.cpp b/nano/core_test/toml.cpp index 9e2a5cbe83..7ea1846f2c 100644 --- a/nano/core_test/toml.cpp +++ b/nano/core_test/toml.cpp @@ -175,6 +175,7 @@ TEST (toml, daemon_config_deserialize_defaults) ASSERT_EQ (conf.node.background_threads, defaults.node.background_threads); ASSERT_EQ (conf.node.secondary_work_peers, defaults.node.secondary_work_peers); ASSERT_EQ (conf.node.online_weight_minimum, defaults.node.online_weight_minimum); + ASSERT_EQ (conf.node.representative_vote_weight_minimum, defaults.node.representative_vote_weight_minimum); ASSERT_EQ (conf.node.rep_crawler_weight_minimum, defaults.node.rep_crawler_weight_minimum); ASSERT_EQ (conf.node.password_fanout, defaults.node.password_fanout); ASSERT_EQ (conf.node.peering_port, defaults.node.peering_port); @@ -404,6 +405,7 @@ TEST (toml, daemon_config_deserialize_no_defaults) network_threads = 999 background_threads = 999 online_weight_minimum = "999" + representative_vote_weight_minimum = "999" rep_crawler_weight_minimum = "999" password_fanout = 999 peering_port = 999 @@ -597,6 +599,7 @@ TEST (toml, daemon_config_deserialize_no_defaults) ASSERT_NE (conf.node.max_pruning_age, defaults.node.max_pruning_age); ASSERT_NE (conf.node.max_pruning_depth, defaults.node.max_pruning_depth); ASSERT_NE (conf.node.online_weight_minimum, defaults.node.online_weight_minimum); + ASSERT_NE (conf.node.representative_vote_weight_minimum, defaults.node.representative_vote_weight_minimum); ASSERT_NE (conf.node.rep_crawler_weight_minimum, defaults.node.rep_crawler_weight_minimum); ASSERT_NE (conf.node.password_fanout, defaults.node.password_fanout); ASSERT_NE (conf.node.peering_port, defaults.node.peering_port); diff --git a/nano/core_test/vote_processor.cpp b/nano/core_test/vote_processor.cpp index f755eb8ac0..4da56adc2c 100644 --- a/nano/core_test/vote_processor.cpp +++ b/nano/core_test/vote_processor.cpp @@ -176,8 +176,10 @@ TEST (vote_processor, no_broadcast_local) nano::node_flags flags; flags.disable_request_loop = true; nano::node_config config1, config2; + config1.representative_vote_weight_minimum = 0; config1.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; auto & node (*system.add_node (config1, flags)); + config2.representative_vote_weight_minimum = 0; config2.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; config2.peering_port = system.get_available_port (); system.add_node (config2, flags); @@ -229,8 +231,10 @@ TEST (vote_processor, local_broadcast_without_a_representative) nano::node_flags flags; flags.disable_request_loop = true; nano::node_config config1, config2; + config1.representative_vote_weight_minimum = 0; config1.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; auto & node (*system.add_node (config1, flags)); + config2.representative_vote_weight_minimum = 0; config2.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; config2.peering_port = system.get_available_port (); system.add_node (config2, flags); diff --git a/nano/node/node.cpp b/nano/node/node.cpp index aa1b11b77e..8c0ca258b9 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -144,7 +144,7 @@ nano::node::node (boost::asio::io_context & io_ctx_a, std::filesystem::path cons unchecked{ config.max_unchecked_blocks, stats, flags.disable_block_processor_unchecked_deletion }, wallets_store_impl (std::make_unique (application_path_a / "wallets.ldb", config_a.lmdb_config)), wallets_store (*wallets_store_impl), - ledger_impl{ std::make_unique (store, stats, network_params.ledger, flags_a.generate_cache) }, + ledger_impl{ std::make_unique (store, stats, network_params.ledger, flags_a.generate_cache, config_a.representative_vote_weight_minimum.number ()) }, ledger{ *ledger_impl }, outbound_limiter{ outbound_bandwidth_limiter_config (config) }, // empty `config.peering_port` means the user made no port choice at all; diff --git a/nano/node/nodeconfig.cpp b/nano/node/nodeconfig.cpp index 16f3c897b8..c240c583c1 100644 --- a/nano/node/nodeconfig.cpp +++ b/nano/node/nodeconfig.cpp @@ -91,6 +91,7 @@ nano::error nano::node_config::serialize_toml (nano::tomlconfig & toml) const toml.put ("bootstrap_fraction_numerator", bootstrap_fraction_numerator, "Change bootstrap threshold (online stake / 256 * bootstrap_fraction_numerator).\ntype:uint32"); toml.put ("receive_minimum", receive_minimum.to_string_dec (), "Minimum receive amount. Only affects node wallets. A large amount is recommended to avoid automatic work generation for tiny transactions.\ntype:string,amount,raw"); toml.put ("online_weight_minimum", online_weight_minimum.to_string_dec (), "When calculating online weight, the node is forced to assume at least this much voting weight is online, thus setting a floor for voting weight to confirm transactions at online_weight_minimum * \"quorum delta\".\ntype:string,amount,raw"); + toml.put ("representative_vote_weight_minimum", representative_vote_weight_minimum.to_string_dec (), "Minimum vote weight that a representative must have for its vote to be counted.\nAll representatives above this weight will be kept in memory!\ntype:string,amount,raw"); toml.put ("password_fanout", password_fanout, "Password fanout factor.\ntype:uint64"); toml.put ("io_threads", io_threads, "Number of threads dedicated to I/O operations. Defaults to the number of CPU threads, and at least 4.\ntype:uint64"); toml.put ("network_threads", network_threads, "Number of threads dedicated to processing network messages. Defaults to the number of CPU threads, and at least 4.\ntype:uint64"); @@ -340,6 +341,16 @@ nano::error nano::node_config::deserialize_toml (nano::tomlconfig & toml) toml.get_error ().set ("online_weight_minimum contains an invalid decimal amount"); } + auto representative_vote_weight_minimum_l{ representative_vote_weight_minimum.to_string_dec () }; + if (toml.has_key ("representative_vote_weight_minimum")) + { + representative_vote_weight_minimum_l = toml.get ("representative_vote_weight_minimum"); + } + if (representative_vote_weight_minimum.decode_dec (representative_vote_weight_minimum_l)) + { + toml.get_error ().set ("representative_vote_weight_minimum contains an invalid decimal amount"); + } + auto vote_minimum_l (vote_minimum.to_string_dec ()); if (toml.has_key ("vote_minimum")) { diff --git a/nano/node/nodeconfig.hpp b/nano/node/nodeconfig.hpp index cf778df4ee..b93611aca0 100644 --- a/nano/node/nodeconfig.hpp +++ b/nano/node/nodeconfig.hpp @@ -64,6 +64,11 @@ class node_config std::chrono::milliseconds vote_generator_delay{ std::chrono::milliseconds (100) }; unsigned vote_generator_threshold{ 3 }; nano::amount online_weight_minimum{ 60000 * nano::Gxrb_ratio }; + /* + * The minimum vote weight that a representative must have for its vote to be counted. + * All representatives above this weight will be kept in memory! + */ + nano::amount representative_vote_weight_minimum{ 10 * nano::Mxrb_ratio }; unsigned password_fanout{ 1024 }; unsigned io_threads{ std::max (4u, nano::hardware_concurrency ()) }; unsigned network_threads{ std::max (4u, nano::hardware_concurrency ()) }; diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index bf644f8220..643e47bc06 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -749,10 +749,10 @@ void representative_visitor::state_block (nano::state_block const & block_a) } } // namespace -nano::ledger::ledger (nano::store::component & store_a, nano::stats & stat_a, nano::ledger_constants & constants, nano::generate_cache_flags const & generate_cache_flags_a) : +nano::ledger::ledger (nano::store::component & store_a, nano::stats & stat_a, nano::ledger_constants & constants, nano::generate_cache_flags const & generate_cache_flags_a, nano::uint128_t min_rep_weight_a) : constants{ constants }, store{ store_a }, - cache{ store_a.rep_weight }, + cache{ store_a.rep_weight, min_rep_weight_a }, stats{ stat_a }, check_bootstrap_weights{ true } { diff --git a/nano/secure/ledger.hpp b/nano/secure/ledger.hpp index 6c325a0296..6293ac1b62 100644 --- a/nano/secure/ledger.hpp +++ b/nano/secure/ledger.hpp @@ -40,7 +40,7 @@ class ledger final friend class receivable_iterator; public: - ledger (nano::store::component &, nano::stats &, nano::ledger_constants & constants, nano::generate_cache_flags const & = nano::generate_cache_flags{}); + ledger (nano::store::component &, nano::stats &, nano::ledger_constants & constants, nano::generate_cache_flags const & = nano::generate_cache_flags{}, nano::uint128_t min_rep_weight_a = 0); /** * Returns the account for a given hash * Returns std::nullopt if the block doesn't exist or has been pruned diff --git a/nano/secure/ledger_cache.cpp b/nano/secure/ledger_cache.cpp index 5076e1cc18..16c46c0f5e 100644 --- a/nano/secure/ledger_cache.cpp +++ b/nano/secure/ledger_cache.cpp @@ -1,6 +1,6 @@ #include -nano::ledger_cache::ledger_cache (nano::store::rep_weight & rep_weight_store_a) : - rep_weights{ rep_weight_store_a } +nano::ledger_cache::ledger_cache (nano::store::rep_weight & rep_weight_store_a, nano::uint128_t min_rep_weight_a) : + rep_weights{ rep_weight_store_a, min_rep_weight_a } { } diff --git a/nano/secure/ledger_cache.hpp b/nano/secure/ledger_cache.hpp index 34b86cb639..12ebc9af22 100644 --- a/nano/secure/ledger_cache.hpp +++ b/nano/secure/ledger_cache.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -11,7 +12,7 @@ namespace nano class ledger_cache { public: - explicit ledger_cache (nano::store::rep_weight & rep_weight_store_a); + explicit ledger_cache (nano::store::rep_weight & rep_weight_store_a, nano::uint128_t min_rep_weight_a = 0); nano::rep_weights rep_weights; std::atomic cemented_count{ 0 }; std::atomic block_count{ 0 }; diff --git a/nano/test_common/system.cpp b/nano/test_common/system.cpp index f4522c62b3..334f77b4e9 100644 --- a/nano/test_common/system.cpp +++ b/nano/test_common/system.cpp @@ -579,6 +579,7 @@ void nano::test::system::stop () nano::node_config nano::test::system::default_config () { nano::node_config config{ get_available_port () }; + config.representative_vote_weight_minimum = 0; return config; } From b6ec7731480e67af9f082bd4f329b57c97b206fc Mon Sep 17 00:00:00 2001 From: Gustav Schauwecker Date: Fri, 15 Mar 2024 08:43:39 +0100 Subject: [PATCH 3/3] Use exact vote weight in certain RPC calls and wallets code --- nano/node/json_handler.cpp | 8 ++++---- nano/node/node.cpp | 3 ++- nano/node/wallet.cpp | 9 +++++++-- nano/secure/ledger.cpp | 5 +++++ nano/secure/ledger.hpp | 7 +++++++ 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 1d98e73cb9..0624c17820 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -698,7 +698,7 @@ void nano::json_handler::account_info () } if (weight) { - auto account_weight (node.ledger.weight (account)); + auto account_weight (node.ledger.weight_exact (transaction, account)); response_l.put ("weight", account_weight.convert_to ()); } if (receivable) @@ -2784,7 +2784,7 @@ void nano::json_handler::ledger () } if (weight) { - auto account_weight (node.ledger.weight (account)); + auto account_weight (node.ledger.weight_exact (transaction, account)); response_a.put ("weight", account_weight.convert_to ()); } accounts.push_back (std::make_pair (account.to_account (), response_a)); @@ -2837,7 +2837,7 @@ void nano::json_handler::ledger () } if (weight) { - auto account_weight (node.ledger.weight (account)); + auto account_weight (node.ledger.weight_exact (transaction, account)); response_a.put ("weight", account_weight.convert_to ()); } accounts.push_back (std::make_pair (account.to_account (), response_a)); @@ -4716,7 +4716,7 @@ void nano::json_handler::wallet_ledger () } if (weight) { - auto account_weight (node.ledger.weight (account)); + auto account_weight (node.ledger.weight_exact (block_transaction, account)); entry.put ("weight", account_weight.convert_to ()); } if (receivable) diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 8c0ca258b9..abbc2e180e 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -739,7 +739,8 @@ std::pair nano::node::balance_pending (nano::a nano::uint128_t nano::node::weight (nano::account const & account_a) { - return ledger.weight (account_a); + auto txn{ ledger.store.tx_begin_read () }; + return ledger.weight_exact (txn, account_a); } nano::uint128_t nano::node::minimum_principal_weight () diff --git a/nano/node/wallet.cpp b/nano/node/wallet.cpp index 864ca4f016..a160855dca 100644 --- a/nano/node/wallet.cpp +++ b/nano/node/wallet.cpp @@ -1537,6 +1537,7 @@ void nano::wallets::foreach_representative (std::function> action_accounts_l; { auto transaction_l (tx_begin_read ()); + auto ledger_txn{ node.ledger.store.tx_begin_read () }; nano::lock_guard lock{ mutex }; for (auto i (items.begin ()), n (items.end ()); i != n; ++i) { @@ -1551,7 +1552,7 @@ void nano::wallets::foreach_representative (std::function> & list_a) { diff --git a/nano/secure/ledger.hpp b/nano/secure/ledger.hpp index 6293ac1b62..ecd2a2627b 100644 --- a/nano/secure/ledger.hpp +++ b/nano/secure/ledger.hpp @@ -53,9 +53,16 @@ class ledger final bool block_exists (store::transaction const & transaction, nano::block_hash const & hash) const; nano::uint128_t account_balance (store::transaction const &, nano::account const &, bool = false); nano::uint128_t account_receivable (store::transaction const &, nano::account const &, bool = false); + /** + * Returns the cached vote weight for the given representative. + * If the weight is below the cache limit it returns 0. + * During bootstrap it returns the preconfigured bootstrap weights. + */ nano::uint128_t weight (nano::account const &); std::optional successor (store::transaction const &, nano::qualified_root const &) const noexcept; std::optional successor (store::transaction const & transaction, nano::block_hash const & hash) const noexcept; + /* Returns the exact vote weight for the given representative by doing a database lookup */ + nano::uint128_t weight_exact (store::transaction const &, nano::account const &); std::shared_ptr forked_block (store::transaction const &, nano::block const &); std::shared_ptr head_block (store::transaction const &, nano::account const &); bool block_confirmed (store::transaction const &, nano::block_hash const &) const;