From d59565539206158076ab043042b1f8e86cb0e2c9 Mon Sep 17 00:00:00 2001 From: RickiNano <81099017+RickiNano@users.noreply.github.com> Date: Mon, 11 Mar 2024 13:52:29 +0100 Subject: [PATCH 001/128] Multithreaded request aggregator (#4469) * Run aggregator threaded * Configurable number of aggregator threads * Default to max 4 threads * Added unit test for request_aggregator_threads toml * Improve the description of request_aggregator_threads --------- Co-authored-by: Dimitrios Siganos --- nano/core_test/toml.cpp | 3 +++ nano/node/nodeconfig.cpp | 2 ++ nano/node/nodeconfig.hpp | 1 + nano/node/request_aggregator.cpp | 16 ++++++++++++---- nano/node/request_aggregator.hpp | 4 +++- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/nano/core_test/toml.cpp b/nano/core_test/toml.cpp index 4e13e7b42..9e2a5cbe8 100644 --- a/nano/core_test/toml.cpp +++ b/nano/core_test/toml.cpp @@ -193,6 +193,7 @@ TEST (toml, daemon_config_deserialize_defaults) ASSERT_EQ (conf.node.work_peers, defaults.node.work_peers); ASSERT_EQ (conf.node.work_threads, defaults.node.work_threads); ASSERT_EQ (conf.node.max_queued_requests, defaults.node.max_queued_requests); + ASSERT_EQ (conf.node.request_aggregator_threads, defaults.node.request_aggregator_threads); ASSERT_EQ (conf.node.max_unchecked_blocks, defaults.node.max_unchecked_blocks); ASSERT_EQ (conf.node.backlog_scan_batch_size, defaults.node.backlog_scan_batch_size); ASSERT_EQ (conf.node.backlog_scan_frequency, defaults.node.backlog_scan_frequency); @@ -422,6 +423,7 @@ TEST (toml, daemon_config_deserialize_no_defaults) work_threads = 999 max_work_generate_multiplier = 1.0 max_queued_requests = 999 + request_aggregator_threads = 999 max_unchecked_blocks = 999 frontiers_confirmation = "always" backlog_scan_batch_size = 999 @@ -613,6 +615,7 @@ TEST (toml, daemon_config_deserialize_no_defaults) ASSERT_NE (conf.node.work_peers, defaults.node.work_peers); ASSERT_NE (conf.node.work_threads, defaults.node.work_threads); ASSERT_NE (conf.node.max_queued_requests, defaults.node.max_queued_requests); + ASSERT_NE (conf.node.request_aggregator_threads, defaults.node.request_aggregator_threads); ASSERT_NE (conf.node.backlog_scan_batch_size, defaults.node.backlog_scan_batch_size); ASSERT_NE (conf.node.backlog_scan_frequency, defaults.node.backlog_scan_frequency); diff --git a/nano/node/nodeconfig.cpp b/nano/node/nodeconfig.cpp index 2d7e23ab4..e5b2cad9f 100644 --- a/nano/node/nodeconfig.cpp +++ b/nano/node/nodeconfig.cpp @@ -130,6 +130,7 @@ nano::error nano::node_config::serialize_toml (nano::tomlconfig & toml) const toml.put ("max_work_generate_multiplier", max_work_generate_multiplier, "Maximum allowed difficulty multiplier for work generation.\ntype:double,[1..]"); toml.put ("frontiers_confirmation", serialize_frontiers_confirmation (frontiers_confirmation), "Mode controlling frontier confirmation rate.\ntype:string,{auto,always,disabled}"); toml.put ("max_queued_requests", max_queued_requests, "Limit for number of queued confirmation requests for one channel, after which new requests are dropped until the queue drops below this value.\ntype:uint32"); + toml.put ("request_aggregator_threads", request_aggregator_threads, "Number of threads to dedicate to request aggregator. The default value is the minimum of 4 or the number returned by nano::hardware_concurency(), which is the number of hardware threads or the value of the environment variable NANO_HARDWARE_CONCURRENCY."); toml.put ("max_unchecked_blocks", max_unchecked_blocks, "Maximum number of unchecked blocks to store in memory. Defaults to 65536. \ntype:uint64,[0..]"); toml.put ("rep_crawler_weight_minimum", rep_crawler_weight_minimum.to_string_dec (), "Rep crawler minimum weight, if this is less than minimum principal weight then this is taken as the minimum weight a rep must have to be tracked. If you want to track all reps set this to 0. If you do not want this to influence anything then set it to max value. This is only useful for debugging or for people who really know what they are doing.\ntype:string,amount,raw"); toml.put ("backlog_scan_batch_size", backlog_scan_batch_size, "Number of accounts per second to process when doing backlog population scan. Increasing this value will help unconfirmed frontiers get into election prioritization queue faster, however it will also increase resource usage. \ntype:uint"); @@ -427,6 +428,7 @@ nano::error nano::node_config::deserialize_toml (nano::tomlconfig & toml) toml.get ("max_work_generate_multiplier", max_work_generate_multiplier); toml.get ("max_queued_requests", max_queued_requests); + toml.get ("request_aggregator_threads", request_aggregator_threads); toml.get ("max_unchecked_blocks", max_unchecked_blocks); diff --git a/nano/node/nodeconfig.hpp b/nano/node/nodeconfig.hpp index 252f6e07f..cf778df4e 100644 --- a/nano/node/nodeconfig.hpp +++ b/nano/node/nodeconfig.hpp @@ -118,6 +118,7 @@ public: bool backup_before_upgrade{ false }; double max_work_generate_multiplier{ 64. }; uint32_t max_queued_requests{ 512 }; + unsigned request_aggregator_threads{ std::min (nano::hardware_concurrency (), 4u) }; // Max 4 threads if available unsigned max_unchecked_blocks{ 65536 }; std::chrono::seconds max_pruning_age{ !network_params.network.is_beta_network () ? std::chrono::seconds (24 * 60 * 60) : std::chrono::seconds (5 * 60) }; // 1 day; 5 minutes for beta network uint64_t max_pruning_depth{ 0 }; diff --git a/nano/node/request_aggregator.cpp b/nano/node/request_aggregator.cpp index 0e61195e8..72a5e3e40 100644 --- a/nano/node/request_aggregator.cpp +++ b/nano/node/request_aggregator.cpp @@ -15,15 +15,20 @@ nano::request_aggregator::request_aggregator (nano::node_config const & config_a max_delay (config_a.network_params.network.is_dev_network () ? 50 : 300), small_delay (config_a.network_params.network.is_dev_network () ? 10 : 50), max_channel_requests (config_a.max_queued_requests), + request_aggregator_threads (config_a.request_aggregator_threads), stats (stats_a), local_votes (history_a), ledger (ledger_a), wallets (wallets_a), active (active_a), generator (generator_a), - final_generator (final_generator_a), - thread ([this] () { run (); }) + final_generator (final_generator_a) { + for (auto i = 0; i < request_aggregator_threads; ++i) + { + threads.emplace_back ([this] () { run (); }); + } + generator.set_reply_action ([this] (std::shared_ptr const & vote_a, std::shared_ptr const & channel_a) { this->reply_action (vote_a, channel_a); }); @@ -132,9 +137,12 @@ void nano::request_aggregator::stop () stopped = true; } condition.notify_all (); - if (thread.joinable ()) + for (auto & thread : threads) { - thread.join (); + if (thread.joinable ()) + { + thread.join (); + } } } diff --git a/nano/node/request_aggregator.hpp b/nano/node/request_aggregator.hpp index 657b7b56f..f88a6b4fa 100644 --- a/nano/node/request_aggregator.hpp +++ b/nano/node/request_aggregator.hpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace mi = boost::multi_index; @@ -74,6 +75,7 @@ public: std::chrono::milliseconds const max_delay; std::chrono::milliseconds const small_delay; std::size_t const max_channel_requests; + std::size_t const request_aggregator_threads; private: void run (); @@ -105,7 +107,7 @@ private: bool started{ false }; nano::condition_variable condition; nano::mutex mutex{ mutex_identifier (mutexes::request_aggregator) }; - std::thread thread; + std::vector threads; friend std::unique_ptr collect_container_info (request_aggregator &, std::string const &); }; From 47e7c811680f5ea1332e19ad1fc7ed01f838ad0d Mon Sep 17 00:00:00 2001 From: clemahieu Date: Mon, 11 Mar 2024 21:09:46 +0000 Subject: [PATCH 002/128] Remove ledger::block_destination and use block::destination (#4478) --- nano/core_test/ledger.cpp | 12 ++++++------ nano/node/active_transactions.cpp | 5 ++--- nano/node/json_handler.cpp | 14 ++++++-------- nano/qt/qt.cpp | 4 ++-- nano/secure/ledger.cpp | 16 ---------------- nano/secure/ledger.hpp | 1 - 6 files changed, 16 insertions(+), 36 deletions(-) diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index a08eb2422..28d2b323b 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -2328,17 +2328,17 @@ TEST (ledger, block_destination_source) ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, block5)); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, block6)); ASSERT_EQ (balance, ledger.balance (transaction, block6->hash ())); - ASSERT_EQ (dest.pub, ledger.block_destination (transaction, *block1)); + ASSERT_EQ (dest.pub, block1->destination ()); ASSERT_FALSE (block1->source_field ()); - ASSERT_EQ (nano::dev::genesis_key.pub, ledger.block_destination (transaction, *block2)); + ASSERT_EQ (nano::dev::genesis_key.pub, block2->destination ()); ASSERT_FALSE (block2->source_field ()); - ASSERT_EQ (ledger.block_destination (transaction, *block3), nullptr); + ASSERT_FALSE (block3->destination_field ()); ASSERT_EQ (block2->hash (), block3->source ()); - ASSERT_EQ (dest.pub, ledger.block_destination (transaction, *block4)); + ASSERT_EQ (dest.pub, block4->destination ()); ASSERT_FALSE (block4->source_field ()); - ASSERT_EQ (nano::dev::genesis_key.pub, ledger.block_destination (transaction, *block5)); + ASSERT_EQ (nano::dev::genesis_key.pub, block5->destination ()); ASSERT_FALSE (block5->source_field ()); - ASSERT_EQ (ledger.block_destination (transaction, *block6), nullptr); + ASSERT_FALSE (block6->destination_field ()); ASSERT_EQ (block5->hash (), block6->source ()); } diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 107e3df7c..be7910775 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -213,12 +213,11 @@ void nano::active_transactions::handle_final_votes_confirmation (std::shared_ptr void nano::active_transactions::activate_successors (const nano::account & account, std::shared_ptr const & block, nano::store::read_transaction const & transaction) { node.scheduler.priority.activate (account, transaction); - auto const & destination = node.ledger.block_destination (transaction, *block); // Start or vote for the next unconfirmed block in the destination account - if (!destination.is_zero () && destination != account) + if (block->is_send () && !block->destination ().is_zero () && block->destination () != account) { - node.scheduler.priority.activate (destination, transaction); + node.scheduler.priority.activate (block->destination (), transaction); } } diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index e8fb99146..27e7c92f1 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -1342,8 +1342,7 @@ void nano::json_handler::blocks_info () } if (receivable || receive_hash) { - auto destination (node.ledger.block_destination (transaction, *block)); - if (destination.is_zero ()) + if (!block->is_send ()) { if (receivable) { @@ -1355,7 +1354,7 @@ void nano::json_handler::blocks_info () entry.put ("receive_hash", nano::block_hash (0).to_string ()); } } - else if (node.store.pending.exists (transaction, nano::pending_key (destination, hash))) + else if (node.store.pending.exists (transaction, nano::pending_key (block->destination (), hash))) { if (receivable) { @@ -1376,7 +1375,7 @@ void nano::json_handler::blocks_info () } if (receive_hash) { - std::shared_ptr receive_block = node.ledger.find_receive_block_by_send_hash (transaction, destination, hash); + std::shared_ptr receive_block = node.ledger.find_receive_block_by_send_hash (transaction, block->destination (), hash); std::string receive_hash = receive_block ? receive_block->hash ().to_string () : nano::block_hash (0).to_string (); entry.put ("receive_hash", receive_hash); } @@ -3163,10 +3162,9 @@ void nano::json_handler::receivable_exists () if (block != nullptr) { auto exists (false); - auto destination (node.ledger.block_destination (transaction, *block)); - if (!destination.is_zero ()) + if (block->is_send ()) { - exists = node.store.pending.exists (transaction, nano::pending_key (destination, hash)); + exists = node.store.pending.exists (transaction, nano::pending_key (block->destination (), hash)); } exists = exists && (block_confirmed (node, transaction, block->hash (), include_active, include_only_confirmed)); response_l.put ("exists", exists ? "1" : "0"); @@ -3668,7 +3666,7 @@ void nano::json_handler::republish () if (destinations != 0) // Republish destination chain { auto block_b = node.ledger.block (transaction, hash); - auto destination (node.ledger.block_destination (transaction, *block_b)); + auto destination = block_b->destination (); if (!destination.is_zero ()) { if (!node.store.pending.exists (transaction, nano::pending_key (destination, hash))) diff --git a/nano/qt/qt.cpp b/nano/qt/qt.cpp index b4d88c579..7c0cd5874 100644 --- a/nano/qt/qt.cpp +++ b/nano/qt/qt.cpp @@ -2318,7 +2318,7 @@ void nano_qt::block_creation::create_receive () auto block_l (wallet.node.ledger.block (block_transaction, source_l)); if (block_l != nullptr) { - auto const & destination (wallet.node.ledger.block_destination (block_transaction, *block_l)); + auto destination = block_l->destination (); if (!destination.is_zero ()) { nano::pending_key pending_key (destination, source_l); @@ -2483,7 +2483,7 @@ void nano_qt::block_creation::create_open () auto block_l (wallet.node.ledger.block (block_transaction, source_l)); if (block_l != nullptr) { - auto const & destination (wallet.node.ledger.block_destination (block_transaction, *block_l)); + auto destination = block_l->destination (); if (!destination.is_zero ()) { nano::pending_key pending_key (destination, source_l); diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index ca6ec8b2f..b15937100 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -950,22 +950,6 @@ std::string nano::ledger::block_text (nano::block_hash const & hash_a) return result; } -nano::account const & nano::ledger::block_destination (store::transaction const & transaction_a, nano::block const & block_a) -{ - nano::send_block const * send_block (dynamic_cast (&block_a)); - nano::state_block const * state_block (dynamic_cast (&block_a)); - if (send_block != nullptr) - { - return send_block->hashables.destination; - } - else if (state_block != nullptr && block_a.is_send ()) - { - return state_block->hashables.link.as_account (); - } - - return nano::account::null (); -} - std::pair nano::ledger::hash_root_random (store::transaction const & transaction_a) const { nano::block_hash hash (0); diff --git a/nano/secure/ledger.hpp b/nano/secure/ledger.hpp index 864902a82..084923ade 100644 --- a/nano/secure/ledger.hpp +++ b/nano/secure/ledger.hpp @@ -64,7 +64,6 @@ public: bool root_exists (store::transaction const &, nano::root const &); std::string block_text (char const *); std::string block_text (nano::block_hash const &); - nano::account const & block_destination (store::transaction const &, nano::block const &); std::pair hash_root_random (store::transaction const &) const; std::optional pending_info (store::transaction const & transaction, nano::pending_key const & key) const; nano::block_status process (store::write_transaction const & transaction, std::shared_ptr block); From c58ff456b7190cd615656338408bde257e2434ab Mon Sep 17 00:00:00 2001 From: clemahieu Date: Mon, 11 Mar 2024 23:31:54 +0000 Subject: [PATCH 003/128] Remove pragma from pending_info.cpp (#4480) --- nano/secure/pending_info.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/nano/secure/pending_info.cpp b/nano/secure/pending_info.cpp index 2989239f1..24aa14b3a 100644 --- a/nano/secure/pending_info.cpp +++ b/nano/secure/pending_info.cpp @@ -1,5 +1,3 @@ -#pragma once - #include nano::pending_info::pending_info (nano::account const & source_a, nano::amount const & amount_a, nano::epoch epoch_a) : From 623a1354f169ef94499d39f8de7398eee2c835a6 Mon Sep 17 00:00:00 2001 From: clemahieu Date: Tue, 12 Mar 2024 09:08:02 +0000 Subject: [PATCH 004/128] Remove unused function ledger::root_exists (#4481) --- nano/secure/ledger.cpp | 5 ----- nano/secure/ledger.hpp | 1 - 2 files changed, 6 deletions(-) diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index b15937100..142d68df9 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -928,11 +928,6 @@ bool nano::ledger::block_or_pruned_exists (store::transaction const & transactio return block_exists (transaction_a, hash_a); } -bool nano::ledger::root_exists (store::transaction const & transaction_a, nano::root const & root_a) -{ - return block_exists (transaction_a, root_a.as_block_hash ()) || store.account.exists (transaction_a, root_a.as_account ()); -} - std::string nano::ledger::block_text (char const * hash_a) { return block_text (nano::block_hash (hash_a)); diff --git a/nano/secure/ledger.hpp b/nano/secure/ledger.hpp index 084923ade..cf6e6515b 100644 --- a/nano/secure/ledger.hpp +++ b/nano/secure/ledger.hpp @@ -61,7 +61,6 @@ public: nano::block_hash representative_calculated (store::transaction const &, nano::block_hash const &); bool block_or_pruned_exists (nano::block_hash const &) const; bool block_or_pruned_exists (store::transaction const &, nano::block_hash const &) const; - bool root_exists (store::transaction const &, nano::root const &); std::string block_text (char const *); std::string block_text (nano::block_hash const &); std::pair hash_root_random (store::transaction const &) const; From 751553710b364843f021b26420f422daff0c676d Mon Sep 17 00:00:00 2001 From: clemahieu Date: Tue, 12 Mar 2024 09:08:31 +0000 Subject: [PATCH 005/128] Convert store::pending::get to return an optional rather than using an out-argument and returning an error code. (#4479) --- nano/core_test/block_store.cpp | 10 +++---- nano/core_test/ledger.cpp | 3 +- nano/qt/qt.cpp | 14 ++++------ nano/secure/ledger.cpp | 50 ++++++++++++++++------------------ nano/store/lmdb/pending.cpp | 8 ++++-- nano/store/lmdb/pending.hpp | 2 +- nano/store/pending.hpp | 3 +- nano/store/rocksdb/pending.cpp | 8 ++++-- nano/store/rocksdb/pending.hpp | 2 +- 9 files changed, 49 insertions(+), 51 deletions(-) diff --git a/nano/core_test/block_store.cpp b/nano/core_test/block_store.cpp index 08e267716..07ca6d9b0 100644 --- a/nano/core_test/block_store.cpp +++ b/nano/core_test/block_store.cpp @@ -295,15 +295,15 @@ TEST (block_store, add_pending) ASSERT_TRUE (!store->init_error ()); nano::keypair key1; nano::pending_key key2 (0, 0); - nano::pending_info pending1; auto transaction (store->tx_begin_write ()); - ASSERT_TRUE (store->pending.get (transaction, key2, pending1)); + ASSERT_FALSE (store->pending.get (transaction, key2)); + nano::pending_info pending1; store->pending.put (transaction, key2, pending1); - nano::pending_info pending2; - ASSERT_FALSE (store->pending.get (transaction, key2, pending2)); + std::optional pending2; + ASSERT_TRUE (pending2 = store->pending.get (transaction, key2)); ASSERT_EQ (pending1, pending2); store->pending.del (transaction, key2); - ASSERT_TRUE (store->pending.get (transaction, key2, pending2)); + ASSERT_FALSE (store->pending.get (transaction, key2)); } TEST (block_store, pending_iterator) diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 28d2b323b..f74d115a6 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -5481,8 +5481,7 @@ TEST (ledger, migrate_lmdb_to_rocksdb) nano::store::rocksdb::component rocksdb_store{ logger, path / "rocksdb", nano::dev::constants }; auto rocksdb_transaction (rocksdb_store.tx_begin_read ()); - nano::pending_info pending_info{}; - ASSERT_FALSE (rocksdb_store.pending.get (rocksdb_transaction, nano::pending_key (nano::dev::genesis_key.pub, send->hash ()), pending_info)); + ASSERT_TRUE (rocksdb_store.pending.get (rocksdb_transaction, nano::pending_key (nano::dev::genesis_key.pub, send->hash ()))); for (auto i = rocksdb_store.online_weight.begin (rocksdb_transaction); i != rocksdb_store.online_weight.end (); ++i) { diff --git a/nano/qt/qt.cpp b/nano/qt/qt.cpp index 7c0cd5874..2b07123e2 100644 --- a/nano/qt/qt.cpp +++ b/nano/qt/qt.cpp @@ -2322,8 +2322,7 @@ void nano_qt::block_creation::create_receive () if (!destination.is_zero ()) { nano::pending_key pending_key (destination, source_l); - nano::pending_info pending; - if (!wallet.node.store.pending.get (block_transaction, pending_key, pending)) + if (auto pending = wallet.node.store.pending.get (block_transaction, pending_key)) { nano::account_info info; auto error (wallet.node.store.account.get (block_transaction, pending_key.account, info)); @@ -2333,10 +2332,10 @@ void nano_qt::block_creation::create_receive () auto error (wallet.wallet_m->store.fetch (transaction, pending_key.account, key)); if (!error) { - nano::state_block receive (pending_key.account, info.head, info.representative, info.balance.number () + pending.amount.number (), source_l, key, pending_key.account, 0); + nano::state_block receive (pending_key.account, info.head, info.representative, info.balance.number () + pending.value ().amount.number (), source_l, key, pending_key.account, 0); nano::block_details details; details.is_receive = true; - details.epoch = std::max (info.epoch (), pending.epoch); + details.epoch = std::max (info.epoch (), pending.value ().epoch); auto required_difficulty{ wallet.node.network_params.work.threshold (receive.work_version (), details) }; if (wallet.node.work_generate_blocking (receive, required_difficulty).has_value ()) { @@ -2487,8 +2486,7 @@ void nano_qt::block_creation::create_open () if (!destination.is_zero ()) { nano::pending_key pending_key (destination, source_l); - nano::pending_info pending; - if (!wallet.node.store.pending.get (block_transaction, pending_key, pending)) + if (auto pending = wallet.node.store.pending.get (block_transaction, pending_key)) { nano::account_info info; auto error (wallet.node.store.account.get (block_transaction, pending_key.account, info)); @@ -2498,10 +2496,10 @@ void nano_qt::block_creation::create_open () auto error (wallet.wallet_m->store.fetch (transaction, pending_key.account, key)); if (!error) { - nano::state_block open (pending_key.account, 0, representative_l, pending.amount, source_l, key, pending_key.account, 0); + nano::state_block open (pending_key.account, 0, representative_l, pending.value ().amount, source_l, key, pending_key.account, 0); nano::block_details details; details.is_receive = true; - details.epoch = pending.epoch; + details.epoch = pending.value ().epoch; auto const required_difficulty{ wallet.node.network_params.work.threshold (open.work_version (), details) }; if (wallet.node.work_generate_blocking (open, required_difficulty).has_value ()) { diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 142d68df9..e31765289 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -39,23 +39,24 @@ public: void send_block (nano::send_block const & block_a) override { auto hash (block_a.hash ()); - nano::pending_info pending; nano::pending_key key (block_a.hashables.destination, hash); - while (!error && ledger.store.pending.get (transaction, key, pending)) + auto pending = ledger.store.pending.get (transaction, key); + while (!error && !pending.has_value ()) { error = ledger.rollback (transaction, ledger.latest (transaction, block_a.hashables.destination), list); + pending = ledger.store.pending.get (transaction, key); } if (!error) { - auto info = ledger.account_info (transaction, pending.source); + auto info = ledger.account_info (transaction, pending.value ().source); debug_assert (info); ledger.store.pending.del (transaction, key); - ledger.cache.rep_weights.representation_add (info->representative, pending.amount.number ()); + ledger.cache.rep_weights.representation_add (info->representative, pending.value ().amount.number ()); nano::account_info new_info (block_a.hashables.previous, info->representative, info->open_block, ledger.balance (transaction, block_a.hashables.previous).value (), nano::seconds_since_epoch (), info->block_count - 1, nano::epoch::epoch_0); - ledger.update_account (transaction, pending.source, *info, new_info); + ledger.update_account (transaction, pending.value ().source, *info, new_info); ledger.store.block.del (transaction, hash); ledger.store.frontier.del (transaction, hash); - ledger.store.frontier.put (transaction, block_a.hashables.previous, pending.source); + ledger.store.frontier.put (transaction, block_a.hashables.previous, pending.value ().source); ledger.store.block.successor_clear (transaction, block_a.hashables.previous); ledger.stats.inc (nano::stat::type::rollback, nano::stat::detail::send); } @@ -315,12 +316,12 @@ void ledger_processor::state_block_impl (nano::state_block & block_a) if (result == nano::block_status::progress) { nano::pending_key key (block_a.hashables.account, block_a.hashables.link.as_block_hash ()); - nano::pending_info pending; - result = ledger.store.pending.get (transaction, key, pending) ? nano::block_status::unreceivable : nano::block_status::progress; // Has this source already been received (Malformed) + auto pending = ledger.store.pending.get (transaction, key); + result = !pending ? nano::block_status::unreceivable : nano::block_status::progress; // Has this source already been received (Malformed) if (result == nano::block_status::progress) { - result = amount == pending.amount ? nano::block_status::progress : nano::block_status::balance_mismatch; - source_epoch = pending.epoch; + result = amount == pending.value ().amount ? nano::block_status::progress : nano::block_status::balance_mismatch; + source_epoch = pending.value ().epoch; epoch = std::max (epoch, source_epoch); } } @@ -577,18 +578,18 @@ void ledger_processor::receive_block (nano::receive_block & block_a) if (result == nano::block_status::progress) { nano::pending_key key (account, block_a.hashables.source); - nano::pending_info pending; - result = ledger.store.pending.get (transaction, key, pending) ? nano::block_status::unreceivable : nano::block_status::progress; // Has this source already been received (Malformed) + auto pending = ledger.store.pending.get (transaction, key); + result = !pending ? nano::block_status::unreceivable : nano::block_status::progress; // Has this source already been received (Malformed) if (result == nano::block_status::progress) { - result = pending.epoch == nano::epoch::epoch_0 ? nano::block_status::progress : nano::block_status::unreceivable; // Are we receiving a state-only send? (Malformed) + result = pending.value ().epoch == nano::epoch::epoch_0 ? nano::block_status::progress : nano::block_status::unreceivable; // Are we receiving a state-only send? (Malformed) if (result == nano::block_status::progress) { nano::block_details block_details (nano::epoch::epoch_0, false /* unused */, false /* unused */, false /* unused */); result = ledger.constants.work.difficulty (block_a) >= ledger.constants.work.threshold (block_a.work_version (), block_details) ? nano::block_status::progress : nano::block_status::insufficient_work; // Does this block have sufficient work? (Malformed) if (result == nano::block_status::progress) { - auto new_balance (info->balance.number () + pending.amount.number ()); + auto new_balance (info->balance.number () + pending.value ().amount.number ()); #ifdef NDEBUG if (ledger.store.block.exists (transaction, block_a.hashables.source)) { @@ -601,7 +602,7 @@ void ledger_processor::receive_block (nano::receive_block & block_a) ledger.store.block.put (transaction, hash, block_a); nano::account_info new_info (hash, info->representative, info->open_block, new_balance, nano::seconds_since_epoch (), info->block_count + 1, nano::epoch::epoch_0); ledger.update_account (transaction, account, *info, new_info); - ledger.cache.rep_weights.representation_add (info->representative, pending.amount.number ()); + ledger.cache.rep_weights.representation_add (info->representative, pending.value ().amount.number ()); ledger.store.frontier.del (transaction, block_a.hashables.previous); ledger.store.frontier.put (transaction, hash, account); ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::receive); @@ -640,14 +641,14 @@ void ledger_processor::open_block (nano::open_block & block_a) if (result == nano::block_status::progress) { nano::pending_key key (block_a.hashables.account, block_a.hashables.source); - nano::pending_info pending; - result = ledger.store.pending.get (transaction, key, pending) ? nano::block_status::unreceivable : nano::block_status::progress; // Has this source already been received (Malformed) + auto pending = ledger.store.pending.get (transaction, key); + result = !pending ? nano::block_status::unreceivable : nano::block_status::progress; // Has this source already been received (Malformed) if (result == nano::block_status::progress) { result = block_a.hashables.account == ledger.constants.burn_account ? nano::block_status::opened_burn_account : nano::block_status::progress; // Is it burning 0 account? (Malicious) if (result == nano::block_status::progress) { - result = pending.epoch == nano::epoch::epoch_0 ? nano::block_status::progress : nano::block_status::unreceivable; // Are we receiving a state-only send? (Malformed) + result = pending.value ().epoch == nano::epoch::epoch_0 ? nano::block_status::progress : nano::block_status::unreceivable; // Are we receiving a state-only send? (Malformed) if (result == nano::block_status::progress) { nano::block_details block_details (nano::epoch::epoch_0, false /* unused */, false /* unused */, false /* unused */); @@ -663,11 +664,11 @@ void ledger_processor::open_block (nano::open_block & block_a) } #endif ledger.store.pending.del (transaction, key); - block_a.sideband_set (nano::block_sideband (block_a.hashables.account, 0, pending.amount, 1, nano::seconds_since_epoch (), block_details, nano::epoch::epoch_0 /* unused */)); + block_a.sideband_set (nano::block_sideband (block_a.hashables.account, 0, pending.value ().amount, 1, nano::seconds_since_epoch (), block_details, nano::epoch::epoch_0 /* unused */)); ledger.store.block.put (transaction, hash, block_a); - nano::account_info new_info (hash, block_a.representative_field ().value (), hash, pending.amount.number (), nano::seconds_since_epoch (), 1, nano::epoch::epoch_0); + nano::account_info new_info (hash, block_a.representative_field ().value (), hash, pending.value ().amount.number (), nano::seconds_since_epoch (), 1, nano::epoch::epoch_0); ledger.update_account (transaction, block_a.hashables.account, info, new_info); - ledger.cache.rep_weights.representation_add (block_a.representative_field ().value (), pending.amount.number ()); + ledger.cache.rep_weights.representation_add (block_a.representative_field ().value (), pending.value ().amount.number ()); ledger.store.frontier.put (transaction, hash, block_a.hashables.account); ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::open); } @@ -880,12 +881,7 @@ nano::uint128_t nano::ledger::account_receivable (store::transaction const & tra std::optional nano::ledger::pending_info (store::transaction const & transaction, nano::pending_key const & key) const { - nano::pending_info result; - if (!store.pending.get (transaction, key, result)) - { - return result; - } - return std::nullopt; + return store.pending.get (transaction, key); } nano::block_status nano::ledger::process (store::write_transaction const & transaction_a, std::shared_ptr block_a) diff --git a/nano/store/lmdb/pending.cpp b/nano/store/lmdb/pending.cpp index 961b20182..cabb70896 100644 --- a/nano/store/lmdb/pending.cpp +++ b/nano/store/lmdb/pending.cpp @@ -17,16 +17,18 @@ void nano::store::lmdb::pending::del (store::write_transaction const & transacti store.release_assert_success (status); } -bool nano::store::lmdb::pending::get (store::transaction const & transaction, nano::pending_key const & key, nano::pending_info & pending_a) +std::optional nano::store::lmdb::pending::get (store::transaction const & transaction, nano::pending_key const & key) { nano::store::lmdb::db_val value; auto status1 = store.get (transaction, tables::pending, key, value); release_assert (store.success (status1) || store.not_found (status1)); - bool result (true); + std::optional result; if (store.success (status1)) { nano::bufferstream stream (reinterpret_cast (value.data ()), value.size ()); - result = pending_a.deserialize (stream); + result = nano::pending_info{}; + auto error = result.value ().deserialize (stream); + release_assert (!error); } return result; } diff --git a/nano/store/lmdb/pending.hpp b/nano/store/lmdb/pending.hpp index 1c110fa18..13cd9172d 100644 --- a/nano/store/lmdb/pending.hpp +++ b/nano/store/lmdb/pending.hpp @@ -19,7 +19,7 @@ public: explicit pending (nano::store::lmdb::component & store_a); void put (store::write_transaction const & transaction_a, nano::pending_key const & key_a, nano::pending_info const & pending_info_a) override; void del (store::write_transaction const & transaction_a, nano::pending_key const & key_a) override; - bool get (store::transaction const & transaction_a, nano::pending_key const & key_a, nano::pending_info & pending_a) override; + std::optional get (store::transaction const & transaction_a, nano::pending_key const & key_a) override; bool exists (store::transaction const & transaction_a, nano::pending_key const & key_a) override; bool any (store::transaction const & transaction_a, nano::account const & account_a) override; store::iterator begin (store::transaction const & transaction_a, nano::pending_key const & key_a) const override; diff --git a/nano/store/pending.hpp b/nano/store/pending.hpp index 1531c7f7c..49f2b8a31 100644 --- a/nano/store/pending.hpp +++ b/nano/store/pending.hpp @@ -5,6 +5,7 @@ #include #include +#include namespace nano { @@ -22,7 +23,7 @@ class pending public: virtual void put (store::write_transaction const &, nano::pending_key const &, nano::pending_info const &) = 0; virtual void del (store::write_transaction const &, nano::pending_key const &) = 0; - virtual bool get (store::transaction const &, nano::pending_key const &, nano::pending_info &) = 0; + virtual std::optional get (store::transaction const &, nano::pending_key const &) = 0; virtual bool exists (store::transaction const &, nano::pending_key const &) = 0; virtual bool any (store::transaction const &, nano::account const &) = 0; virtual store::iterator begin (store::transaction const &, nano::pending_key const &) const = 0; diff --git a/nano/store/rocksdb/pending.cpp b/nano/store/rocksdb/pending.cpp index f7e843f18..27efe5e63 100644 --- a/nano/store/rocksdb/pending.cpp +++ b/nano/store/rocksdb/pending.cpp @@ -17,16 +17,18 @@ void nano::store::rocksdb::pending::del (store::write_transaction const & transa store.release_assert_success (status); } -bool nano::store::rocksdb::pending::get (store::transaction const & transaction, nano::pending_key const & key, nano::pending_info & pending) +std::optional nano::store::rocksdb::pending::get (store::transaction const & transaction, nano::pending_key const & key) { nano::store::rocksdb::db_val value; auto status1 = store.get (transaction, tables::pending, key, value); release_assert (store.success (status1) || store.not_found (status1)); - bool result (true); + std::optional result; if (store.success (status1)) { nano::bufferstream stream (reinterpret_cast (value.data ()), value.size ()); - result = pending.deserialize (stream); + result = nano::pending_info{}; + auto error = result.value ().deserialize (stream); + release_assert (!error); } return result; } diff --git a/nano/store/rocksdb/pending.hpp b/nano/store/rocksdb/pending.hpp index 76a5f67d6..0ee4ced54 100644 --- a/nano/store/rocksdb/pending.hpp +++ b/nano/store/rocksdb/pending.hpp @@ -13,7 +13,7 @@ public: explicit pending (nano::store::rocksdb::component & store_a); void put (store::write_transaction const & transaction_a, nano::pending_key const & key_a, nano::pending_info const & pending_info_a) override; void del (store::write_transaction const & transaction_a, nano::pending_key const & key_a) override; - bool get (store::transaction const & transaction_a, nano::pending_key const & key_a, nano::pending_info & pending_a) override; + std::optional get (store::transaction const & transaction_a, nano::pending_key const & key_a) override; bool exists (store::transaction const & transaction_a, nano::pending_key const & key_a) override; bool any (store::transaction const & transaction_a, nano::account const & account_a) override; store::iterator begin (store::transaction const & transaction_a, nano::pending_key const & key_a) const override; From da4e9eb66a0fd723c06d1690e17aba2601755988 Mon Sep 17 00:00:00 2001 From: RickiNano <81099017+RickiNano@users.noreply.github.com> Date: Tue, 12 Mar 2024 12:50:33 +0100 Subject: [PATCH 006/128] Changed description of request_aggregator_threads (#4477) * Changed description of request_aggregator_threads to something more user friendly * Improved description --- nano/node/nodeconfig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nano/node/nodeconfig.cpp b/nano/node/nodeconfig.cpp index e5b2cad9f..16f3c897b 100644 --- a/nano/node/nodeconfig.cpp +++ b/nano/node/nodeconfig.cpp @@ -130,7 +130,7 @@ nano::error nano::node_config::serialize_toml (nano::tomlconfig & toml) const toml.put ("max_work_generate_multiplier", max_work_generate_multiplier, "Maximum allowed difficulty multiplier for work generation.\ntype:double,[1..]"); toml.put ("frontiers_confirmation", serialize_frontiers_confirmation (frontiers_confirmation), "Mode controlling frontier confirmation rate.\ntype:string,{auto,always,disabled}"); toml.put ("max_queued_requests", max_queued_requests, "Limit for number of queued confirmation requests for one channel, after which new requests are dropped until the queue drops below this value.\ntype:uint32"); - toml.put ("request_aggregator_threads", request_aggregator_threads, "Number of threads to dedicate to request aggregator. The default value is the minimum of 4 or the number returned by nano::hardware_concurency(), which is the number of hardware threads or the value of the environment variable NANO_HARDWARE_CONCURRENCY."); + toml.put ("request_aggregator_threads", request_aggregator_threads, "Number of threads to dedicate to request aggregator. Defaults to using all cpu threads, up to a maximum of 4"); toml.put ("max_unchecked_blocks", max_unchecked_blocks, "Maximum number of unchecked blocks to store in memory. Defaults to 65536. \ntype:uint64,[0..]"); toml.put ("rep_crawler_weight_minimum", rep_crawler_weight_minimum.to_string_dec (), "Rep crawler minimum weight, if this is less than minimum principal weight then this is taken as the minimum weight a rep must have to be tracked. If you want to track all reps set this to 0. If you do not want this to influence anything then set it to max value. This is only useful for debugging or for people who really know what they are doing.\ntype:string,amount,raw"); toml.put ("backlog_scan_batch_size", backlog_scan_batch_size, "Number of accounts per second to process when doing backlog population scan. Increasing this value will help unconfirmed frontiers get into election prioritization queue faster, however it will also increase resource usage. \ntype:uint"); From 97cc02ce29325790f3ed4a7465744c5620fefff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20W=C3=B3jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Wed, 13 Mar 2024 09:56:35 +0100 Subject: [PATCH 007/128] Allow easy printing ranges of objects (#4488) --- nano/core_test/object_stream.cpp | 34 +++++++++++++++++++- nano/lib/object_stream.hpp | 3 +- nano/lib/object_stream_adapters.hpp | 49 +++++++++++++++++++++-------- 3 files changed, 70 insertions(+), 16 deletions(-) diff --git a/nano/core_test/object_stream.cpp b/nano/core_test/object_stream.cpp index ead8bad71..11f14922f 100644 --- a/nano/core_test/object_stream.cpp +++ b/nano/core_test/object_stream.cpp @@ -556,4 +556,36 @@ TEST (object_stream, to_json) )"); ASSERT_EQ (str, expected); -} \ No newline at end of file +} + +TEST (object_stream, print_range) +{ + std::deque objects; + objects.push_back ({ 1 }); + objects.push_back ({ 2 }); + objects.push_back ({ 3 }); + + std::stringstream ss1, ss2; + ss1 << nano::streamed_range (objects); + ss2 << fmt::format ("{}", nano::streamed_range (objects)); + + auto expected = trim (R"( +[ + { + uint256_union_field: "0000000000000000000000000000000000000000000000000000000000000001", + block_hash: "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + uint256_union_field: "0000000000000000000000000000000000000000000000000000000000000002", + block_hash: "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + uint256_union_field: "0000000000000000000000000000000000000000000000000000000000000003", + block_hash: "0000000000000000000000000000000000000000000000000000000000000000" + } +] +)"); + + ASSERT_EQ (ss1.str (), expected); + ASSERT_EQ (ss2.str (), expected); +} diff --git a/nano/lib/object_stream.hpp b/nano/lib/object_stream.hpp index 40c638df9..f51e6feb0 100644 --- a/nano/lib/object_stream.hpp +++ b/nano/lib/object_stream.hpp @@ -281,7 +281,7 @@ public: array_stream (array_stream const &) = delete; // Disallow copying -private: +public: template void write_single (Value const & value) { @@ -290,7 +290,6 @@ private: ctx.end_array_element (); } -public: // Handle `.write (container)` template void write (Container const & container) diff --git a/nano/lib/object_stream_adapters.hpp b/nano/lib/object_stream_adapters.hpp index 8be5b445f..29e372c86 100644 --- a/nano/lib/object_stream_adapters.hpp +++ b/nano/lib/object_stream_adapters.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -9,42 +10,64 @@ namespace nano { -template +template struct object_stream_formatter { nano::object_stream_config const & config; Streamable const & value; + Writer writer; - explicit object_stream_formatter (Streamable const & value, nano::object_stream_config const & config) : + explicit object_stream_formatter (Streamable const & value, Writer writer, nano::object_stream_config const & config) : config{ config }, - value{ value } + value{ value }, + writer{ writer } { } - friend std::ostream & operator<< (std::ostream & os, object_stream_formatter const & self) + friend std::ostream & operator<< (std::ostream & os, object_stream_formatter const & self) { nano::root_object_stream obs{ os, self.config }; - obs.write (self.value); + self.writer (self.value, obs); return os; } // Needed for fmt formatting, uses the ostream operator under the hood - friend auto format_as (object_stream_formatter const & val) + friend auto format_as (object_stream_formatter const & self) { - return fmt::streamed (val); + return fmt::streamed (self); } }; -template -auto streamed (Streamable const & value) +enum class streamed_format { - return object_stream_formatter{ value, nano::object_stream_config::default_config () }; + basic, + json +}; + +inline nano::object_stream_config const & to_object_stream_config (streamed_format format) +{ + switch (format) + { + case streamed_format::basic: + return nano::object_stream_config::default_config (); + case streamed_format::json: + return nano::object_stream_config::json_config (); + default: + debug_assert (false); + return nano::object_stream_config::default_config (); + } } template -auto streamed_as_json (Streamable const & value) +auto streamed (Streamable const & value, streamed_format format = streamed_format::basic) { - return object_stream_formatter{ value, nano::object_stream_config::json_config () }; + return object_stream_formatter{ value, [] (auto const & value, nano::root_object_stream & obs) { obs.write (value); }, to_object_stream_config (format) }; +} + +template +auto streamed_range (StreamableRange const & value, streamed_format format = streamed_format::basic) +{ + return object_stream_formatter{ value, [] (auto const & value, nano::root_object_stream & obs) { obs.write_range (value); }, to_object_stream_config (format) }; } /** @@ -109,7 +132,7 @@ template std::string to_json (Value const & value) { std::stringstream ss; - ss << nano::streamed_as_json (value); + ss << nano::streamed (value, nano::streamed_format::json); return ss.str (); } } From 7dbf64f11c2b7d7c193a27d6cbfee6c474a11d9e Mon Sep 17 00:00:00 2001 From: Gustav Schauwecker Date: Wed, 13 Mar 2024 10:20:16 +0100 Subject: [PATCH 008/128] Fix build error with new optional pending_info (#4487) --- nano/secure/ledger.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index e31765289..8e69bde8e 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -593,7 +593,7 @@ void ledger_processor::receive_block (nano::receive_block & block_a) #ifdef NDEBUG if (ledger.store.block.exists (transaction, block_a.hashables.source)) { - auto info = ledger.account_info (transaction, pending.source); + auto info = ledger.account_info (transaction, pending.value ().source); debug_assert (info); } #endif @@ -659,7 +659,7 @@ void ledger_processor::open_block (nano::open_block & block_a) if (ledger.store.block.exists (transaction, block_a.hashables.source)) { nano::account_info source_info; - [[maybe_unused]] auto error (ledger.store.account.get (transaction, pending.source, source_info)); + [[maybe_unused]] auto error (ledger.store.account.get (transaction, pending.value ().source, source_info)); debug_assert (!error); } #endif From 752317263499aca6ec54119c308267df33173f73 Mon Sep 17 00:00:00 2001 From: clemahieu Date: Thu, 14 Mar 2024 15:00:18 +0000 Subject: [PATCH 009/128] Add functions to allow pending_key objects to be in containers. (#4489) --- nano/core_test/utility.cpp | 14 ++++++++++++++ nano/secure/pending_info.cpp | 5 +++++ nano/secure/pending_info.hpp | 13 +++++++++++++ 3 files changed, 32 insertions(+) diff --git a/nano/core_test/utility.cpp b/nano/core_test/utility.cpp index 1bd6a50c2..730714dc8 100644 --- a/nano/core_test/utility.cpp +++ b/nano/core_test/utility.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -346,3 +347,16 @@ TEST (relaxed_atomic_integral, many_threads) // Check values ASSERT_EQ (0, atomic); } + +TEST (pending_key, sorting) +{ + nano::pending_key one{ 1, 2 }; + nano::pending_key two{ 1, 3 }; + nano::pending_key three{ 2, 1 }; + ASSERT_LT (one, two); + ASSERT_LT (one, three); + ASSERT_LT (two, three); + nano::pending_key one_same{ 1, 2 }; + ASSERT_EQ (std::hash{}(one), std::hash{}(one_same)); + ASSERT_NE (std::hash{}(one), std::hash{}(two)); +} diff --git a/nano/secure/pending_info.cpp b/nano/secure/pending_info.cpp index 24aa14b3a..637f4adf7 100644 --- a/nano/secure/pending_info.cpp +++ b/nano/secure/pending_info.cpp @@ -65,3 +65,8 @@ nano::account const & nano::pending_key::key () const { return account; } + +bool nano::pending_key::operator< (nano::pending_key const & other_a) const +{ + return account == other_a.account ? hash < other_a.hash : account < other_a.account; +} diff --git a/nano/secure/pending_info.hpp b/nano/secure/pending_info.hpp index 4e6946039..584337e95 100644 --- a/nano/secure/pending_info.hpp +++ b/nano/secure/pending_info.hpp @@ -28,8 +28,21 @@ public: pending_key (nano::account const &, nano::block_hash const &); bool deserialize (nano::stream &); bool operator== (nano::pending_key const &) const; + bool operator< (nano::pending_key const &) const; nano::account const & key () const; nano::account account{}; nano::block_hash hash{ 0 }; }; } // namespace nano + +namespace std +{ +template <> +struct hash<::nano::pending_key> +{ + size_t operator() (::nano::pending_key const & data_a) const + { + return hash<::nano::uint512_union>{}({ ::nano::uint256_union{ data_a.account.number () }, data_a.hash }); + } +}; +} From f109cb1ee910eaf631cb643df87fda4d2829e7c0 Mon Sep 17 00:00:00 2001 From: clemahieu Date: Thu, 14 Mar 2024 16:28:54 +0000 Subject: [PATCH 010/128] Remove usages of frontiers table from ledger_processor block checks (#4460) The account can be derived from the previous block's account field. --- nano/core_test/block_store.cpp | 5 ----- nano/core_test/ledger.cpp | 22 ++-------------------- nano/secure/ledger.cpp | 28 ++++++++++++---------------- 3 files changed, 14 insertions(+), 41 deletions(-) diff --git a/nano/core_test/block_store.cpp b/nano/core_test/block_store.cpp index 07ca6d9b0..ecd575ea4 100644 --- a/nano/core_test/block_store.cpp +++ b/nano/core_test/block_store.cpp @@ -820,11 +820,6 @@ TEST (block_store, frontier) auto transaction (store->tx_begin_write ()); nano::block_hash hash (100); nano::account account (200); - ASSERT_TRUE (store->frontier.get (transaction, hash).is_zero ()); - store->frontier.put (transaction, hash, account); - ASSERT_EQ (account, store->frontier.get (transaction, hash)); - store->frontier.del (transaction, hash); - ASSERT_TRUE (store->frontier.get (transaction, hash).is_zero ()); } TEST (block_store, block_replace) diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index f74d115a6..01c844728 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -103,16 +103,13 @@ TEST (ledger, process_send) .work (*pool.generate (info1->head)) .build (); nano::block_hash hash1 = send->hash (); - ASSERT_EQ (nano::dev::genesis_key.pub, store.frontier.get (transaction, info1->head)); ASSERT_EQ (1, info1->block_count); // This was a valid block, it should progress. auto return1 = ledger.process (transaction, send); + ASSERT_EQ (nano::block_status::progress, return1); ASSERT_EQ (nano::dev::genesis_key.pub, send->sideband ().account); ASSERT_EQ (2, send->sideband ().height); ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.amount (transaction, hash1)); - ASSERT_TRUE (store.frontier.get (transaction, info1->head).is_zero ()); - ASSERT_EQ (nano::dev::genesis_key.pub, store.frontier.get (transaction, hash1)); - ASSERT_EQ (nano::block_status::progress, return1); ASSERT_EQ (nano::dev::genesis_key.pub, send->account ()); ASSERT_EQ (50, ledger.account_balance (transaction, nano::dev::genesis_key.pub)); ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.account_receivable (transaction, key2.pub)); @@ -144,7 +141,6 @@ TEST (ledger, process_send) ASSERT_EQ (nano::block_status::progress, return2); ASSERT_EQ (key2.pub, open->account ()); ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.amount (transaction, hash2)); - ASSERT_EQ (key2.pub, store.frontier.get (transaction, hash2)); ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.account_balance (transaction, key2.pub)); ASSERT_EQ (0, ledger.account_receivable (transaction, key2.pub)); ASSERT_EQ (50, ledger.weight (nano::dev::genesis_key.pub)); @@ -164,7 +160,6 @@ TEST (ledger, process_send) ASSERT_NE (nullptr, latest5); ASSERT_EQ (*open, *latest5); ASSERT_FALSE (ledger.rollback (transaction, hash2)); - ASSERT_TRUE (store.frontier.get (transaction, hash2).is_zero ()); auto info5 = ledger.account_info (transaction, key2.pub); ASSERT_FALSE (info5); auto pending1 = ledger.pending_info (transaction, nano::pending_key (key2.pub, hash1)); @@ -181,8 +176,6 @@ TEST (ledger, process_send) ASSERT_EQ (hash1, info6->head); ASSERT_FALSE (ledger.rollback (transaction, info6->head)); ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub)); - ASSERT_EQ (nano::dev::genesis_key.pub, store.frontier.get (transaction, info1->head)); - ASSERT_TRUE (store.frontier.get (transaction, hash1).is_zero ()); auto info7 = ledger.account_info (transaction, nano::dev::genesis_key.pub); ASSERT_TRUE (info7); ASSERT_EQ (1, info7->block_count); @@ -250,14 +243,11 @@ TEST (ledger, process_receive) .work (*pool.generate (hash2)) .build (); auto hash4 = receive->hash (); - ASSERT_EQ (key2.pub, store.frontier.get (transaction, hash2)); auto return2 = ledger.process (transaction, receive); ASSERT_EQ (key2.pub, receive->sideband ().account); ASSERT_EQ (nano::dev::constants.genesis_amount - 25, receive->sideband ().balance.number ()); ASSERT_EQ (2, receive->sideband ().height); ASSERT_EQ (25, ledger.amount (transaction, hash4)); - ASSERT_TRUE (store.frontier.get (transaction, hash2).is_zero ()); - ASSERT_EQ (key2.pub, store.frontier.get (transaction, hash4)); ASSERT_EQ (nano::block_status::progress, return2); ASSERT_EQ (key2.pub, receive->account ()); ASSERT_EQ (hash4, ledger.latest (transaction, key2.pub)); @@ -267,8 +257,6 @@ TEST (ledger, process_receive) ASSERT_EQ (nano::dev::constants.genesis_amount - 25, ledger.weight (key3.pub)); ASSERT_FALSE (ledger.rollback (transaction, hash4)); ASSERT_TRUE (store.block.successor (transaction, hash2).is_zero ()); - ASSERT_EQ (key2.pub, store.frontier.get (transaction, hash2)); - ASSERT_TRUE (store.frontier.get (transaction, hash4).is_zero ()); ASSERT_EQ (25, ledger.account_balance (transaction, nano::dev::genesis_key.pub)); ASSERT_EQ (25, ledger.account_receivable (transaction, key2.pub)); ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.account_balance (transaction, key2.pub)); @@ -520,12 +508,9 @@ TEST (ledger, representative_change) .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) .work (*pool.generate (info1->head)) .build (); - ASSERT_EQ (nano::dev::genesis_key.pub, store.frontier.get (transaction, info1->head)); auto return1 (ledger.process (transaction, block)); - ASSERT_EQ (0, ledger.amount (transaction, block->hash ())); - ASSERT_TRUE (store.frontier.get (transaction, info1->head).is_zero ()); - ASSERT_EQ (nano::dev::genesis_key.pub, store.frontier.get (transaction, block->hash ())); ASSERT_EQ (nano::block_status::progress, return1); + ASSERT_EQ (0, ledger.amount (transaction, block->hash ())); ASSERT_EQ (nano::dev::genesis_key.pub, block->account ()); ASSERT_EQ (0, ledger.weight (nano::dev::genesis_key.pub)); ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (key2.pub)); @@ -533,8 +518,6 @@ TEST (ledger, representative_change) ASSERT_TRUE (info2); ASSERT_EQ (block->hash (), info2->head); ASSERT_FALSE (ledger.rollback (transaction, info2->head)); - ASSERT_EQ (nano::dev::genesis_key.pub, store.frontier.get (transaction, info1->head)); - ASSERT_TRUE (store.frontier.get (transaction, block->hash ()).is_zero ()); auto info3 = ledger.account_info (transaction, nano::dev::genesis_key.pub); ASSERT_TRUE (info3); ASSERT_EQ (info1->head, info3->head); @@ -5496,7 +5479,6 @@ TEST (ledger, migrate_lmdb_to_rocksdb) ASSERT_EQ (*send, *block1); ASSERT_TRUE (rocksdb_store.peer.exists (rocksdb_transaction, endpoint_key)); ASSERT_EQ (rocksdb_store.version.get (rocksdb_transaction), version); - ASSERT_EQ (rocksdb_store.frontier.get (rocksdb_transaction, 2), 5); nano::confirmation_height_info confirmation_height_info; ASSERT_FALSE (rocksdb_store.confirmation_height.get (rocksdb_transaction, nano::dev::genesis_key.pub, confirmation_height_info)); ASSERT_EQ (confirmation_height_info.height, 2); diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 8e69bde8e..9472af329 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -464,12 +464,12 @@ void ledger_processor::change_block (nano::change_block & block_a) result = block_a.valid_predecessor (*previous) ? nano::block_status::progress : nano::block_status::block_position; if (result == nano::block_status::progress) { - auto account (ledger.store.frontier.get (transaction, block_a.hashables.previous)); - result = account.is_zero () ? nano::block_status::fork : nano::block_status::progress; + auto account = previous->account (); + auto info = ledger.account_info (transaction, account); + debug_assert (info); + result = info->head != block_a.hashables.previous ? nano::block_status::fork : nano::block_status::progress; if (result == nano::block_status::progress) { - auto info = ledger.account_info (transaction, account); - debug_assert (info); debug_assert (info->head == block_a.hashables.previous); result = validate_message (account, hash, block_a.signature) ? nano::block_status::bad_signature : nano::block_status::progress; // Is this block signed correctly (Malformed) if (result == nano::block_status::progress) @@ -510,8 +510,10 @@ void ledger_processor::send_block (nano::send_block & block_a) result = block_a.valid_predecessor (*previous) ? nano::block_status::progress : nano::block_status::block_position; if (result == nano::block_status::progress) { - auto account (ledger.store.frontier.get (transaction, block_a.hashables.previous)); - result = account.is_zero () ? nano::block_status::fork : nano::block_status::progress; + auto account = previous->account (); + auto info = ledger.account_info (transaction, account); + debug_assert (info); + result = info->head != block_a.hashables.previous ? nano::block_status::fork : nano::block_status::progress; if (result == nano::block_status::progress) { result = validate_message (account, hash, block_a.signature) ? nano::block_status::bad_signature : nano::block_status::progress; // Is this block signed correctly (Malformed) @@ -522,8 +524,6 @@ void ledger_processor::send_block (nano::send_block & block_a) if (result == nano::block_status::progress) { debug_assert (!validate_message (account, hash, block_a.signature)); - auto info = ledger.account_info (transaction, account); - debug_assert (info); debug_assert (info->head == block_a.hashables.previous); result = info->balance.number () >= block_a.hashables.balance.number () ? nano::block_status::progress : nano::block_status::negative_spend; // Is this trying to spend a negative amount (Malicious) if (result == nano::block_status::progress) @@ -561,8 +561,10 @@ void ledger_processor::receive_block (nano::receive_block & block_a) result = block_a.valid_predecessor (*previous) ? nano::block_status::progress : nano::block_status::block_position; if (result == nano::block_status::progress) { - auto account (ledger.store.frontier.get (transaction, block_a.hashables.previous)); - result = account.is_zero () ? nano::block_status::gap_previous : nano::block_status::progress; // Have we seen the previous block? No entries for account at all (Harmless) + auto account = previous->account (); + auto info = ledger.account_info (transaction, account); + debug_assert (info); + result = info->head != block_a.hashables.previous ? nano::block_status::fork : nano::block_status::progress; // If we have the block but it's not the latest we have a signed fork (Malicious) if (result == nano::block_status::progress) { result = validate_message (account, hash, block_a.signature) ? nano::block_status::bad_signature : nano::block_status::progress; // Is the signature valid (Malformed) @@ -572,8 +574,6 @@ void ledger_processor::receive_block (nano::receive_block & block_a) result = ledger.block_or_pruned_exists (transaction, block_a.hashables.source) ? nano::block_status::progress : nano::block_status::gap_source; // Have we seen the source block already? (Harmless) if (result == nano::block_status::progress) { - auto info = ledger.account_info (transaction, account); - debug_assert (info); result = info->head == block_a.hashables.previous ? nano::block_status::progress : nano::block_status::gap_previous; // Block doesn't immediately follow latest block (Harmless) if (result == nano::block_status::progress) { @@ -613,10 +613,6 @@ void ledger_processor::receive_block (nano::receive_block & block_a) } } } - else - { - result = ledger.store.block.exists (transaction, block_a.hashables.previous) ? nano::block_status::fork : nano::block_status::gap_previous; // If we have the block but it's not the latest we have a signed fork (Malicious) - } } } } From ca0e2268ce6464296684da8c61e89eeeecd60af6 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Tue, 12 Mar 2024 12:25:28 +0000 Subject: [PATCH 011/128] Simplify the nano::ledger::successor function to make it more obvious. --- nano/secure/ledger.cpp | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 9472af329..22c9ab526 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -1247,36 +1247,22 @@ void nano::ledger::update_account (store::write_transaction const & transaction_ std::shared_ptr nano::ledger::successor (store::transaction const & transaction_a, nano::qualified_root const & root_a) { - nano::block_hash successor (0); - auto get_from_previous = false; - if (root_a.previous ().is_zero ()) + if (!root_a.previous ().is_zero ()) + { + return block (transaction_a, store.block.successor (transaction_a, root_a.previous ())); + } + else { auto info = account_info (transaction_a, root_a.root ().as_account ()); if (info) { - successor = info->open_block; + return block (transaction_a, info->open_block); } else { - get_from_previous = true; + return nullptr; } } - else - { - get_from_previous = true; - } - - if (get_from_previous) - { - successor = store.block.successor (transaction_a, root_a.previous ()); - } - std::shared_ptr result; - if (!successor.is_zero ()) - { - result = block (transaction_a, successor); - } - debug_assert (successor.is_zero () || result != nullptr); - return result; } std::shared_ptr nano::ledger::forked_block (store::transaction const & transaction_a, nano::block const & block_a) From 2f35b00ca1a5fbd21dc48ff70f84a1eaf21ef925 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Wed, 13 Mar 2024 12:21:53 +0000 Subject: [PATCH 012/128] Modifying ledger::successor to return std::optional so callsites don't necessarily need to load the block. --- nano/core_test/ledger.cpp | 10 +++++----- nano/node/blockprocessor.cpp | 3 ++- nano/secure/ledger.cpp | 13 +++++++++---- nano/secure/ledger.hpp | 2 +- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 01c844728..2e285ea76 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -1175,9 +1175,9 @@ TEST (ledger, successor) node1.work_generate_blocking (*send1); auto transaction (node1.store.tx_begin_write ()); ASSERT_EQ (nano::block_status::progress, node1.ledger.process (transaction, send1)); - ASSERT_EQ (*send1, *node1.ledger.successor (transaction, nano::qualified_root (nano::root (0), nano::dev::genesis->hash ()))); - ASSERT_EQ (*nano::dev::genesis, *node1.ledger.successor (transaction, nano::dev::genesis->qualified_root ())); - ASSERT_EQ (nullptr, node1.ledger.successor (transaction, nano::qualified_root (0))); + ASSERT_EQ (*send1, *node1.ledger.block (transaction, node1.ledger.successor (transaction, nano::qualified_root (nano::root (0), nano::dev::genesis->hash ())).value ())); + ASSERT_EQ (*nano::dev::genesis, *node1.ledger.block (transaction, node1.ledger.successor (transaction, nano::dev::genesis->qualified_root ()).value ())); + ASSERT_FALSE (node1.ledger.successor (transaction, nano::qualified_root (0))); } TEST (ledger, fail_change_old) @@ -4006,8 +4006,8 @@ TEST (ledger, successor_epoch) ASSERT_EQ (nano::block_status::progress, node1.ledger.process (transaction, change)); ASSERT_EQ (nano::block_status::progress, node1.ledger.process (transaction, send2)); ASSERT_EQ (nano::block_status::progress, node1.ledger.process (transaction, epoch_open)); - ASSERT_EQ (*change, *node1.ledger.successor (transaction, change->qualified_root ())); - ASSERT_EQ (*epoch_open, *node1.ledger.successor (transaction, epoch_open->qualified_root ())); + ASSERT_EQ (*change, *node1.ledger.block (transaction, node1.ledger.successor (transaction, change->qualified_root ()).value ())); + ASSERT_EQ (*epoch_open, *node1.ledger.block (transaction, node1.ledger.successor (transaction, epoch_open->qualified_root ()).value ())); ASSERT_EQ (nano::epoch::epoch_1, epoch_open->sideband ().details.epoch); ASSERT_EQ (nano::epoch::epoch_0, epoch_open->sideband ().source_epoch); // Not used for epoch state blocks } diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index 3bb23c8ee..978f4f62f 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -155,7 +155,8 @@ void nano::block_processor::force (std::shared_ptr const & block_a) void nano::block_processor::rollback_competitor (store::write_transaction const & transaction, nano::block const & block) { auto hash = block.hash (); - auto successor = node.ledger.successor (transaction, block.qualified_root ()); + auto successor_hash = node.ledger.successor (transaction, block.qualified_root ()); + auto successor = successor_hash ? node.ledger.block (transaction, successor_hash.value ()) : nullptr; if (successor != nullptr && successor->hash () != hash) { // Replace our block with the winner and roll back any dependent blocks diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 22c9ab526..0e1c4edd5 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -1245,22 +1245,27 @@ void nano::ledger::update_account (store::write_transaction const & transaction_ } } -std::shared_ptr nano::ledger::successor (store::transaction const & transaction_a, nano::qualified_root const & root_a) +std::optional nano::ledger::successor (store::transaction const & transaction_a, nano::qualified_root const & root_a) const noexcept { if (!root_a.previous ().is_zero ()) { - return block (transaction_a, store.block.successor (transaction_a, root_a.previous ())); + auto result = store.block.successor (transaction_a, root_a.previous ()); + if (result.is_zero ()) + { + return std::nullopt; + } + return result; } else { auto info = account_info (transaction_a, root_a.root ().as_account ()); if (info) { - return block (transaction_a, info->open_block); + return info.value ().open_block; } else { - return nullptr; + return std::nullopt; } } } diff --git a/nano/secure/ledger.hpp b/nano/secure/ledger.hpp index cf6e6515b..7149bdfb6 100644 --- a/nano/secure/ledger.hpp +++ b/nano/secure/ledger.hpp @@ -51,7 +51,7 @@ public: 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); nano::uint128_t weight (nano::account const &); - std::shared_ptr successor (store::transaction const &, nano::qualified_root const &); + std::optional successor (store::transaction const &, nano::qualified_root const &) const noexcept; 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; From 83853d832bd777e6e118778f4bd8bebdff0e3bc0 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Wed, 13 Mar 2024 13:19:25 +0000 Subject: [PATCH 013/128] Modify direct access to store.block.successor to use ledger::successor Adds ledger::successor convenience functions taking nano::block_hash and expands them to nano::qualified_root so both block successor and account open blocks are searched. --- nano/core_test/ledger.cpp | 4 ++-- nano/nano_node/entry.cpp | 2 +- nano/node/bootstrap/bootstrap_bulk_pull.cpp | 2 +- nano/node/json_handler.cpp | 6 +++--- nano/node/request_aggregator.cpp | 18 ++++-------------- nano/node/scheduler/priority.cpp | 2 +- nano/qt/qt.cpp | 8 ++++---- nano/secure/ledger.cpp | 12 +++++++++++- nano/secure/ledger.hpp | 1 + 9 files changed, 28 insertions(+), 27 deletions(-) diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 2e285ea76..77a19cae4 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -256,7 +256,7 @@ TEST (ledger, process_receive) ASSERT_EQ (nano::dev::constants.genesis_amount - 25, ledger.account_balance (transaction, key2.pub)); ASSERT_EQ (nano::dev::constants.genesis_amount - 25, ledger.weight (key3.pub)); ASSERT_FALSE (ledger.rollback (transaction, hash4)); - ASSERT_TRUE (store.block.successor (transaction, hash2).is_zero ()); + ASSERT_FALSE (ledger.successor (transaction, hash2)); ASSERT_EQ (25, ledger.account_balance (transaction, nano::dev::genesis_key.pub)); ASSERT_EQ (25, ledger.account_receivable (transaction, key2.pub)); ASSERT_EQ (nano::dev::constants.genesis_amount - 50, ledger.account_balance (transaction, key2.pub)); @@ -3139,7 +3139,7 @@ TEST (ledger, state_rollback_send) ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.account_balance (transaction, nano::dev::genesis_key.pub)); ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub)); ASSERT_FALSE (store.pending.exists (transaction, nano::pending_key (nano::dev::genesis_key.pub, send1->hash ()))); - ASSERT_TRUE (store.block.successor (transaction, nano::dev::genesis->hash ()).is_zero ()); + ASSERT_FALSE (ledger.successor (transaction, nano::dev::genesis->hash ())); ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); } diff --git a/nano/nano_node/entry.cpp b/nano/nano_node/entry.cpp index 97268e884..0b3c67a56 100644 --- a/nano/nano_node/entry.cpp +++ b/nano/nano_node/entry.cpp @@ -1588,7 +1588,7 @@ int main (int argc, char * const * argv) calculated_representative = block->representative_field ().value (); } // Retrieving successor block hash - hash = node->store.block.successor (transaction, hash); + hash = node->ledger.successor (transaction, hash).value_or (0); // Retrieving block data if (!hash.is_zero ()) { diff --git a/nano/node/bootstrap/bootstrap_bulk_pull.cpp b/nano/node/bootstrap/bootstrap_bulk_pull.cpp index a1fe5b8ec..c5ca04b28 100644 --- a/nano/node/bootstrap/bootstrap_bulk_pull.cpp +++ b/nano/node/bootstrap/bootstrap_bulk_pull.cpp @@ -376,7 +376,7 @@ void nano::bulk_pull_server::set_current_end () { node->logger.debug (nano::log::type::bulk_pull_server, "Bulk pull request for block hash: {}", request->start.to_string ()); - current = ascending () ? node->store.block.successor (transaction, request->start.as_block_hash ()) : request->start.as_block_hash (); + current = ascending () ? node->ledger.successor (transaction, request->start.as_block_hash ()).value_or (0) : request->start.as_block_hash (); include_start = true; } else diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 27e7c92f1..78eb9502f 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -1956,7 +1956,7 @@ void nano::json_handler::chain (bool successors) entry.put ("", hash.to_string ()); blocks.push_back (std::make_pair ("", entry)); } - hash = successors ? node.store.block.successor (transaction, hash) : block_l->previous (); + hash = successors ? node.ledger.successor (transaction, hash).value_or (0) : block_l->previous (); } else { @@ -2665,7 +2665,7 @@ void nano::json_handler::account_history () --count; } } - hash = reverse ? node.store.block.successor (transaction, hash) : block->previous (); + hash = reverse ? node.ledger.successor (transaction, hash).value_or (0) : block->previous (); block = node.ledger.block (transaction, hash); } response_l.add_child ("history", history); @@ -3698,7 +3698,7 @@ void nano::json_handler::republish () } } } - hash = node.store.block.successor (transaction, hash); + hash = node.ledger.successor (transaction, hash).value_or (0); } node.network.flood_block_many (std::move (republish_bundle), nullptr, 25); response_l.put ("success", ""); // obsolete diff --git a/nano/node/request_aggregator.cpp b/nano/node/request_aggregator.cpp index 72a5e3e40..32ad38d61 100644 --- a/nano/node/request_aggregator.cpp +++ b/nano/node/request_aggregator.cpp @@ -246,24 +246,14 @@ std::pair>, std::vectoropen_block; - } - } - if (!successor.is_zero ()) - { - auto successor_block = ledger.block (transaction, successor); + auto successor_block = ledger.block (transaction, successor.value ()); debug_assert (successor_block != nullptr); block = std::move (successor_block); // 5. Votes in cache for successor - auto find_successor_votes (local_votes.votes (root, successor)); + auto find_successor_votes (local_votes.votes (root, successor.value ())); if (!find_successor_votes.empty ()) { cached_votes.insert (cached_votes.end (), find_successor_votes.begin (), find_successor_votes.end ()); diff --git a/nano/node/scheduler/priority.cpp b/nano/node/scheduler/priority.cpp index 3fbaafda3..f8e03ff39 100644 --- a/nano/node/scheduler/priority.cpp +++ b/nano/node/scheduler/priority.cpp @@ -48,7 +48,7 @@ bool nano::scheduler::priority::activate (nano::account const & account_a, store if (conf_info.height < info->block_count) { debug_assert (conf_info.frontier != info->head); - auto hash = conf_info.height == 0 ? info->open_block : node.store.block.successor (transaction, conf_info.frontier); + auto hash = conf_info.height == 0 ? info->open_block : node.ledger.successor (transaction, conf_info.frontier).value_or (0); auto block = node.ledger.block (transaction, hash); debug_assert (block != nullptr); if (node.ledger.dependents_confirmed (transaction, *block)) diff --git a/nano/qt/qt.cpp b/nano/qt/qt.cpp index 2b07123e2..78dcaf88e 100644 --- a/nano/qt/qt.cpp +++ b/nano/qt/qt.cpp @@ -693,7 +693,7 @@ nano_qt::block_viewer::block_viewer (nano_qt::wallet & wallet_a) : std::string contents; block_l->serialize_json (contents); block->setPlainText (contents.c_str ()); - auto successor_l (this->wallet.node.store.block.successor (transaction, hash_l)); + auto successor_l = this->wallet.node.ledger.successor (transaction, hash_l).value_or (0); successor->setText (successor_l.to_string ().c_str ()); } else @@ -737,13 +737,13 @@ void nano_qt::block_viewer::rebroadcast_action (nano::block_hash const & hash_a) if (block != nullptr) { wallet.node.network.flood_block (block); - auto successor (wallet.node.store.block.successor (transaction, hash_a)); - if (!successor.is_zero ()) + auto successor = wallet.node.ledger.successor (transaction, hash_a); + if (successor) { done = false; wallet.node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (1), [this, successor] () { this->wallet.application.postEvent (&this->wallet.processor, new eventloop_event ([this, successor] () { - rebroadcast_action (successor); + rebroadcast_action (successor.value ()); })); }); } diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 0e1c4edd5..9ce5f4ece 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -1270,12 +1270,22 @@ std::optional nano::ledger::successor (store::transaction cons } } +std::optional nano::ledger::successor (store::transaction const & transaction, nano::block_hash const & hash) const noexcept +{ + return successor (transaction, { hash, hash }); +} + std::shared_ptr nano::ledger::forked_block (store::transaction const & transaction_a, nano::block const & block_a) { debug_assert (!block_exists (transaction_a, block_a.hash ())); auto root (block_a.root ()); debug_assert (block_exists (transaction_a, root.as_block_hash ()) || store.account.exists (transaction_a, root.as_account ())); - auto result = block (transaction_a, store.block.successor (transaction_a, root.as_block_hash ())); + std::shared_ptr result; + auto successor_l = successor (transaction_a, root.as_block_hash ()); + if (successor_l) + { + result = block (transaction_a, successor_l.value ()); + } if (result == nullptr) { auto info = account_info (transaction_a, root.as_account ()); diff --git a/nano/secure/ledger.hpp b/nano/secure/ledger.hpp index 7149bdfb6..f5009b664 100644 --- a/nano/secure/ledger.hpp +++ b/nano/secure/ledger.hpp @@ -52,6 +52,7 @@ public: nano::uint128_t account_receivable (store::transaction const &, nano::account const &, bool = false); 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; 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; From 5681836f37bc7482826fc7bf5274dce77a9361e4 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Wed, 13 Mar 2024 12:59:47 +0000 Subject: [PATCH 014/128] Change store::block::successor to return an std::optional to signal if there is a successor block. --- nano/secure/ledger.cpp | 7 +------ nano/store/block.hpp | 3 ++- nano/store/lmdb/block.cpp | 6 +++++- nano/store/lmdb/block.hpp | 2 +- nano/store/rocksdb/block.cpp | 6 +++++- nano/store/rocksdb/block.hpp | 2 +- 6 files changed, 15 insertions(+), 11 deletions(-) diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 9ce5f4ece..8b412717b 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -1249,12 +1249,7 @@ std::optional nano::ledger::successor (store::transaction cons { if (!root_a.previous ().is_zero ()) { - auto result = store.block.successor (transaction_a, root_a.previous ()); - if (result.is_zero ()) - { - return std::nullopt; - } - return result; + return store.block.successor (transaction_a, root_a.previous ()); } else { diff --git a/nano/store/block.hpp b/nano/store/block.hpp index ad1fd5591..91b60babc 100644 --- a/nano/store/block.hpp +++ b/nano/store/block.hpp @@ -6,6 +6,7 @@ #include #include +#include namespace nano { @@ -28,7 +29,7 @@ class block public: virtual void put (store::write_transaction const &, nano::block_hash const &, nano::block const &) = 0; virtual void raw_put (store::write_transaction const &, std::vector const &, nano::block_hash const &) = 0; - virtual nano::block_hash successor (store::transaction const &, nano::block_hash const &) const = 0; + virtual std::optional successor (store::transaction const &, nano::block_hash const &) const = 0; virtual void successor_clear (store::write_transaction const &, nano::block_hash const &) = 0; virtual std::shared_ptr get (store::transaction const &, nano::block_hash const &) const = 0; virtual std::shared_ptr random (store::transaction const &) = 0; diff --git a/nano/store/lmdb/block.cpp b/nano/store/lmdb/block.cpp index 38e695ba6..9d3e308ab 100644 --- a/nano/store/lmdb/block.cpp +++ b/nano/store/lmdb/block.cpp @@ -49,7 +49,7 @@ void nano::store::lmdb::block::raw_put (store::write_transaction const & transac store.release_assert_success (status); } -nano::block_hash nano::store::lmdb::block::successor (store::transaction const & transaction_a, nano::block_hash const & hash_a) const +std::optional nano::store::lmdb::block::successor (store::transaction const & transaction_a, nano::block_hash const & hash_a) const { nano::store::lmdb::db_val value; block_raw_get (transaction_a, hash_a, value); @@ -67,6 +67,10 @@ nano::block_hash nano::store::lmdb::block::successor (store::transaction const & { result.clear (); } + if (result.is_zero ()) + { + return std::nullopt; + } return result; } diff --git a/nano/store/lmdb/block.hpp b/nano/store/lmdb/block.hpp index b9ccc3b47..575803d83 100644 --- a/nano/store/lmdb/block.hpp +++ b/nano/store/lmdb/block.hpp @@ -24,7 +24,7 @@ public: explicit block (nano::store::lmdb::component & store_a); void put (store::write_transaction const & transaction_a, nano::block_hash const & hash_a, nano::block const & block_a) override; void raw_put (store::write_transaction const & transaction_a, std::vector const & data, nano::block_hash const & hash_a) override; - nano::block_hash successor (store::transaction const & transaction_a, nano::block_hash const & hash_a) const override; + std::optional successor (store::transaction const & transaction_a, nano::block_hash const & hash_a) const override; void successor_clear (store::write_transaction const & transaction_a, nano::block_hash const & hash_a) override; std::shared_ptr get (store::transaction const & transaction_a, nano::block_hash const & hash_a) const override; std::shared_ptr random (store::transaction const & transaction_a) override; diff --git a/nano/store/rocksdb/block.cpp b/nano/store/rocksdb/block.cpp index a910e0cc7..10ca4067e 100644 --- a/nano/store/rocksdb/block.cpp +++ b/nano/store/rocksdb/block.cpp @@ -49,7 +49,7 @@ void nano::store::rocksdb::block::raw_put (store::write_transaction const & tran store.release_assert_success (status); } -nano::block_hash nano::store::rocksdb::block::successor (store::transaction const & transaction_a, nano::block_hash const & hash_a) const +std::optional nano::store::rocksdb::block::successor (store::transaction const & transaction_a, nano::block_hash const & hash_a) const { nano::store::rocksdb::db_val value; block_raw_get (transaction_a, hash_a, value); @@ -67,6 +67,10 @@ nano::block_hash nano::store::rocksdb::block::successor (store::transaction cons { result.clear (); } + if (result.is_zero ()) + { + return std::nullopt; + } return result; } diff --git a/nano/store/rocksdb/block.hpp b/nano/store/rocksdb/block.hpp index 6ecf26fd6..7df264a2f 100644 --- a/nano/store/rocksdb/block.hpp +++ b/nano/store/rocksdb/block.hpp @@ -22,7 +22,7 @@ public: explicit block (nano::store::rocksdb::component & store_a); void put (store::write_transaction const & transaction_a, nano::block_hash const & hash_a, nano::block const & block_a) override; void raw_put (store::write_transaction const & transaction_a, std::vector const & data, nano::block_hash const & hash_a) override; - nano::block_hash successor (store::transaction const & transaction_a, nano::block_hash const & hash_a) const override; + std::optional successor (store::transaction const & transaction_a, nano::block_hash const & hash_a) const override; void successor_clear (store::write_transaction const & transaction_a, nano::block_hash const & hash_a) override; std::shared_ptr get (store::transaction const & transaction_a, nano::block_hash const & hash_a) const override; std::shared_ptr random (store::transaction const & transaction_a) override; From 77a3512d18b5b303f0a4063f1bb569149db3bf2c Mon Sep 17 00:00:00 2001 From: gr0vity-dev <85646666+gr0vity-dev@users.noreply.github.com> Date: Fri, 15 Mar 2024 11:39:25 +0100 Subject: [PATCH 015/128] Reintroduce RC builds for releases branches (#4381) * Extend tag generation functionality - Add `IS_RELEASE_BUILD` env variable to indicate release builds - Set default suffix to RC for releases/v branches - Set default suffix to DB for develop branch - Set default suffix to `branch_name` for all other branches - Add -s flag for manual suffix - Make sure tags have the right min_version and pre_release_version in their CMakeLists.txt - Make sure RC builds have the right min_version - Write variables to GITHUB_ENV if run from workflow - improve variable names * Reintroduce RC builds - `is_release_build` is ignored except if the workflow executes on a `releases/v` branch - `is_release_build` is false by default and creates RC builds that increment when a new commit is detected - simplify workflow by using environment variables * rename artifacts_build_deploy to build_deploy * fix: use lowercase output --------- Co-authored-by: homebot Co-authored-by: gr0vity --- ...acts_build_deploy.yml => build_deploy.yml} | 62 ++-- ci/actions/generate_next_git_tag.sh | 332 +++++++++--------- 2 files changed, 202 insertions(+), 192 deletions(-) rename .github/workflows/{artifacts_build_deploy.yml => build_deploy.yml} (78%) diff --git a/.github/workflows/artifacts_build_deploy.yml b/.github/workflows/build_deploy.yml similarity index 78% rename from .github/workflows/artifacts_build_deploy.yml rename to .github/workflows/build_deploy.yml index 4f67d50e6..b9a25f868 100644 --- a/.github/workflows/artifacts_build_deploy.yml +++ b/.github/workflows/build_deploy.yml @@ -3,6 +3,11 @@ on: schedule: - cron: "0 0 * * 3,6" workflow_dispatch: + inputs: + is_release_build: + description: 'Is this a release build?' + required: false + default: 'false' env: artifact: 1 @@ -11,28 +16,27 @@ jobs: prepare_build: runs-on: ubuntu-22.04 outputs: - CI_TAG: ${{ steps.tag_set.outputs.CI_TAG }} - TAG_CREATED: ${{ steps.tag_set.outputs.tag_created }} + ci_tag: ${{ steps.set_vars.outputs.ci_tag }} + tag_created: ${{ steps.set_vars.outputs.tag_created }} steps: - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 #v3.1.0 - with: - ref: ${{ github.ref }} - name: Set the tag and version id: tag_set run: | - output_var_file="variable_list.txt" - ci/actions/generate_next_git_tag.sh -c -o "${output_var_file}" - CI_TAG=$(grep 'build_tag' ${output_var_file} | cut -d= -f2) - echo "CI_TAG=${CI_TAG}" >> $GITHUB_OUTPUT - TAG_CREATED=$(grep 'tag_created' ${output_var_file} | cut -d= -f2) - echo "TAG_CREATED=${TAG_CREATED}" >> $GITHUB_OUTPUT + ci/actions/generate_next_git_tag.sh -c env: GITHUB_ACTOR: ${{ github.actor }} + IS_RELEASE_BUILD: ${{ github.event.inputs.is_release_build || 'false' }} + - name: Set output + id: set_vars + run: | + echo "ci_tag=$CI_TAG" >> $GITHUB_OUTPUT + echo "tag_created=$TAG_CREATED" >> $GITHUB_OUTPUT osx_job: needs: prepare_build - if: ${{ needs.prepare_build.outputs.TAG_CREATED == 'true' }} + if: ${{ needs.prepare_build.outputs.tag_created == 'true' }} runs-on: macOS-14 timeout-minutes: 90 strategy: @@ -43,7 +47,7 @@ jobs: - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 #v3.1.0 with: submodules: "recursive" - ref: ${{ needs.prepare_build.outputs.CI_TAG }} + ref: ${{ needs.prepare_build.outputs.ci_tag }} repository: ${{ github.repository }} - name: Prepare run: ci/prepare/macos/prepare.sh @@ -51,12 +55,12 @@ jobs: run: ci/build-deploy.sh "/tmp/qt/lib/cmake/Qt5"; env: NETWORK: ${{ matrix.network }} - CI_TAG: ${{ needs.prepare_build.outputs.CI_TAG }} + CI_TAG: ${{ needs.prepare_build.outputs.ci_tag }} - name: Deploy Artifact run: ci/actions/deploy.sh env: NETWORK: ${{ matrix.network }} - TAG: ${{ needs.prepare_build.outputs.CI_TAG }} + TAG: ${{ needs.prepare_build.outputs.ci_tag }} S3_BUCKET_NAME: ${{ vars.S3_BUCKET_NAME }} S3_BUILD_DIRECTORY: ${{ vars.S3_BUILD_DIRECTORY }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} @@ -66,7 +70,7 @@ jobs: linux_job: needs: prepare_build - if: ${{ needs.prepare_build.outputs.TAG_CREATED == 'true' }} + if: ${{ needs.prepare_build.outputs.tag_created == 'true' }} runs-on: ubuntu-22.04 timeout-minutes: 90 strategy: @@ -76,7 +80,7 @@ jobs: - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 #v3.1.0 with: submodules: "recursive" - ref: ${{ needs.prepare_build.outputs.CI_TAG }} + ref: ${{ needs.prepare_build.outputs.ci_tag }} repository: ${{ github.repository }} - name: Prepare run: sudo -E ci/prepare/linux/prepare.sh @@ -84,13 +88,13 @@ jobs: run: ci/build-deploy.sh "/usr/lib/x86_64-linux-gnu/cmake/Qt5" env: NETWORK: ${{ matrix.network }} - CI_TAG: ${{ needs.prepare_build.outputs.CI_TAG }} + CI_TAG: ${{ needs.prepare_build.outputs.ci_tag }} - name: Deploy Artifact run: ci/actions/deploy.sh env: NETWORK: ${{ matrix.network }} - TAG: ${{ needs.prepare_build.outputs.CI_TAG }} + TAG: ${{ needs.prepare_build.outputs.ci_tag }} S3_BUCKET_NAME: ${{ vars.S3_BUCKET_NAME }} S3_BUILD_DIRECTORY: ${{ vars.S3_BUILD_DIRECTORY }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} @@ -100,7 +104,7 @@ jobs: linux_rpm_job: needs: prepare_build - if: ${{ needs.prepare_build.outputs.TAG_CREATED == 'true' }} + if: ${{ needs.prepare_build.outputs.tag_created == 'true' }} runs-on: ubuntu-22.04 timeout-minutes: 90 strategy: @@ -125,7 +129,7 @@ jobs: docker run -v ${GITHUB_WORKSPACE}:/workspace -v ${GITHUB_WORKSPACE}/artifacts:/root/rpmbuild \ local/nano-env:rhel /bin/bash -c " \ NETWORK=${{ matrix.network }} \ - TAG=${{ needs.prepare_build.outputs.CI_TAG }} \ + TAG=${{ needs.prepare_build.outputs.ci_tag }} \ REPO_TO_BUILD=${{ github.repository }} \ RPM_RELEASE=1 \ ci/build-rhel.sh" @@ -135,7 +139,7 @@ jobs: env: LINUX_RPM: 1 NETWORK: ${{ matrix.network }} - # TAG: ${{ needs.prepare_build.outputs.CI_TAG }} # (not used in the deploy script if LINUX_RPM==1 ) + # TAG: ${{ needs.prepare_build.outputs.ci_tag }} # (not used in the deploy script if LINUX_RPM==1 ) S3_BUCKET_NAME: ${{ vars.S3_BUCKET_NAME }} S3_BUILD_DIRECTORY: ${{ vars.S3_BUILD_DIRECTORY }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} @@ -145,7 +149,7 @@ jobs: linux_docker_job: needs: prepare_build - if: ${{ needs.prepare_build.outputs.TAG_CREATED == 'true' }} + if: ${{ needs.prepare_build.outputs.tag_created == 'true' }} runs-on: ubuntu-22.04 timeout-minutes: 90 strategy: @@ -155,13 +159,13 @@ jobs: - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 #v3.1.0 with: submodules: "recursive" - ref: ${{ needs.prepare_build.outputs.CI_TAG }} + ref: ${{ needs.prepare_build.outputs.ci_tag }} repository: ${{ github.repository }} - name: Build Docker run: ci/actions/linux/docker-build.sh env: NETWORK: ${{ matrix.network }} - CI_TAG: ${{ needs.prepare_build.outputs.CI_TAG }} + CI_TAG: ${{ needs.prepare_build.outputs.ci_tag }} DOCKER_REGISTRY: ${{ vars.DOCKER_REGISTRY }} - name: Check if secrets.DOCKER_PASSWORD exists run: echo "DOCKER_PASSWORD_EXISTS=${{ secrets.DOCKER_PASSWORD != '' }}" >> $GITHUB_ENV @@ -169,7 +173,7 @@ jobs: if: env.DOCKER_PASSWORD_EXISTS == 'true' run: ci/actions/linux/docker-deploy.sh env: - CI_TAG: ${{ needs.prepare_build.outputs.CI_TAG }} + CI_TAG: ${{ needs.prepare_build.outputs.ci_tag }} NETWORK: ${{ matrix.network }} DOCKER_REGISTRY: ${{ vars.DOCKER_REGISTRY }} DOCKER_USER: ${{ vars.DOCKER_USER }} @@ -177,7 +181,7 @@ jobs: - name: Deploy Docker (ghcr.io) run: ci/actions/linux/ghcr-deploy.sh env: - CI_TAG: ${{ needs.prepare_build.outputs.CI_TAG }} + CI_TAG: ${{ needs.prepare_build.outputs.ci_tag }} NETWORK: ${{ matrix.network }} DOCKER_REGISTRY: ghcr.io DOCKER_USER: ${{ github.repository_owner }} @@ -186,7 +190,7 @@ jobs: windows_job: needs: prepare_build - if: ${{ needs.prepare_build.outputs.TAG_CREATED == 'true' }} + if: ${{ needs.prepare_build.outputs.tag_created == 'true' }} runs-on: windows-latest timeout-minutes: 90 strategy: @@ -196,7 +200,7 @@ jobs: - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 #v3.1.0 with: submodules: "recursive" - ref: ${{ needs.prepare_build.outputs.CI_TAG }} + ref: ${{ needs.prepare_build.outputs.ci_tag }} repository: ${{ github.repository }} - name: Prepare run: ci/prepare/windows/prepare.ps1 @@ -209,7 +213,7 @@ jobs: run: ci/actions/windows/deploy.ps1 env: NETWORK: ${{ matrix.network }} - TAG: ${{ needs.prepare_build.outputs.CI_TAG }} + TAG: ${{ needs.prepare_build.outputs.ci_tag }} S3_BUCKET_NAME: ${{ vars.S3_BUCKET_NAME }} S3_BUILD_DIRECTORY: ${{ vars.S3_BUILD_DIRECTORY }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} diff --git a/ci/actions/generate_next_git_tag.sh b/ci/actions/generate_next_git_tag.sh index 436e4dcbc..a68f49bb9 100755 --- a/ci/actions/generate_next_git_tag.sh +++ b/ci/actions/generate_next_git_tag.sh @@ -1,220 +1,224 @@ #!/bin/bash -# This script creates the next tag for the current branch by incrementing the version_pre_release by 1 -# A new tag is only created if a new commit has been detected compared to the previous tag. -# The tag has the following format V${current_version_major}.${current_version_minor}${branch_name} -# ${branch_name} is converted to "DB" if the script operates on develop branch. (e.g first tag for V26: V26.0DB1) -# if -c flag is provided, version_pre_release in CMakeLists.txt is incremented and a new tag is created and pushed to origin -# if -o is provided, "build_tag" , "version_pre_release" and "tag_created" are written to file -# If it executes on a release-branch : -# --> if there is no new commit, the same tag is generated again -# --> If there is a new commit compared to the previous tag, we would increment the minor version by 1 and build the new binaries & docker images +# Script Description -#!/bin/bash +# Purpose: +# This script generates a new Git tag based on the current branch and previously generated tags. +# It creates a new tag only if there's a new commit compared to the previous tag. + +# Tag Format: +# General: V{MAJOR}.{MINOR}{tag_suffix}{increment} +# For releases/v branch: V{MAJOR}.{MINOR} + +# Options: +# $IS_RELEASE_BUILD : Indicates a release build. In this case, {tag_suffix} is ignored. +# New commit: Increments the {MINOR} version. +# -s {tag_suffix} : overwrites tag_suffix derived from the branch name. Derived tag_suffixes are : +# DB for develop branch (e.g. V26.0DB1) +# RC for releases/v branch (e.g. V26.0RC1) +# {branch_name} for other branches (e.g. V26.0current_git_branch1) +# -c : Create and push the tag to origin. +# -o {output} : Write results to the specified output file. set -e -set -x +set -x output="" -create=false -tag_created="false" +push_tag=false +is_release_build=${IS_RELEASE_BUILD:-false} +tag_suffix="" -while getopts ":o:c" opt; do - case ${opt} in - o ) - output=$OPTARG - ;; - c ) - create=true - ;; - \? ) - echo "Invalid Option: -$OPTARG" 1>&2 - exit 1 - ;; - : ) - echo "Invalid Option: -$OPTARG requires an argument" 1>&2 - exit 1 - ;; - esac +while getopts ":o:cs:" opt; do + case ${opt} in + o) + output=$OPTARG + ;; + c) + push_tag=true + ;; + s) + tag_suffix=$OPTARG + ;; + \?) + echo "Invalid Option: -$OPTARG" 1>&2 + exit 1 + ;; + :) + echo "Invalid Option: -$OPTARG requires an argument" 1>&2 + exit 1 + ;; + esac done -shift $((OPTIND -1)) +shift $((OPTIND - 1)) +is_release_build() { + [[ $is_release_branch == true && $is_release_build == true ]] +} + +is_release_branch_and_release_tag_exists() { + [[ $is_release_branch == true && $exists_tag_current_release == true ]] +} get_tag_suffix() { - local branch_name=$1 - local version_major=$2 - local tag_suffix=${branch_name//[^a-zA-Z0-9]/_} + local existing_suffix=$1 + local branch_name=$2 - if [[ "$branch_name" == "develop" ]]; then - tag_suffix="DB" + # If tag_suffix is already provided, return it + if [[ -n "$existing_suffix" ]]; then + echo "$existing_suffix" + return fi - echo $tag_suffix + # Replace non-alphanumeric characters with underscores + local new_tag_suffix=${branch_name//[^a-zA-Z0-9]/_} + + # Specific rules for certain branch names + if [[ "$branch_name" == "develop" ]]; then + new_tag_suffix="DB" + elif [[ "$branch_name" =~ ^releases/v[0-9]+ ]]; then + new_tag_suffix="RC" + fi + echo $new_tag_suffix } -get_next_tag_number() { - local last_tag_number=$1 - local increment=$2 - echo $((last_tag_number + increment)) -} - -get_next_minor_version() { - local current_minor=$1 - local increment=$2 - echo $((current_minor + increment)) -} - -get_new_release_tag() { - local version_major=$1 - local next_minor=$2 - echo "V${version_major}.${next_minor}" -} - -get_new_other_tag() { - local base_version=$1 - local next_tag_number=$2 - echo "${base_version}${next_tag_number}" -} - -update_output_file() { +update_output() { + #Responsible for either writing to file (-o flag) or to $GITHUB_ENV (when run from a workflow) local new_tag=$1 - local next_number=$2 - local tag_created=$3 - local tag_type=$4 + local tag_created=$2 if [[ -n "$output" ]]; then - echo "build_tag =$new_tag" > $output - echo "$tag_type =$next_number" >> $output - echo "tag_created =$tag_created" >> $output + # Output to the specified file if -o is used + echo "CI_TAG=${new_tag}" >"$output" + echo "TAG_CREATED=${tag_created}" >>"$output" + elif [[ $GITHUB_ACTIONS == 'true' ]]; then + # Set environment variables if -o is not used + echo "CI_TAG=${new_tag}" >>$GITHUB_ENV + echo "TAG_CREATED=${tag_created}" >>$GITHUB_ENV + else + echo "Not running in a GitHub Actions environment. No action taken for CI_TAG, CI_TAG_NUMBER, TAG_CREATED." fi } update_cmake_lists() { - local tag_type=$1 - local next_number=$2 + local tag_types=("$@") # Array of tag types local variable_to_update="" - if [[ "$tag_type" == "version_pre_release" ]]; then - variable_to_update="CPACK_PACKAGE_VERSION_PRE_RELEASE" - elif [[ "$tag_type" == "version_minor" ]]; then - variable_to_update="CPACK_PACKAGE_VERSION_MINOR" - fi + for tag_type in "${tag_types[@]}"; do + case "$tag_type" in + "version_pre_release") + variable_to_update="CPACK_PACKAGE_VERSION_PRE_RELEASE" + new_tag_number=${tag_next_suffix_number} + ;; + "version_minor") + variable_to_update="CPACK_PACKAGE_VERSION_MINOR" + new_tag_number=${tag_next_minor_number} + ;; + esac - if [[ -n "$variable_to_update" ]]; then - echo "Update ${variable_to_update} to $next_number" - sed -i.bak "s/set(${variable_to_update} \"[0-9]*\")/set(${variable_to_update} \"${next_number}\")/g" CMakeLists.txt - rm CMakeLists.txt.bak - fi + if [[ -n "$variable_to_update" ]]; then + echo "Update ${variable_to_update} to $new_tag_number" + sed -i.bak "s/set(${variable_to_update} \"[0-9]*\")/set(${variable_to_update} \"${new_tag_number}\")/g" CMakeLists.txt + rm CMakeLists.txt.bak + fi + done git add CMakeLists.txt } function create_commit() { git diff --cached --quiet - local has_changes=$? # store exit status of the last command + local has_changes=$? # store exit status of the last command if [[ $has_changes -eq 0 ]]; then # no changes echo "No changes to commit" echo "false" else # changes detected - git commit -m "Update CMakeLists.txt" + git commit -m "Update CMakeLists.txt" >/dev/null 2>&1 echo "true" fi } - # Fetch all existing tags git fetch --tags -f -# Fetch the last commit hash of the current branch +current_branch_name=$(git rev-parse --abbrev-ref HEAD) current_commit_hash=$(git rev-parse HEAD) - -# Fetch branch name -branch_name=$(git rev-parse --abbrev-ref HEAD) - -# Determine if it's a release branch or not -is_release_branch=$(echo "$branch_name" | grep -q "releases/v$current_version_major" && echo true || echo false) - - -# Fetch major and minor version numbers from CMakeLists.txt current_version_major=$(grep "CPACK_PACKAGE_VERSION_MAJOR" CMakeLists.txt | grep -o "[0-9]\+") current_version_minor=$(grep "CPACK_PACKAGE_VERSION_MINOR" CMakeLists.txt | grep -o "[0-9]\+") +declare -a cmake_versions_to_update -# Initialize tag suffix and next number and increment -tag_suffix="" -next_number=0 -increment=1 - -if [[ $is_release_branch == true ]]; then - - tag_type="version_minor" - # Find existing tags for the release branch - existing_release_tags=$(git tag --list "V${current_version_major}.*" | grep -E "V${current_version_major}\.[0-9]+$" || true) - - # Check if any tag exists for the release branch - if [[ -z "$existing_release_tags" ]]; then - # No tag exists yet, use current minor version without incrementing - tag_created="true" - increment=0 - else - # Some tags already exist - # Get the commit hash of the latest tag - last_tag=$(echo "$existing_release_tags" | sort -V | tail -n1) - last_tag_commit_hash=$(git rev-list -n 1 $last_tag) - - if [[ "$current_commit_hash" == "$last_tag_commit_hash" ]]; then - # The commit hash of the HEAD is the same as the last tag, hence no new commits. No need to increment - tag_created="true" - increment=0 - else - # There is a new commit, hence increment the minor version by 1 - tag_created="true" - increment=1 - fi - fi - next_number=$(get_next_minor_version $current_version_minor $increment) - new_tag=$(get_new_release_tag $current_version_major $next_number) +is_release_branch=$(echo "$current_branch_name" | grep -q "releases/v$current_version_major" && echo true || echo false) +tag_current_release="V${current_version_major}.${current_version_minor}" +exists_tag_current_release=$(git tag --list "${tag_current_release}" | grep -qE "${tag_current_release}$" && echo true || echo false) +# Determine the tag type and base version format +if is_release_build; then + cmake_versions_to_update+=("version_minor") + tag_base="${tag_current_release}" else - # Non-release branches handling - tag_type="version_pre_release" - - tag_suffix=$(get_tag_suffix $branch_name $current_version_major) - base_version="V${current_version_major}.${current_version_minor}${tag_suffix}" - existing_tags=$(git tag --list "${base_version}*" | grep -E "${base_version}[0-9]+$" || true) - - if [[ -n "$existing_tags" ]]; then - last_tag=$(echo "$existing_tags" | sort -V | tail -n1) - last_tag_number=$(echo "$last_tag" | awk -F"${tag_suffix}" '{print $2}') - last_tag_commit_hash=$(git rev-list -n 2 $last_tag | tail -n 1) #ignore the commit that updates the version_pre_release - - if [[ "$current_commit_hash" == "$last_tag_commit_hash" ]]; then - echo "No new commits since the last tag. No new tag will be created." - tag_created="false" - else - tag_created="true" - next_number=$(get_next_tag_number $last_tag_number $increment) - fi - else - tag_created="true" - next_number=1 #replace the default 99 + if is_release_branch_and_release_tag_exists; then + # Make sure RC builds have release_build_minor_version + 1 + current_version_minor=$((current_version_minor + 1)) + cmake_versions_to_update+=("version_minor") fi - new_tag=$(get_new_other_tag $base_version $next_number) + cmake_versions_to_update+=("version_pre_release") + tag_suffix=$(get_tag_suffix "$tag_suffix" "$current_branch_name") + tag_base="V${current_version_major}.${current_version_minor}${tag_suffix}" +fi +tag_next_suffix_number=1 # Will be overwritten if a previous tag exists +tag_next_minor_number=${current_version_minor} # Default value if no previous tag exists + +# Fetch existing tags based on the base version +existing_tags=$(git tag --list "${tag_base}*" | grep -E "${tag_base}[0-9]*$" || true) +should_create_tag="true" + +# Get next tag if a previous tag exists: +if [[ -n "$existing_tags" ]]; then + most_recent_tag=$(echo "$existing_tags" | sort -V | tail -n1) + + if is_release_build; then + # Increment the minor version for release builds (-r flag is set) or RC builds if the release tag exists + tag_next_minor_number=$((current_version_minor + 1)) + else + tag_next_minor_number=${current_version_minor} + fi + + # Increment the suffix number based on the existing tags + if [[ -n "$tag_suffix" && -n "$most_recent_tag" ]]; then + tag_max_suffix_number=$(echo "$most_recent_tag" | awk -F"${tag_suffix}" '{print $2}') + tag_next_suffix_number=$((tag_max_suffix_number + 1)) + fi +# Else if no previous tag matching tag_base exists, use default values set above fi -update_output_file $new_tag $next_number $tag_created $tag_type +# Check if the current commit is included in the last tag +tags_containing_current_commit=$(git tag --contains "$current_commit_hash") +if [[ -n "$most_recent_tag" ]] && echo "$tags_containing_current_commit" | grep -q "$most_recent_tag"; then + should_create_tag="false" +fi + +# Generate the new tag name +if is_release_build; then + # tag_suffix is ignored for release builds + new_tag="V${current_version_major}.${tag_next_minor_number}" +else + new_tag="${tag_base}${tag_next_suffix_number}" +fi + +update_output $new_tag $should_create_tag # Skip tag creation if no new commits -if [[ "$tag_created" == "true" ]]; then - echo "$new_tag" +if [[ "$should_create_tag" == "true" ]]; then + echo "Tag '$new_tag' ready to be created" else + echo "No new commits. Tag '$new_tag' will not be created." exit 0 fi -if [[ $create == true ]]; then +if [[ $push_tag == true ]]; then # Stash current changes git config user.name "${GITHUB_ACTOR}" git config user.email "${GITHUB_ACTOR}@users.noreply.github.com" # Update variable in CMakeLists.txt - update_cmake_lists "$tag_type" "$next_number" + update_cmake_lists "${cmake_versions_to_update[@]}" commit_made=$(create_commit) @@ -222,15 +226,17 @@ if [[ $create == true ]]; then git push origin "$new_tag" -f echo "The tag $new_tag has been created and pushed." - # If it's a release branch, also push the commit to the branch - if [[ $is_release_branch == true ]]; then - git push origin "$branch_name" -f - echo "The commit has been pushed to the $branch_name branch." - fi + # If it's a release build, also push the commit to the branch + if is_release_build; then + git push origin "$current_branch_name" -f + echo "The commit has been pushed to the $current_branch_name branch." - # Only reset local branch if a commit was made and it's not a "releases" branch. - if [[ "$commit_made" == "true" && $is_release_branch == false ]]; then + elif [[ "$commit_made" == "true" ]]; then + # Resets the last commit on non-release branches after tagging, keeping the current branch clean. git reset --hard HEAD~1 echo "The commit used for the tag does not exist on any branch." fi -fi \ No newline at end of file + +else + echo "Tag was not created. Run the script with -c option to create and push the tag" +fi From 9da00f1a7888b9075227d3f086f7996e9db65d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Tue, 12 Mar 2024 19:29:52 +0100 Subject: [PATCH 016/128] Logging time helpers --- nano/lib/logging.hpp | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/nano/lib/logging.hpp b/nano/lib/logging.hpp index 14e6e2fc3..ae9563300 100644 --- a/nano/lib/logging.hpp +++ b/nano/lib/logging.hpp @@ -32,12 +32,58 @@ using logger_id = std::pair; std::string to_string (logger_id); logger_id parse_logger_id (std::string const &); +} +// Time helpers +namespace nano::log +{ template auto microseconds (std::chrono::time_point time) { return std::chrono::duration_cast (time.time_since_epoch ()).count (); } + +template +auto microseconds (Duration duration) +{ + return std::chrono::duration_cast (duration).count (); +} + +template +auto milliseconds (std::chrono::time_point time) +{ + return std::chrono::duration_cast (time.time_since_epoch ()).count (); +} + +template +auto milliseconds (Duration duration) +{ + return std::chrono::duration_cast (duration).count (); +} + +template +auto seconds (std::chrono::time_point time) +{ + return std::chrono::duration_cast (time.time_since_epoch ()).count (); +} + +template +auto seconds (Duration duration) +{ + return std::chrono::duration_cast (duration).count (); +} + +template +auto milliseconds_delta (std::chrono::time_point time, std::chrono::time_point now = Clock::now ()) +{ + return std::chrono::duration_cast (now - time).count (); +} + +template +auto seconds_delta (std::chrono::time_point time, std::chrono::time_point now = Clock::now ()) +{ + return std::chrono::duration_cast (now - time).count (); +} } namespace nano From 99b8b5364fbfabd933f46479ef42f64992ae84d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Wed, 13 Mar 2024 15:35:13 +0100 Subject: [PATCH 017/128] Organize --- nano/node/network.cpp | 21 ++++++++++++--------- nano/node/network.hpp | 19 +++++++++++++------ nano/node/node.cpp | 4 ++-- nano/node/transport/inproc.cpp | 31 ++----------------------------- nano/node/voting.cpp | 5 +++-- nano/node/voting.hpp | 4 +++- 6 files changed, 35 insertions(+), 49 deletions(-) diff --git a/nano/node/network.cpp b/nano/node/network.cpp index 80660cad0..04e5f489f 100644 --- a/nano/node/network.cpp +++ b/nano/node/network.cpp @@ -14,20 +14,16 @@ */ nano::network::network (nano::node & node_a, uint16_t port_a) : + node (node_a), id (nano::network_constants::active_network), syn_cookies (node_a.network_params.network.max_peers_per_ip), - inbound{ [this] (nano::message const & message, std::shared_ptr const & channel) { - debug_assert (message.header.network == node.network_params.network.current_network); - debug_assert (message.header.version_using >= node.network_params.network.protocol_version_min); - process_message (message, channel); - } }, resolver (node_a.io_ctx), tcp_message_manager (node_a.config.tcp_incoming_connections_max), - node (node_a), publish_filter (256 * 1024), - tcp_channels (node_a, inbound), - port (port_a), - disconnect_observer ([] () {}) + tcp_channels (node_a, [this] (nano::message const & message, std::shared_ptr const & channel) { + inbound (message, channel); + }), + port (port_a), disconnect_observer ([] () {}) { for (std::size_t i = 0; i < node.config.network_threads && !node.flags.disable_tcp_realtime; ++i) { @@ -354,6 +350,13 @@ void nano::network::process_message (nano::message const & message, std::shared_ message.visit (visitor); } +void nano::network::inbound (const nano::message & message, const std::shared_ptr & channel) +{ + debug_assert (message.header.network == node.network_params.network.current_network); + debug_assert (message.header.version_using >= node.network_params.network.protocol_version_min); + process_message (message, channel); +} + // Send keepalives to all the peers we've been notified of void nano::network::merge_peers (std::array const & peers_a) { diff --git a/nano/node/network.hpp b/nano/node/network.hpp index cd5b90e04..f49b5f498 100644 --- a/nano/node/network.hpp +++ b/nano/node/network.hpp @@ -74,12 +74,12 @@ private: class network final { public: - network (nano::node &, uint16_t); + network (nano::node &, uint16_t port); ~network (); - nano::networks id; void start (); void stop (); + void flood_message (nano::message &, nano::transport::buffer_drop_policy const = nano::transport::buffer_drop_policy::limiter, float const = 1.0f); void flood_keepalive (float const scale_a = 1.0f); void flood_keepalive_self (float const scale_a = 0.5f); @@ -114,8 +114,6 @@ public: nano::endpoint endpoint () const; void cleanup (std::chrono::steady_clock::time_point const &); void ongoing_cleanup (); - // Node ID cookies cleanup - nano::syn_cookies syn_cookies; void ongoing_syn_cookie_cleanup (); void ongoing_keepalive (); std::size_t size () const; @@ -124,7 +122,9 @@ public: void erase (nano::transport::channel const &); /** Disconnects and adds peer to exclusion list */ void exclude (std::shared_ptr const & channel); + void inbound (nano::message const &, std::shared_ptr const &); +public: // Handshake /** Verifies that handshake response matches our query. @returns true if OK */ bool verify_handshake_response (nano::node_id_handshake::response_payload const & response, nano::endpoint const & remote_endpoint); std::optional prepare_handshake_query (nano::endpoint const & remote_endpoint); @@ -133,20 +133,27 @@ public: private: void process_message (nano::message const &, std::shared_ptr const &); +private: // Dependencies + nano::node & node; + public: - std::function const &)> inbound; + nano::networks const id; + nano::syn_cookies syn_cookies; boost::asio::ip::udp::resolver resolver; std::vector packet_processing_threads; nano::peer_exclusion excluded_peers; nano::tcp_message_manager tcp_message_manager; - nano::node & node; nano::network_filter publish_filter; nano::transport::tcp_channels tcp_channels; std::atomic port{ 0 }; std::function disconnect_observer; // Called when a new channel is observed std::function)> channel_observer; + +private: std::atomic stopped{ false }; + +public: static unsigned const broadcast_interval_ms = 10; static std::size_t const buffer_size = 512; diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 8397fa70d..b0de6bc93 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -174,8 +174,8 @@ nano::node::node (boost::asio::io_context & io_ctx_a, std::filesystem::path cons 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 }, - generator{ config, ledger, wallets, vote_processor, history, network, stats, logger, /* non-final */ false }, - final_generator{ config, ledger, wallets, vote_processor, history, network, stats, logger, /* final */ true }, + generator{ config, *this, ledger, wallets, vote_processor, history, network, stats, logger, /* non-final */ false }, + final_generator{ config, *this, ledger, wallets, vote_processor, history, network, stats, logger, /* final */ true }, active{ *this, confirmation_height_processor, block_processor }, scheduler_impl{ std::make_unique (*this) }, scheduler{ *scheduler_impl }, diff --git a/nano/node/transport/inproc.cpp b/nano/node/transport/inproc.cpp index b07bd0d35..2dfb36d12 100644 --- a/nano/node/transport/inproc.cpp +++ b/nano/node/transport/inproc.cpp @@ -25,30 +25,6 @@ bool nano::transport::inproc::channel::operator== (nano::transport::channel cons return endpoint == other_a.get_endpoint (); } -/** - * This function is called for every message received by the inproc channel. - * Note that it is called from inside the context of nano::transport::inproc::channel::send_buffer - */ -class message_visitor_inbound : public nano::message_visitor -{ -public: - message_visitor_inbound (decltype (nano::network::inbound) & inbound, std::shared_ptr channel) : - inbound{ inbound }, - channel{ channel } - { - } - - decltype (nano::network::inbound) & inbound; - - // the channel to reply to, if a reply is generated - std::shared_ptr channel; - - void default_handler (nano::message const & message) override - { - inbound (message, channel); - } -}; - /** * Send the buffer to the peer and call the callback function when done. The call never fails. * Note that the inbound message visitor will be called before the callback because it is called directly whereas the callback is spawned in the background. @@ -78,11 +54,8 @@ void nano::transport::inproc::channel::send_buffer (nano::shared_const_buffer co // process message { - node.stats.inc (nano::stat::type::message, to_stat_detail (message_a->header.type), nano::stat::dir::in); - - // create an inbound message visitor class to handle incoming messages - message_visitor_inbound visitor{ destination.network.inbound, remote_channel }; - message_a->visit (visitor); + node.stats.inc (nano::stat::type::message, to_stat_detail (message_a->type ()), nano::stat::dir::in); + destination.network.inbound (*message_a, remote_channel); } }); diff --git a/nano/node/voting.cpp b/nano/node/voting.cpp index 9967f7edb..13a66ea9e 100644 --- a/nano/node/voting.cpp +++ b/nano/node/voting.cpp @@ -162,8 +162,9 @@ std::unique_ptr nano::collect_container_info (na return composite; } -nano::vote_generator::vote_generator (nano::node_config const & config_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) : +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), ledger (ledger_a), wallets (wallets_a), vote_processor (vote_processor_a), @@ -394,7 +395,7 @@ void nano::vote_generator::broadcast_action (std::shared_ptr const & { network.flood_vote_pr (vote_a); network.flood_vote (vote_a, 2.0f); - vote_processor.vote (vote_a, std::make_shared (network.node, network.node)); + vote_processor.vote (vote_a, std::make_shared (node, node)); // TODO: Avoid creating a temporary channel each time } void nano::vote_generator::run () diff --git a/nano/node/voting.hpp b/nano/node/voting.hpp index 6a2b2c79b..24c5813dd 100644 --- a/nano/node/voting.hpp +++ b/nano/node/voting.hpp @@ -22,6 +22,7 @@ namespace mi = boost::multi_index; namespace nano { +class node; class ledger; class network; class node_config; @@ -122,7 +123,7 @@ private: using queue_entry_t = std::pair; public: - vote_generator (nano::node_config const &, nano::ledger &, nano::wallets &, nano::vote_processor &, nano::local_vote_history &, nano::network &, nano::stats &, nano::logger &, bool is_final); + vote_generator (nano::node_config const &, nano::node &, nano::ledger &, nano::wallets &, nano::vote_processor &, nano::local_vote_history &, nano::network &, nano::stats &, nano::logger &, bool is_final); ~vote_generator (); /** Queue items for vote generation, or broadcast votes already in cache */ @@ -153,6 +154,7 @@ private: private: // Dependencies nano::node_config const & config; + nano::node & node; nano::ledger & ledger; nano::wallets & wallets; nano::vote_processor & vote_processor; From f0fcfecccd26f9be2c2d9f4b211d91f0d83156b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Wed, 13 Mar 2024 20:17:04 +0100 Subject: [PATCH 018/128] Unused cleanup --- nano/node/network.cpp | 28 ---------------------------- nano/node/network.hpp | 1 - 2 files changed, 29 deletions(-) diff --git a/nano/node/network.cpp b/nano/node/network.cpp index 04e5f489f..8bf4f1b8f 100644 --- a/nano/node/network.cpp +++ b/nano/node/network.cpp @@ -105,34 +105,6 @@ void nano::network::send_keepalive_self (std::shared_ptrsend (message); } -void nano::network::send_node_id_handshake (std::shared_ptr const & channel_a, std::optional const & cookie, std::optional const & respond_to) -{ - std::optional response; - if (respond_to) - { - nano::node_id_handshake::response_payload pld{ node.node_id.pub, nano::sign_message (node.node_id.prv, node.node_id.pub, *respond_to) }; - debug_assert (!nano::validate_message (pld.node_id, *respond_to, pld.signature)); - response = pld; - } - - std::optional query; - if (cookie) - { - nano::node_id_handshake::query_payload pld{ *cookie }; - query = pld; - } - - nano::node_id_handshake message{ node.network_params.network, query, response }; - - node.logger.debug (nano::log::type::network, "Node ID handshake sent to: {} (query: {}, respond to: {}, signature: {})", - nano::util::to_str (channel_a->get_endpoint ()), - (query ? query->cookie.to_string () : ""), - (respond_to ? respond_to->to_string () : ""), - (response ? response->signature.to_string () : "")); - - channel_a->send (message); -} - void nano::network::flood_message (nano::message & message_a, nano::transport::buffer_drop_policy const drop_policy_a, float const scale_a) { for (auto & i : list (fanout (scale_a))) diff --git a/nano/node/network.hpp b/nano/node/network.hpp index f49b5f498..75ed20679 100644 --- a/nano/node/network.hpp +++ b/nano/node/network.hpp @@ -94,7 +94,6 @@ public: void merge_peer (nano::endpoint const &); void send_keepalive (std::shared_ptr const &); void send_keepalive_self (std::shared_ptr const &); - void send_node_id_handshake (std::shared_ptr const &, std::optional const & cookie, std::optional const & respond_to); void send_confirm_req (std::shared_ptr const & channel_a, std::pair const & hash_root_a); std::shared_ptr find_node_id (nano::account const &); std::shared_ptr find_channel (nano::endpoint const &); From 09a94096b6af658e4fe08b230ff5b3e3f6e4905a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Wed, 13 Mar 2024 19:31:02 +0100 Subject: [PATCH 019/128] Run packet processing from start --- nano/node/network.cpp | 96 ++++++++++++++++++++++++------------------- nano/node/network.hpp | 9 ++-- 2 files changed, 60 insertions(+), 45 deletions(-) diff --git a/nano/node/network.cpp b/nano/node/network.cpp index 8bf4f1b8f..cf11683a3 100644 --- a/nano/node/network.cpp +++ b/nano/node/network.cpp @@ -23,43 +23,14 @@ nano::network::network (nano::node & node_a, uint16_t port_a) : tcp_channels (node_a, [this] (nano::message const & message, std::shared_ptr const & channel) { inbound (message, channel); }), - port (port_a), disconnect_observer ([] () {}) + port (port_a) { - for (std::size_t i = 0; i < node.config.network_threads && !node.flags.disable_tcp_realtime; ++i) - { - packet_processing_threads.emplace_back (nano::thread_attributes::get_default (), [this, i] () { - nano::thread_role::set (nano::thread_role::name::packet_processing); - try - { - tcp_channels.process_messages (); - } - catch (boost::system::error_code & ec) - { - node.logger.critical (nano::log::type::network, "Error: {}", ec.message ()); - release_assert (false); - } - catch (std::error_code & ec) - { - node.logger.critical (nano::log::type::network, "Error: {}", ec.message ()); - release_assert (false); - } - catch (std::runtime_error & err) - { - node.logger.critical (nano::log::type::network, "Error: {}", err.what ()); - release_assert (false); - } - catch (...) - { - node.logger.critical (nano::log::type::network, "Unknown error"); - release_assert (false); - } - }); - } } nano::network::~network () { - stop (); + // All threads must be stopped before this destructor + debug_assert (processing_threads.empty ()); } void nano::network::start () @@ -68,26 +39,67 @@ void nano::network::start () { ongoing_cleanup (); } + ongoing_syn_cookie_cleanup (); + ongoing_keepalive (); + if (!node.flags.disable_tcp_realtime) { tcp_channels.start (); + + for (std::size_t i = 0; i < node.config.network_threads; ++i) + { + processing_threads.emplace_back (nano::thread_attributes::get_default (), [this] () { + nano::thread_role::set (nano::thread_role::name::packet_processing); + run_processing (); + }); + } } - ongoing_keepalive (); } void nano::network::stop () { - if (!stopped.exchange (true)) + stopped = true; + + tcp_channels.stop (); + resolver.cancel (); + tcp_message_manager.stop (); + + for (auto & thread : processing_threads) { - tcp_channels.stop (); - resolver.cancel (); - tcp_message_manager.stop (); - port = 0; - for (auto & thread : packet_processing_threads) - { - thread.join (); - } + thread.join (); + } + processing_threads.clear (); + + port = 0; +} + +void nano::network::run_processing () +{ + try + { + // TODO: Move responsibility of packet queuing and processing to the message_processor class + tcp_channels.process_messages (); + } + catch (boost::system::error_code & ec) + { + node.logger.critical (nano::log::type::network, "Error: {}", ec.message ()); + release_assert (false); + } + catch (std::error_code & ec) + { + node.logger.critical (nano::log::type::network, "Error: {}", ec.message ()); + release_assert (false); + } + catch (std::runtime_error & err) + { + node.logger.critical (nano::log::type::network, "Error: {}", err.what ()); + release_assert (false); + } + catch (...) + { + node.logger.critical (nano::log::type::network, "Unknown error"); + release_assert (false); } } diff --git a/nano/node/network.hpp b/nano/node/network.hpp index 75ed20679..79b66dba9 100644 --- a/nano/node/network.hpp +++ b/nano/node/network.hpp @@ -130,6 +130,7 @@ public: // Handshake nano::node_id_handshake::response_payload prepare_handshake_response (nano::node_id_handshake::query_payload const & query, bool v2) const; private: + void run_processing (); void process_message (nano::message const &, std::shared_ptr const &); private: // Dependencies @@ -139,18 +140,20 @@ public: nano::networks const id; nano::syn_cookies syn_cookies; boost::asio::ip::udp::resolver resolver; - std::vector packet_processing_threads; nano::peer_exclusion excluded_peers; nano::tcp_message_manager tcp_message_manager; nano::network_filter publish_filter; nano::transport::tcp_channels tcp_channels; std::atomic port{ 0 }; - std::function disconnect_observer; + +public: // Callbacks + std::function disconnect_observer{ [] () {} }; // Called when a new channel is observed - std::function)> channel_observer; + std::function)> channel_observer{ [] (auto) {} }; private: std::atomic stopped{ false }; + std::vector processing_threads; // Using boost::thread to enable increased stack size public: static unsigned const broadcast_interval_ms = 10; From 98b3de81422f84cd8eb6eb270bfaacd3c43d94e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Wed, 13 Mar 2024 20:11:20 +0100 Subject: [PATCH 020/128] Dedicated thread for periodic cleanup --- nano/lib/stats_enums.hpp | 2 ++ nano/lib/thread_roles.cpp | 3 ++ nano/lib/thread_roles.hpp | 1 + nano/node/network.cpp | 60 ++++++++++++++++++++++++++++----------- nano/node/network.hpp | 7 +++-- 5 files changed, 55 insertions(+), 18 deletions(-) diff --git a/nano/lib/stats_enums.hpp b/nano/lib/stats_enums.hpp index 6d1ac20b2..71128cef1 100644 --- a/nano/lib/stats_enums.hpp +++ b/nano/lib/stats_enums.hpp @@ -17,6 +17,7 @@ enum class type : uint8_t ledger, rollback, bootstrap, + network, tcp_server, vote, election, @@ -67,6 +68,7 @@ enum class detail : uint8_t // common ok, loop, + loop_cleanup, total, process, processed, diff --git a/nano/lib/thread_roles.cpp b/nano/lib/thread_roles.cpp index 944ac0e1b..3bb70376e 100644 --- a/nano/lib/thread_roles.cpp +++ b/nano/lib/thread_roles.cpp @@ -109,6 +109,9 @@ std::string nano::thread_role::get_string (nano::thread_role::name role) case nano::thread_role::name::rep_tiers: thread_role_name_string = "Rep tiers"; break; + case nano::thread_role::name::network_cleanup: + thread_role_name_string = "Net cleanup"; + break; default: debug_assert (false && "nano::thread_role::get_string unhandled thread role"); } diff --git a/nano/lib/thread_roles.hpp b/nano/lib/thread_roles.hpp index 724efad5c..0fc3c9724 100644 --- a/nano/lib/thread_roles.hpp +++ b/nano/lib/thread_roles.hpp @@ -45,6 +45,7 @@ enum class name rep_crawler, local_block_broadcasting, rep_tiers, + network_cleanup, }; /* diff --git a/nano/node/network.cpp b/nano/node/network.cpp index cf11683a3..c1da3eabf 100644 --- a/nano/node/network.cpp +++ b/nano/node/network.cpp @@ -9,6 +9,8 @@ #include +using namespace std::chrono_literals; + /* * network */ @@ -31,13 +33,17 @@ nano::network::~network () { // All threads must be stopped before this destructor debug_assert (processing_threads.empty ()); + debug_assert (!cleanup_thread.joinable ()); } void nano::network::start () { if (!node.flags.disable_connection_cleanup) { - ongoing_cleanup (); + cleanup_thread = std::thread ([this] () { + nano::thread_role::set (nano::thread_role::name::network_cleanup); + run_cleanup (); + }); } ongoing_syn_cookie_cleanup (); @@ -59,7 +65,11 @@ void nano::network::start () void nano::network::stop () { - stopped = true; + { + nano::lock_guard lock{ mutex }; + stopped = true; + } + condition.notify_all (); tcp_channels.stop (); resolver.cancel (); @@ -71,6 +81,11 @@ void nano::network::stop () } processing_threads.clear (); + if (cleanup_thread.joinable ()) + { + cleanup_thread.join (); + } + port = 0; } @@ -103,6 +118,28 @@ void nano::network::run_processing () } } +void nano::network::run_cleanup () +{ + nano::unique_lock lock{ mutex }; + while (!stopped) + { + condition.wait_for (lock, node.network_params.network.is_dev_network () ? 1s : 5s); + lock.unlock (); + + if (stopped) + { + return; + } + + node.stats.inc (nano::stat::type::network, nano::stat::detail::loop_cleanup); + + auto const cutoff = std::chrono::steady_clock::now () - node.network_params.network.cleanup_cutoff (); + cleanup (cutoff); + + lock.lock (); + } +} + void nano::network::send_keepalive (std::shared_ptr const & channel_a) { nano::keepalive message{ node.network_params.network }; @@ -493,27 +530,18 @@ nano::endpoint nano::network::endpoint () const return nano::endpoint (boost::asio::ip::address_v6::loopback (), port); } -void nano::network::cleanup (std::chrono::steady_clock::time_point const & cutoff_a) +void nano::network::cleanup (std::chrono::steady_clock::time_point const & cutoff) { - tcp_channels.purge (cutoff_a); + node.logger.debug (nano::log::type::network, "Performing cleanup, cutoff: {}s", nano::log::seconds_delta (cutoff)); + + tcp_channels.purge (cutoff); + if (node.network.empty ()) { disconnect_observer (); } } -void nano::network::ongoing_cleanup () -{ - cleanup (std::chrono::steady_clock::now () - node.network_params.network.cleanup_cutoff ()); - std::weak_ptr node_w (node.shared ()); - node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (node.network_params.network.is_dev_network () ? 1 : 5), [node_w] () { - if (auto node_l = node_w.lock ()) - { - node_l->network.ongoing_cleanup (); - } - }); -} - void nano::network::ongoing_syn_cookie_cleanup () { syn_cookies.purge (std::chrono::steady_clock::now () - nano::transport::syn_cookie_cutoff); diff --git a/nano/node/network.hpp b/nano/node/network.hpp index 79b66dba9..0e1a74225 100644 --- a/nano/node/network.hpp +++ b/nano/node/network.hpp @@ -111,8 +111,7 @@ public: // Get the next peer for attempting a tcp bootstrap connection nano::tcp_endpoint bootstrap_peer (); nano::endpoint endpoint () const; - void cleanup (std::chrono::steady_clock::time_point const &); - void ongoing_cleanup (); + void cleanup (std::chrono::steady_clock::time_point const & cutoff); void ongoing_syn_cookie_cleanup (); void ongoing_keepalive (); std::size_t size () const; @@ -131,6 +130,7 @@ public: // Handshake private: void run_processing (); + void run_cleanup (); void process_message (nano::message const &, std::shared_ptr const &); private: // Dependencies @@ -153,7 +153,10 @@ public: // Callbacks private: std::atomic stopped{ false }; + mutable nano::mutex mutex; + nano::condition_variable condition; std::vector processing_threads; // Using boost::thread to enable increased stack size + std::thread cleanup_thread; public: static unsigned const broadcast_interval_ms = 10; From 05fbd416e438550be5358ca5f2e2a462677df740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Wed, 13 Mar 2024 20:34:53 +0100 Subject: [PATCH 021/128] Purge syn cookies from thread --- nano/lib/logging_enums.hpp | 1 + nano/lib/stats_enums.hpp | 1 + nano/node/network.cpp | 67 ++++++++++++++----------------- nano/node/network.hpp | 8 ++-- nano/node/transport/transport.hpp | 1 - 5 files changed, 37 insertions(+), 41 deletions(-) diff --git a/nano/lib/logging_enums.hpp b/nano/lib/logging_enums.hpp index eddf9b120..822f73621 100644 --- a/nano/lib/logging_enums.hpp +++ b/nano/lib/logging_enums.hpp @@ -72,6 +72,7 @@ enum class type election_scheduler, vote_generator, rep_tiers, + syn_cookies, // bootstrap bulk_pull_client, diff --git a/nano/lib/stats_enums.hpp b/nano/lib/stats_enums.hpp index 71128cef1..3e16f8d5c 100644 --- a/nano/lib/stats_enums.hpp +++ b/nano/lib/stats_enums.hpp @@ -53,6 +53,7 @@ enum class type : uint8_t rep_crawler, local_block_broadcaster, rep_tiers, + syn_cookies, bootstrap_ascending, bootstrap_ascending_accounts, diff --git a/nano/node/network.cpp b/nano/node/network.cpp index c1da3eabf..61e60b01b 100644 --- a/nano/node/network.cpp +++ b/nano/node/network.cpp @@ -15,17 +15,17 @@ using namespace std::chrono_literals; * network */ -nano::network::network (nano::node & node_a, uint16_t port_a) : - node (node_a), - id (nano::network_constants::active_network), - syn_cookies (node_a.network_params.network.max_peers_per_ip), - resolver (node_a.io_ctx), - tcp_message_manager (node_a.config.tcp_incoming_connections_max), - publish_filter (256 * 1024), - tcp_channels (node_a, [this] (nano::message const & message, std::shared_ptr const & channel) { - inbound (message, channel); - }), - port (port_a) +nano::network::network (nano::node & node, uint16_t port) : + node{ node }, + id{ nano::network_constants::active_network }, + syn_cookies{ node.network_params.network.max_peers_per_ip, node.logger }, + resolver{ node.io_ctx }, + tcp_message_manager{ node.config.tcp_incoming_connections_max }, + publish_filter{ 256 * 1024 }, + tcp_channels{ node, [this] (nano::message const & message, std::shared_ptr const & channel) { + inbound (message, channel); + } }, + port{ port } { } @@ -38,15 +38,11 @@ nano::network::~network () void nano::network::start () { - if (!node.flags.disable_connection_cleanup) - { - cleanup_thread = std::thread ([this] () { - nano::thread_role::set (nano::thread_role::name::network_cleanup); - run_cleanup (); - }); - } + cleanup_thread = std::thread ([this] () { + nano::thread_role::set (nano::thread_role::name::network_cleanup); + run_cleanup (); + }); - ongoing_syn_cookie_cleanup (); ongoing_keepalive (); if (!node.flags.disable_tcp_realtime) @@ -133,8 +129,14 @@ void nano::network::run_cleanup () node.stats.inc (nano::stat::type::network, nano::stat::detail::loop_cleanup); - auto const cutoff = std::chrono::steady_clock::now () - node.network_params.network.cleanup_cutoff (); - cleanup (cutoff); + if (!node.flags.disable_connection_cleanup) + { + auto const cutoff = std::chrono::steady_clock::now () - node.network_params.network.cleanup_cutoff (); + cleanup (cutoff); + } + + auto const syn_cookie_cutoff = std::chrono::steady_clock::now () - node.network_params.network.syn_cookie_cutoff; + syn_cookies.purge (syn_cookie_cutoff); lock.lock (); } @@ -542,18 +544,6 @@ void nano::network::cleanup (std::chrono::steady_clock::time_point const & cutof } } -void nano::network::ongoing_syn_cookie_cleanup () -{ - syn_cookies.purge (std::chrono::steady_clock::now () - nano::transport::syn_cookie_cutoff); - std::weak_ptr node_w (node.shared ()); - node.workers.add_timed_task (std::chrono::steady_clock::now () + (nano::transport::syn_cookie_cutoff * 2), [node_w] () { - if (auto node_l = node_w.lock ()) - { - node_l->network.ongoing_syn_cookie_cleanup (); - } - }); -} - void nano::network::ongoing_keepalive () { flood_keepalive (0.75f); @@ -717,18 +707,19 @@ void nano::tcp_message_manager::stop () * syn_cookies */ -nano::syn_cookies::syn_cookies (std::size_t max_cookies_per_ip_a) : - max_cookies_per_ip (max_cookies_per_ip_a) +nano::syn_cookies::syn_cookies (std::size_t max_cookies_per_ip_a, nano::logger & logger_a) : + max_cookies_per_ip (max_cookies_per_ip_a), + logger (logger_a) { } -boost::optional nano::syn_cookies::assign (nano::endpoint const & endpoint_a) +std::optional nano::syn_cookies::assign (nano::endpoint const & endpoint_a) { auto ip_addr (endpoint_a.address ()); debug_assert (ip_addr.is_v6 ()); nano::lock_guard lock{ syn_cookie_mutex }; unsigned & ip_cookies = cookies_per_ip[ip_addr]; - boost::optional result; + std::optional result; if (ip_cookies < max_cookies_per_ip) { if (cookies.find (endpoint_a) == cookies.end ()) @@ -770,6 +761,8 @@ bool nano::syn_cookies::validate (nano::endpoint const & endpoint_a, nano::accou void nano::syn_cookies::purge (std::chrono::steady_clock::time_point const & cutoff_a) { + logger.debug (nano::log::type::syn_cookies, "Purging syn cookies, cutoff: {}s", nano::log::seconds_delta (cutoff_a)); + nano::lock_guard lock{ syn_cookie_mutex }; auto it (cookies.begin ()); while (it != cookies.end ()) diff --git a/nano/node/network.hpp b/nano/node/network.hpp index 0e1a74225..d9706fcf3 100644 --- a/nano/node/network.hpp +++ b/nano/node/network.hpp @@ -43,12 +43,12 @@ private: class syn_cookies final { public: - explicit syn_cookies (std::size_t); + syn_cookies (std::size_t max_peers_per_ip, nano::logger &); void purge (std::chrono::steady_clock::time_point const &); // Returns boost::none if the IP is rate capped on syn cookie requests, // or if the endpoint already has a syn cookie query - boost::optional assign (nano::endpoint const &); + std::optional assign (nano::endpoint const &); // Returns false if valid, true if invalid (true on error convention) // Also removes the syn cookie from the store if valid bool validate (nano::endpoint const &, nano::account const &, nano::signature const &); @@ -58,6 +58,9 @@ public: std::unique_ptr collect_container_info (std::string const &); std::size_t cookies_size (); +private: // Dependencies + nano::logger & logger; + private: class syn_cookie_info final { @@ -112,7 +115,6 @@ public: nano::tcp_endpoint bootstrap_peer (); nano::endpoint endpoint () const; void cleanup (std::chrono::steady_clock::time_point const & cutoff); - void ongoing_syn_cookie_cleanup (); void ongoing_keepalive (); std::size_t size () const; float size_sqrt () const; diff --git a/nano/node/transport/transport.hpp b/nano/node/transport/transport.hpp index 359adb0e3..6e177a3f8 100644 --- a/nano/node/transport/transport.hpp +++ b/nano/node/transport/transport.hpp @@ -22,5 +22,4 @@ bool is_ipv4_or_v4_mapped_address (boost::asio::ip::address const &); // Unassigned, reserved, self bool reserved_address (nano::endpoint const &, bool = false); -static std::chrono::seconds constexpr syn_cookie_cutoff = std::chrono::seconds (5); } \ No newline at end of file From fa84f7b38125c2b32439405c7bb7feaaf63fa280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 14 Mar 2024 17:38:55 +0100 Subject: [PATCH 022/128] Remove unused code --- nano/node/network.cpp | 8 -------- nano/node/network.hpp | 1 - 2 files changed, 9 deletions(-) diff --git a/nano/node/network.cpp b/nano/node/network.cpp index 61e60b01b..69e3a0d29 100644 --- a/nano/node/network.cpp +++ b/nano/node/network.cpp @@ -239,14 +239,6 @@ void nano::network::flood_block_many (std::deque> b } } -void nano::network::send_confirm_req (std::shared_ptr const & channel_a, std::pair const & hash_root_a) -{ - auto & [hash, root] = hash_root_a; - // Confirmation request with hash + root - nano::confirm_req req (node.network_params.network, hash, root); - channel_a->send (req); -} - namespace { class network_message_visitor : public nano::message_visitor diff --git a/nano/node/network.hpp b/nano/node/network.hpp index d9706fcf3..43e1c1f1d 100644 --- a/nano/node/network.hpp +++ b/nano/node/network.hpp @@ -97,7 +97,6 @@ public: void merge_peer (nano::endpoint const &); void send_keepalive (std::shared_ptr const &); void send_keepalive_self (std::shared_ptr const &); - void send_confirm_req (std::shared_ptr const & channel_a, std::pair const & hash_root_a); std::shared_ptr find_node_id (nano::account const &); std::shared_ptr find_channel (nano::endpoint const &); bool not_a_peer (nano::endpoint const &, bool); From b67d125896773fe1e383a42391b5375f8642b62f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 14 Mar 2024 17:47:30 +0100 Subject: [PATCH 023/128] Dedicated thread for keepalives --- nano/lib/stats_enums.hpp | 1 + nano/lib/thread_roles.cpp | 3 +++ nano/lib/thread_roles.hpp | 1 + nano/node/network.cpp | 45 +++++++++++++++++++++++++++------------ nano/node/network.hpp | 3 ++- 5 files changed, 38 insertions(+), 15 deletions(-) diff --git a/nano/lib/stats_enums.hpp b/nano/lib/stats_enums.hpp index 3e16f8d5c..68679b2c5 100644 --- a/nano/lib/stats_enums.hpp +++ b/nano/lib/stats_enums.hpp @@ -70,6 +70,7 @@ enum class detail : uint8_t ok, loop, loop_cleanup, + loop_keepalive, total, process, processed, diff --git a/nano/lib/thread_roles.cpp b/nano/lib/thread_roles.cpp index 3bb70376e..6c66748d6 100644 --- a/nano/lib/thread_roles.cpp +++ b/nano/lib/thread_roles.cpp @@ -112,6 +112,9 @@ std::string nano::thread_role::get_string (nano::thread_role::name role) case nano::thread_role::name::network_cleanup: thread_role_name_string = "Net cleanup"; break; + case nano::thread_role::name::network_keepalive: + thread_role_name_string = "Net keepalive"; + break; default: debug_assert (false && "nano::thread_role::get_string unhandled thread role"); } diff --git a/nano/lib/thread_roles.hpp b/nano/lib/thread_roles.hpp index 0fc3c9724..8fd4175ae 100644 --- a/nano/lib/thread_roles.hpp +++ b/nano/lib/thread_roles.hpp @@ -46,6 +46,7 @@ enum class name local_block_broadcasting, rep_tiers, network_cleanup, + network_keepalive, }; /* diff --git a/nano/node/network.cpp b/nano/node/network.cpp index 69e3a0d29..befea2af9 100644 --- a/nano/node/network.cpp +++ b/nano/node/network.cpp @@ -34,6 +34,7 @@ nano::network::~network () // All threads must be stopped before this destructor debug_assert (processing_threads.empty ()); debug_assert (!cleanup_thread.joinable ()); + debug_assert (!keepalive_thread.joinable ()); } void nano::network::start () @@ -43,7 +44,10 @@ void nano::network::start () run_cleanup (); }); - ongoing_keepalive (); + keepalive_thread = std::thread ([this] () { + nano::thread_role::set (nano::thread_role::name::network_keepalive); + run_keepalive (); + }); if (!node.flags.disable_tcp_realtime) { @@ -77,6 +81,10 @@ void nano::network::stop () } processing_threads.clear (); + if (keepalive_thread.joinable ()) + { + keepalive_thread.join (); + } if (cleanup_thread.joinable ()) { cleanup_thread.join (); @@ -142,6 +150,28 @@ void nano::network::run_cleanup () } } +void nano::network::run_keepalive () +{ + nano::unique_lock lock{ mutex }; + while (!stopped) + { + condition.wait_for (lock, node.network_params.network.keepalive_period); + lock.unlock (); + + if (stopped) + { + return; + } + + node.stats.inc (nano::stat::type::network, nano::stat::detail::loop_keepalive); + + flood_keepalive (0.75f); + flood_keepalive_self (0.25f); + + lock.lock (); + } +} + void nano::network::send_keepalive (std::shared_ptr const & channel_a) { nano::keepalive message{ node.network_params.network }; @@ -536,19 +566,6 @@ void nano::network::cleanup (std::chrono::steady_clock::time_point const & cutof } } -void nano::network::ongoing_keepalive () -{ - flood_keepalive (0.75f); - flood_keepalive_self (0.25f); - std::weak_ptr node_w (node.shared ()); - node.workers.add_timed_task (std::chrono::steady_clock::now () + node.network_params.network.keepalive_period, [node_w] () { - if (auto node_l = node_w.lock ()) - { - node_l->network.ongoing_keepalive (); - } - }); -} - std::size_t nano::network::size () const { return tcp_channels.size (); diff --git a/nano/node/network.hpp b/nano/node/network.hpp index 43e1c1f1d..5969333fd 100644 --- a/nano/node/network.hpp +++ b/nano/node/network.hpp @@ -114,7 +114,6 @@ public: nano::tcp_endpoint bootstrap_peer (); nano::endpoint endpoint () const; void cleanup (std::chrono::steady_clock::time_point const & cutoff); - void ongoing_keepalive (); std::size_t size () const; float size_sqrt () const; bool empty () const; @@ -132,6 +131,7 @@ public: // Handshake private: void run_processing (); void run_cleanup (); + void run_keepalive (); void process_message (nano::message const &, std::shared_ptr const &); private: // Dependencies @@ -158,6 +158,7 @@ private: nano::condition_variable condition; std::vector processing_threads; // Using boost::thread to enable increased stack size std::thread cleanup_thread; + std::thread keepalive_thread; public: static unsigned const broadcast_interval_ms = 10; From e1d1517ab841ec23358218ec0f277836ff481f7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 14 Mar 2024 19:21:47 +0100 Subject: [PATCH 024/128] Move `tcp_message_manager` into `tcp_channels` class --- nano/node/network.cpp | 2 -- nano/node/network.hpp | 22 --------------------- nano/node/transport/tcp.cpp | 6 +++++- nano/node/transport/tcp.hpp | 31 +++++++++++++++++++++++++++++- nano/node/transport/tcp_server.cpp | 2 +- 5 files changed, 36 insertions(+), 27 deletions(-) diff --git a/nano/node/network.cpp b/nano/node/network.cpp index befea2af9..528739133 100644 --- a/nano/node/network.cpp +++ b/nano/node/network.cpp @@ -20,7 +20,6 @@ nano::network::network (nano::node & node, uint16_t port) : id{ nano::network_constants::active_network }, syn_cookies{ node.network_params.network.max_peers_per_ip, node.logger }, resolver{ node.io_ctx }, - tcp_message_manager{ node.config.tcp_incoming_connections_max }, publish_filter{ 256 * 1024 }, tcp_channels{ node, [this] (nano::message const & message, std::shared_ptr const & channel) { inbound (message, channel); @@ -73,7 +72,6 @@ void nano::network::stop () tcp_channels.stop (); resolver.cancel (); - tcp_message_manager.stop (); for (auto & thread : processing_threads) { diff --git a/nano/node/network.hpp b/nano/node/network.hpp index 5969333fd..d51aedaa4 100644 --- a/nano/node/network.hpp +++ b/nano/node/network.hpp @@ -16,27 +16,6 @@ namespace nano { class node; -class tcp_message_manager final -{ -public: - tcp_message_manager (unsigned incoming_connections_max_a); - void put_message (nano::tcp_message_item const & item_a); - nano::tcp_message_item get_message (); - // Stop container and notify waiting threads - void stop (); - -private: - nano::mutex mutex; - nano::condition_variable producer_condition; - nano::condition_variable consumer_condition; - std::deque entries; - unsigned max_entries; - static unsigned const max_entries_per_connection = 16; - bool stopped{ false }; - - friend class network_tcp_message_manager_Test; -}; - /** * Node ID cookies for node ID handshakes */ @@ -142,7 +121,6 @@ public: nano::syn_cookies syn_cookies; boost::asio::ip::udp::resolver resolver; nano::peer_exclusion excluded_peers; - nano::tcp_message_manager tcp_message_manager; nano::network_filter publish_filter; nano::transport::tcp_channels tcp_channels; std::atomic port{ 0 }; diff --git a/nano/node/transport/tcp.cpp b/nano/node/transport/tcp.cpp index e6ddca612..6dab45020 100644 --- a/nano/node/transport/tcp.cpp +++ b/nano/node/transport/tcp.cpp @@ -126,6 +126,7 @@ void nano::transport::channel_tcp::operator() (nano::object_stream & obs) const nano::transport::tcp_channels::tcp_channels (nano::node & node, std::function const &)> sink) : node{ node }, + message_manager{ node.config.tcp_incoming_connections_max }, sink{ std::move (sink) } { } @@ -295,7 +296,7 @@ void nano::transport::tcp_channels::process_messages () { while (!stopped) { - auto item (node.network.tcp_message_manager.get_message ()); + auto item = message_manager.get_message (); if (item.message != nullptr) { process_message (*item.message, item.endpoint, item.node_id, item.socket); @@ -364,6 +365,9 @@ void nano::transport::tcp_channels::stop () { stopped = true; nano::unique_lock lock{ mutex }; + + message_manager.stop (); + // Close all TCP sockets for (auto const & channel : channels) { diff --git a/nano/node/transport/tcp.hpp b/nano/node/transport/tcp.hpp index 0f65b3bd0..fd588eb45 100644 --- a/nano/node/transport/tcp.hpp +++ b/nano/node/transport/tcp.hpp @@ -25,6 +25,28 @@ public: nano::account node_id; std::shared_ptr socket; }; + +class tcp_message_manager final +{ +public: + tcp_message_manager (unsigned incoming_connections_max_a); + void put_message (nano::tcp_message_item const & item_a); + nano::tcp_message_item get_message (); + // Stop container and notify waiting threads + void stop (); + +private: + nano::mutex mutex; + nano::condition_variable producer_condition; + nano::condition_variable consumer_condition; + std::deque entries; + unsigned max_entries; + static unsigned const max_entries_per_connection = 16; + bool stopped{ false }; + + friend class network_tcp_message_manager_Test; +}; + namespace transport { class tcp_server; @@ -136,10 +158,14 @@ namespace transport // Connection start void start_tcp (nano::endpoint const &); void start_tcp_receive_node_id (std::shared_ptr const &, nano::endpoint const &, std::shared_ptr> const &); + + private: // Dependencies nano::node & node; + public: + nano::tcp_message_manager message_manager; + private: - std::function const &)> sink; class endpoint_tag { }; @@ -255,6 +281,9 @@ namespace transport mi::member>>> attempts; // clang-format on + + private: + std::function const &)> sink; std::atomic stopped{ false }; friend class network_peer_max_tcp_attempts_subnetwork_Test; diff --git a/nano/node/transport/tcp_server.cpp b/nano/node/transport/tcp_server.cpp index 7e37ef97b..edd7f9663 100644 --- a/nano/node/transport/tcp_server.cpp +++ b/nano/node/transport/tcp_server.cpp @@ -496,7 +496,7 @@ void nano::transport::tcp_server::queue_realtime (std::unique_ptr { return; } - node->network.tcp_message_manager.put_message (nano::tcp_message_item{ std::move (message), remote_endpoint, remote_node_id, socket }); + node->network.tcp_channels.message_manager.put_message (nano::tcp_message_item{ std::move (message), remote_endpoint, remote_node_id, socket }); } /* From 89317dc542a10fc1cb3f0f2fa0f826be91578c7f Mon Sep 17 00:00:00 2001 From: clemahieu Date: Sat, 16 Mar 2024 15:44:24 +0000 Subject: [PATCH 025/128] Add operator* to store::iterator. (#4493) --- nano/store/iterator.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nano/store/iterator.hpp b/nano/store/iterator.hpp index aef5cc709..7fe957d72 100644 --- a/nano/store/iterator.hpp +++ b/nano/store/iterator.hpp @@ -49,6 +49,10 @@ public: { return ¤t; } + std::pair const & operator* () const + { + return current; + } bool operator== (iterator const & other_a) const { return (impl == nullptr && other_a.impl == nullptr) || (impl != nullptr && *impl == other_a.impl.get ()) || (other_a.impl != nullptr && *other_a.impl == impl.get ()); From ea579c76c06ecaa567b58ee581b8ce447d85cb1f Mon Sep 17 00:00:00 2001 From: clemahieu Date: Sat, 16 Mar 2024 15:44:40 +0000 Subject: [PATCH 026/128] Add work_pool to nano::test::context::ledger_context (#4494) --- nano/core_test/ledger.cpp | 156 ++++++++++++++++++------------------ nano/test_common/ledger.cpp | 8 +- nano/test_common/ledger.hpp | 2 + 3 files changed, 87 insertions(+), 79 deletions(-) diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 77a19cae4..4c021abdd 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -66,7 +66,7 @@ TEST (ledger, process_modifies_sideband) auto ctx = nano::test::context::ledger_empty (); auto & ledger = ctx.ledger (); auto & store = ctx.store (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::block_builder builder; auto send1 = builder .state () @@ -89,7 +89,7 @@ TEST (ledger, process_send) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); auto info1 = ledger.account_info (transaction, nano::dev::genesis_key.pub); ASSERT_TRUE (info1); nano::keypair key2; @@ -192,7 +192,7 @@ TEST (ledger, process_receive) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); auto info1 = ledger.account_info (transaction, nano::dev::genesis_key.pub); ASSERT_TRUE (info1); nano::keypair key2; @@ -275,7 +275,7 @@ TEST (ledger, rollback_receiver) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); auto info1 = ledger.account_info (transaction, nano::dev::genesis_key.pub); ASSERT_TRUE (info1); nano::keypair key2; @@ -324,7 +324,7 @@ TEST (ledger, rollback_representation) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key5; nano::block_builder builder; auto change1 = builder @@ -414,7 +414,7 @@ TEST (ledger, receive_rollback) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::block_builder builder; auto send = builder .send () @@ -442,7 +442,7 @@ TEST (ledger, process_duplicate) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); auto info1 = ledger.account_info (transaction, nano::dev::genesis_key.pub); ASSERT_TRUE (info1); nano::keypair key2; @@ -495,7 +495,7 @@ TEST (ledger, representative_change) auto & store = ctx.store (); auto transaction = store.tx_begin_write (); nano::keypair key2; - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub)); ASSERT_EQ (0, ledger.weight (key2.pub)); auto info1 = ledger.account_info (transaction, nano::dev::genesis_key.pub); @@ -533,7 +533,7 @@ TEST (ledger, send_fork) nano::keypair key2; nano::keypair key3; auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); auto info1 = ledger.account_info (transaction, nano::dev::genesis_key.pub); ASSERT_TRUE (info1); nano::block_builder builder; @@ -565,7 +565,7 @@ TEST (ledger, receive_fork) nano::keypair key2; nano::keypair key3; auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); auto info1 = ledger.account_info (transaction, nano::dev::genesis_key.pub); ASSERT_TRUE (info1); nano::block_builder builder; @@ -622,7 +622,7 @@ TEST (ledger, open_fork) nano::keypair key2; nano::keypair key3; auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); auto info1 = ledger.account_info (transaction, nano::dev::genesis_key.pub); ASSERT_TRUE (info1); nano::block_builder builder; @@ -673,7 +673,7 @@ TEST (ledger, representation) auto & store = ctx.store (); auto & rep_weights = ledger.cache.rep_weights; auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); ASSERT_EQ (nano::dev::constants.genesis_amount, rep_weights.representation_get (nano::dev::genesis_key.pub)); nano::keypair key2; nano::block_builder builder; @@ -845,7 +845,7 @@ TEST (ledger, double_receive) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key2; nano::block_builder builder; auto send1 = builder @@ -1186,7 +1186,7 @@ TEST (ledger, fail_change_old) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block = builder @@ -1208,7 +1208,7 @@ TEST (ledger, fail_change_gap_previous) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block = builder @@ -1228,7 +1228,7 @@ TEST (ledger, fail_state_bad_signature) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::block_builder builder; auto block = builder .state () @@ -1250,7 +1250,7 @@ TEST (ledger, fail_epoch_bad_signature) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::block_builder builder; auto block = builder .state () @@ -1276,7 +1276,7 @@ TEST (ledger, fail_change_bad_signature) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block = builder @@ -1296,7 +1296,7 @@ TEST (ledger, fail_change_fork) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block1 = builder @@ -1326,7 +1326,7 @@ TEST (ledger, fail_send_old) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block = builder @@ -1349,7 +1349,7 @@ TEST (ledger, fail_send_gap_previous) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block = builder @@ -1370,7 +1370,7 @@ TEST (ledger, fail_send_bad_signature) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block = builder @@ -1391,7 +1391,7 @@ TEST (ledger, fail_send_negative_spend) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block1 = builder @@ -1421,7 +1421,7 @@ TEST (ledger, fail_send_fork) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block1 = builder @@ -1451,7 +1451,7 @@ TEST (ledger, fail_open_old) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block1 = builder @@ -1481,7 +1481,7 @@ TEST (ledger, fail_open_gap_source) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block2 = builder @@ -1502,7 +1502,7 @@ TEST (ledger, fail_open_bad_signature) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block1 = builder @@ -1532,7 +1532,7 @@ TEST (ledger, fail_open_fork_previous) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block1 = builder @@ -1580,7 +1580,7 @@ TEST (ledger, fail_open_account_mismatch) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block1 = builder @@ -1611,7 +1611,7 @@ TEST (ledger, fail_receive_old) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block1 = builder @@ -1658,7 +1658,7 @@ TEST (ledger, fail_receive_gap_source) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block1 = builder @@ -1708,7 +1708,7 @@ TEST (ledger, fail_receive_overreceive) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block1 = builder @@ -1748,7 +1748,7 @@ TEST (ledger, fail_receive_bad_signature) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block1 = builder @@ -1798,7 +1798,7 @@ TEST (ledger, fail_receive_gap_previous_opened) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block1 = builder @@ -1848,7 +1848,7 @@ TEST (ledger, fail_receive_gap_previous_unopened) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block1 = builder @@ -1888,7 +1888,7 @@ TEST (ledger, fail_receive_fork_previous) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block1 = builder @@ -1949,7 +1949,7 @@ TEST (ledger, fail_receive_received_source) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; nano::block_builder builder; auto block1 = builder @@ -2040,7 +2040,7 @@ TEST (ledger, latest_root) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key; ASSERT_EQ (key.pub, ledger.latest_root (transaction, key.pub).as_account ()); auto hash1 = ledger.latest (transaction, nano::dev::genesis_key.pub); @@ -2064,7 +2064,7 @@ TEST (ledger, change_representative_move_representation) auto & store = ctx.store (); nano::keypair key1; auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub)); nano::block_builder builder; auto send = builder @@ -2105,7 +2105,7 @@ TEST (ledger, send_open_receive_rollback) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); auto info1 = ledger.account_info (transaction, nano::dev::genesis_key.pub); ASSERT_TRUE (info1); nano::keypair key1; @@ -2194,7 +2194,7 @@ TEST (ledger, bootstrap_rep_weight) auto & ledger = ctx.ledger (); auto & store = ctx.store (); nano::keypair key2; - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); { auto transaction = store.tx_begin_write (); auto info1 = ledger.account_info (transaction, nano::dev::genesis_key.pub); @@ -2241,7 +2241,7 @@ TEST (ledger, block_destination_source) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair dest; nano::uint128_t balance (nano::dev::constants.genesis_amount); balance -= nano::Gxrb_ratio; @@ -2331,7 +2331,7 @@ TEST (ledger, state_account) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::block_builder builder; auto send1 = builder .state () @@ -2353,7 +2353,7 @@ TEST (ledger, state_send_receive) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::block_builder builder; auto send1 = builder .state () @@ -2410,7 +2410,7 @@ TEST (ledger, state_receive) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::block_builder builder; auto send1 = builder .send () @@ -2458,7 +2458,7 @@ TEST (ledger, state_rep_change) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair rep; nano::block_builder builder; auto change1 = builder @@ -2492,7 +2492,7 @@ TEST (ledger, state_open) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair destination; nano::block_builder builder; auto send1 = builder @@ -2547,7 +2547,7 @@ TEST (ledger, send_after_state_fail) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::block_builder builder; auto send1 = builder .state () @@ -2578,7 +2578,7 @@ TEST (ledger, receive_after_state_fail) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::block_builder builder; auto send1 = builder .state () @@ -2608,7 +2608,7 @@ TEST (ledger, change_after_state_fail) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::block_builder builder; auto send1 = builder .state () @@ -2638,7 +2638,7 @@ TEST (ledger, state_unreceivable_fail) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::block_builder builder; auto send1 = builder .send () @@ -2675,7 +2675,7 @@ TEST (ledger, state_receive_bad_amount_fail) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::block_builder builder; auto send1 = builder .send () @@ -2712,7 +2712,7 @@ TEST (ledger, state_no_link_amount_fail) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::block_builder builder; auto send1 = builder .state () @@ -2745,7 +2745,7 @@ TEST (ledger, state_receive_wrong_account_fail) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::block_builder builder; auto send1 = builder .state () @@ -2785,7 +2785,7 @@ TEST (ledger, state_open_state_fork) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair destination; nano::block_builder builder; auto send1 = builder @@ -2828,7 +2828,7 @@ TEST (ledger, state_state_open_fork) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair destination; nano::block_builder builder; auto send1 = builder @@ -2872,7 +2872,7 @@ TEST (ledger, state_open_previous_fail) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair destination; nano::block_builder builder; auto send1 = builder @@ -2905,7 +2905,7 @@ TEST (ledger, state_open_source_fail) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair destination; nano::block_builder builder; auto send1 = builder @@ -2938,7 +2938,7 @@ TEST (ledger, state_send_change) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair rep; nano::block_builder builder; auto send1 = builder @@ -2972,7 +2972,7 @@ TEST (ledger, state_receive_change) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::block_builder builder; auto send1 = builder .state () @@ -3024,7 +3024,7 @@ TEST (ledger, state_open_old) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair destination; nano::block_builder builder; auto send1 = builder @@ -3058,7 +3058,7 @@ TEST (ledger, state_receive_old) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair destination; nano::block_builder builder; auto send1 = builder @@ -3111,7 +3111,7 @@ TEST (ledger, state_rollback_send) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::block_builder builder; auto send1 = builder .state () @@ -3149,7 +3149,7 @@ TEST (ledger, state_rollback_receive) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::block_builder builder; auto send1 = builder .state () @@ -3191,7 +3191,7 @@ TEST (ledger, state_rollback_received_send) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key; nano::block_builder builder; auto send1 = builder @@ -3234,7 +3234,7 @@ TEST (ledger, state_rep_change_rollback) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair rep; nano::block_builder builder; auto change1 = builder @@ -3261,7 +3261,7 @@ TEST (ledger, state_open_rollback) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair destination; nano::block_builder builder; auto send1 = builder @@ -3303,7 +3303,7 @@ TEST (ledger, state_send_change_rollback) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair rep; nano::block_builder builder; auto send1 = builder @@ -3331,7 +3331,7 @@ TEST (ledger, state_receive_change_rollback) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::block_builder builder; auto send1 = builder .state () @@ -3370,7 +3370,7 @@ TEST (ledger, epoch_blocks_v1_general) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair destination; nano::block_builder builder; auto epoch1 = builder @@ -3513,7 +3513,7 @@ TEST (ledger, epoch_blocks_v2_general) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair destination; nano::block_builder builder; auto epoch1 = builder @@ -3678,7 +3678,7 @@ TEST (ledger, epoch_blocks_receive_upgrade) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair destination; nano::block_builder builder; auto send1 = builder @@ -3886,7 +3886,7 @@ TEST (ledger, epoch_blocks_fork) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair destination; nano::block_builder builder; auto send1 = builder @@ -4413,7 +4413,7 @@ TEST (ledger, confirmation_height_not_updated) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto transaction = store.tx_begin_write (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); auto account_info = ledger.account_info (transaction, nano::dev::genesis_key.pub); ASSERT_TRUE (account_info); nano::keypair key; @@ -4485,7 +4485,7 @@ TEST (ledger, work_validation) auto ctx = nano::test::context::ledger_empty (); auto & ledger = ctx.ledger (); auto & store = ctx.store (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::block_builder builder; auto gen = nano::dev::genesis_key; nano::keypair key; @@ -4577,7 +4577,7 @@ TEST (ledger, dependents_confirmed) auto transaction = store.tx_begin_write (); nano::block_builder builder; ASSERT_TRUE (ledger.dependents_confirmed (transaction, *nano::dev::genesis)); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; auto send1 = builder.state () .account (nano::dev::genesis_key.pub) @@ -4697,7 +4697,7 @@ TEST (ledger, block_confirmed) auto transaction = store.tx_begin_write (); nano::block_builder builder; ASSERT_TRUE (ledger.block_confirmed (transaction, nano::dev::genesis->hash ())); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::keypair key1; auto send1 = builder.state () .account (nano::dev::genesis_key.pub) @@ -4725,7 +4725,7 @@ TEST (ledger, cache) auto & ledger = ctx.ledger (); auto & store = ctx.store (); auto & stats = ctx.stats (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto & pool = ctx.pool (); nano::block_builder builder; size_t const total = 100; diff --git a/nano/test_common/ledger.cpp b/nano/test_common/ledger.cpp index a4c91533d..3f3f21b65 100644 --- a/nano/test_common/ledger.cpp +++ b/nano/test_common/ledger.cpp @@ -6,7 +6,8 @@ nano::test::context::ledger_context::ledger_context (std::deque> && blocks) : store_m{ nano::make_store (logger, nano::unique_path (), nano::dev::constants) }, ledger_m{ *store_m, stats_m, nano::dev::constants }, - blocks_m{ blocks } + blocks_m{ blocks }, + pool_m{ nano::dev::network_params.network, 1 } { debug_assert (!store_m->init_error ()); auto tx = store_m->tx_begin_write (); @@ -38,6 +39,11 @@ std::deque> const & nano::test::context::ledger_con return blocks_m; } +nano::work_pool & nano::test::context::ledger_context::pool () +{ + return pool_m; +} + auto nano::test::context::ledger_empty () -> ledger_context { return ledger_context{}; diff --git a/nano/test_common/ledger.hpp b/nano/test_common/ledger.hpp index 6b12c258a..6c81f46bf 100644 --- a/nano/test_common/ledger.hpp +++ b/nano/test_common/ledger.hpp @@ -23,6 +23,7 @@ namespace test nano::store::component & store (); nano::stats & stats (); std::deque> const & blocks () const; + nano::work_pool & pool (); private: nano::logger logger; @@ -30,6 +31,7 @@ namespace test nano::stats stats_m; nano::ledger ledger_m; std::deque> blocks_m; + nano::work_pool pool_m; }; /** Only a genesis block */ From 58911217ab565ffa0602ac4d445e60f1fa6474b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20W=C3=B3jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Sun, 17 Mar 2024 10:07:29 +0100 Subject: [PATCH 027/128] Election vote result enum (#4497) * Remove unused classes * Election vote result enum --- nano/node/CMakeLists.txt | 4 --- nano/node/active_transactions.cpp | 11 ++++--- nano/node/election.cpp | 23 +++++-------- nano/node/election.hpp | 18 +++++----- nano/node/inactive_cache_information.cpp | 32 ------------------ nano/node/inactive_cache_information.hpp | 42 ------------------------ nano/node/inactive_cache_status.cpp | 19 ----------- nano/node/inactive_cache_status.hpp | 26 --------------- nano/node/vote_cache.cpp | 4 +-- 9 files changed, 25 insertions(+), 154 deletions(-) delete mode 100644 nano/node/inactive_cache_information.cpp delete mode 100644 nano/node/inactive_cache_information.hpp delete mode 100644 nano/node/inactive_cache_status.cpp delete mode 100644 nano/node/inactive_cache_status.hpp diff --git a/nano/node/CMakeLists.txt b/nano/node/CMakeLists.txt index f8f3b5407..9602dc0a2 100644 --- a/nano/node/CMakeLists.txt +++ b/nano/node/CMakeLists.txt @@ -78,10 +78,6 @@ add_library( election_insertion_result.hpp epoch_upgrader.hpp epoch_upgrader.cpp - inactive_cache_information.hpp - inactive_cache_information.cpp - inactive_cache_status.hpp - inactive_cache_status.cpp ipc/action_handler.hpp ipc/action_handler.cpp ipc/flatbuffers_handler.hpp diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index be7910775..4afc45e2a 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -539,13 +539,14 @@ nano::vote_code nano::active_transactions::vote (std::shared_ptr con if (!process.empty ()) { - bool replay (false); - bool processed (false); + bool replay = false; + bool processed = false; + for (auto const & [election, block_hash] : process) { - auto const result_l = election->vote (vote_a->account, vote_a->timestamp (), block_hash); - processed = processed || result_l.processed; - replay = replay || result_l.replay; + auto const vote_result = election->vote (vote_a->account, vote_a->timestamp (), block_hash); + processed |= (vote_result == nano::election::vote_result::processed); + replay |= (vote_result == nano::election::vote_result::replay); } // Republish vote if it is new and the node does not host a principal representative (or close to) diff --git a/nano/node/election.cpp b/nano/node/election.cpp index 9dda84032..e0eebd181 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -16,12 +16,6 @@ std::chrono::milliseconds nano::election::base_latency () const return node.network_params.network.is_dev_network () ? 25ms : 1000ms; } -nano::election_vote_result::election_vote_result (bool replay_a, bool processed_a) -{ - replay = replay_a; - processed = processed_a; -} - /* * election */ @@ -460,13 +454,14 @@ std::shared_ptr nano::election::find (nano::block_hash const & hash return result; } -nano::election_vote_result nano::election::vote (nano::account const & rep, uint64_t timestamp_a, nano::block_hash const & block_hash_a, vote_source vote_source_a) +auto nano::election::vote (nano::account const & rep, uint64_t timestamp_a, nano::block_hash const & block_hash_a, vote_source vote_source_a) -> vote_result { auto weight = node.ledger.weight (rep); if (!node.network_params.network.is_dev_network () && weight <= node.minimum_principal_weight ()) { - return nano::election_vote_result (false, false); + return vote_result::ignored; } + nano::unique_lock lock{ mutex }; auto last_vote_it (last_votes.find (rep)); @@ -475,18 +470,17 @@ nano::election_vote_result nano::election::vote (nano::account const & rep, uint auto last_vote_l (last_vote_it->second); if (last_vote_l.timestamp > timestamp_a) { - return nano::election_vote_result (true, false); + return vote_result::replay; } if (last_vote_l.timestamp == timestamp_a && !(last_vote_l.hash < block_hash_a)) { - return nano::election_vote_result (true, false); + return vote_result::replay; } auto max_vote = timestamp_a == std::numeric_limits::max () && last_vote_l.timestamp < timestamp_a; bool past_cooldown = true; - // Only cooldown live votes - if (vote_source_a == vote_source::live) + if (vote_source_a == vote_source::live) // Only cooldown live votes { const auto cooldown = cooldown_time (weight); past_cooldown = last_vote_l.time <= std::chrono::steady_clock::now () - cooldown; @@ -494,7 +488,7 @@ nano::election_vote_result nano::election::vote (nano::account const & rep, uint if (!max_vote && !past_cooldown) { - return nano::election_vote_result (false, false); + return vote_result::ignored; } } @@ -519,7 +513,8 @@ nano::election_vote_result nano::election::vote (nano::account const & rep, uint { confirm_if_quorum (lock); } - return nano::election_vote_result (false, true); + + return vote_result::processed; } bool nano::election::publish (std::shared_ptr const & block_a) diff --git a/nano/node/election.hpp b/nano/node/election.hpp index 258c5655e..63e758278 100644 --- a/nano/node/election.hpp +++ b/nano/node/election.hpp @@ -34,15 +34,6 @@ public: nano::uint128_t weight; }; -class election_vote_result final -{ -public: - election_vote_result () = default; - election_vote_result (bool, bool); - bool replay{ false }; - bool processed{ false }; -}; - enum class election_behavior { normal, @@ -87,6 +78,13 @@ public: cache, }; + enum class vote_result + { + ignored, + processed, + replay, + }; + private: // Minimum time between broadcasts of the current winner of an election, as a backup to requesting confirmations std::chrono::milliseconds base_latency () const; @@ -144,7 +142,7 @@ public: // Interface * Process vote. Internally uses cooldown to throttle non-final votes * If the election reaches consensus, it will be confirmed */ - nano::election_vote_result vote (nano::account const & representative, uint64_t timestamp, nano::block_hash const & block_hash, vote_source = vote_source::live); + vote_result vote (nano::account const & representative, uint64_t timestamp, nano::block_hash const & block_hash, vote_source = vote_source::live); bool publish (std::shared_ptr const & block_a); // Confirm this block if quorum is met void confirm_if_quorum (nano::unique_lock &); diff --git a/nano/node/inactive_cache_information.cpp b/nano/node/inactive_cache_information.cpp deleted file mode 100644 index 2207c8388..000000000 --- a/nano/node/inactive_cache_information.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include -#include - -using namespace std::chrono; - -std::string nano::inactive_cache_information::to_string () const -{ - std::stringstream ss; - ss << "hash=" << hash.to_string (); - ss << ", arrival=" << std::chrono::duration_cast (arrival.time_since_epoch ()).count (); - ss << ", " << status.to_string (); - ss << ", " << voters.size () << " voters"; - for (auto const & [rep, timestamp] : voters) - { - ss << " " << rep.to_account () << "/" << timestamp; - } - return ss.str (); -} - -std::size_t nano::inactive_cache_information::fill (std::shared_ptr election) const -{ - std::size_t inserted = 0; - for (auto const & [rep, timestamp] : voters) - { - auto [is_replay, processed] = election->vote (rep, timestamp, hash, nano::election::vote_source::cache); - if (processed) - { - inserted++; - } - } - return inserted; -} \ No newline at end of file diff --git a/nano/node/inactive_cache_information.hpp b/nano/node/inactive_cache_information.hpp deleted file mode 100644 index 557a38e0b..000000000 --- a/nano/node/inactive_cache_information.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace nano -{ -class inactive_cache_information final -{ -public: - inactive_cache_information () = default; - inactive_cache_information (std::chrono::steady_clock::time_point arrival, nano::block_hash hash, nano::account initial_rep_a, uint64_t initial_timestamp_a, nano::inactive_cache_status status) : - arrival (arrival), - hash (hash), - status (status) - { - voters.reserve (8); - voters.emplace_back (initial_rep_a, initial_timestamp_a); - } - - std::chrono::steady_clock::time_point arrival; - nano::block_hash hash; - nano::inactive_cache_status status; - std::vector> voters; - - bool needs_eval () const - { - return !status.bootstrap_started || !status.election_started || !status.confirmed; - } - - std::string to_string () const; - - /** - * Inserts votes stored in this entry into an election - * @return number of votes inserted - */ - std::size_t fill (std::shared_ptr election) const; -}; - -} diff --git a/nano/node/inactive_cache_status.cpp b/nano/node/inactive_cache_status.cpp deleted file mode 100644 index 2ec4bb639..000000000 --- a/nano/node/inactive_cache_status.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include - -bool nano::inactive_cache_status::operator!= (inactive_cache_status const other) const -{ - return bootstrap_started != other.bootstrap_started - || election_started != other.election_started - || confirmed != other.confirmed - || tally != other.tally; -} - -std::string nano::inactive_cache_status::to_string () const -{ - std::stringstream ss; - ss << "bootstrap_started=" << bootstrap_started; - ss << ", election_started=" << election_started; - ss << ", confirmed=" << confirmed; - ss << ", tally=" << nano::uint128_union (tally).to_string (); - return ss.str (); -} diff --git a/nano/node/inactive_cache_status.hpp b/nano/node/inactive_cache_status.hpp deleted file mode 100644 index bbd0ffafc..000000000 --- a/nano/node/inactive_cache_status.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include - -namespace nano -{ -class inactive_cache_status final -{ -public: - bool bootstrap_started{ false }; - - /** Did item reach config threshold to start an impromptu election? */ - bool election_started{ false }; - - /** Did item reach votes quorum? (minimum config value) */ - bool confirmed{ false }; - - /** Last votes tally for block */ - nano::uint128_t tally{ 0 }; - - bool operator!= (inactive_cache_status const other) const; - - std::string to_string () const; -}; - -} diff --git a/nano/node/vote_cache.cpp b/nano/node/vote_cache.cpp index 78c6fa86f..4d40807d2 100644 --- a/nano/node/vote_cache.cpp +++ b/nano/node/vote_cache.cpp @@ -68,8 +68,8 @@ std::size_t nano::vote_cache::entry::fill (std::shared_ptr const std::size_t inserted = 0; for (const auto & entry : voters_m) { - auto [is_replay, processed] = election->vote (entry.representative, entry.timestamp, hash_m, nano::election::vote_source::cache); - if (processed) + auto result = election->vote (entry.representative, entry.timestamp, hash_m, nano::election::vote_source::cache); + if (result == nano::election::vote_result::processed) { inserted++; } From 9b38bb2eaf4cc315c5ca19bf07b4d1c98b9b1f2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20W=C3=B3jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Mon, 18 Mar 2024 15:09:14 +0100 Subject: [PATCH 028/128] Networking cleanup continued (#4495) * Organize * Renamings * Dedicated thread for tcp keepalives * Rename 'reachout' to `track_reachout` * Dedicated network reachout thread * Merge tcp channels and network keepalive loops * Unused * Random number generator helper * Properly close channel container * Rework channel purging * Reverse track_reachout return value * Rework `add_initial_peers` * Stop network component last --- nano/core_test/network.cpp | 10 +- nano/core_test/peer_container.cpp | 11 +- nano/core_test/socket.cpp | 4 +- nano/lib/logging_enums.hpp | 1 + nano/lib/random.hpp | 31 ++++ nano/lib/stats_enums.hpp | 7 +- nano/lib/thread_roles.cpp | 3 + nano/lib/thread_roles.hpp | 1 + nano/node/network.cpp | 68 +++++-- nano/node/network.hpp | 8 +- nano/node/node.cpp | 20 +- nano/node/transport/channel.hpp | 3 + nano/node/transport/fake.hpp | 2 +- nano/node/transport/inproc.hpp | 5 + nano/node/transport/tcp.cpp | 284 ++++++++++++++--------------- nano/node/transport/tcp.hpp | 144 ++++++++------- nano/node/transport/tcp_server.cpp | 8 + nano/node/transport/tcp_server.hpp | 7 +- 18 files changed, 360 insertions(+), 257 deletions(-) create mode 100644 nano/lib/random.hpp diff --git a/nano/core_test/network.cpp b/nano/core_test/network.cpp index bf9ed2e65..cf5188bc5 100644 --- a/nano/core_test/network.cpp +++ b/nano/core_test/network.cpp @@ -759,7 +759,7 @@ TEST (network, peer_max_tcp_attempts) node->network.merge_peer (node2->network.endpoint ()); } ASSERT_EQ (0, node->network.size ()); - ASSERT_TRUE (node->network.tcp_channels.reachout (nano::endpoint (node->network.endpoint ().address (), system.get_available_port ()))); + ASSERT_FALSE (node->network.tcp_channels.track_reachout (nano::endpoint (node->network.endpoint ().address (), system.get_available_port ()))); ASSERT_EQ (1, node->stats.count (nano::stat::type::tcp, nano::stat::detail::tcp_max_per_ip, nano::stat::dir::out)); } #endif @@ -779,11 +779,11 @@ namespace transport { auto address (boost::asio::ip::address_v6::v4_mapped (boost::asio::ip::address_v4 (0x7f000001 + i))); // 127.0.0.1 hex nano::endpoint endpoint (address, system.get_available_port ()); - ASSERT_FALSE (node->network.tcp_channels.reachout (endpoint)); + ASSERT_TRUE (node->network.tcp_channels.track_reachout (endpoint)); } ASSERT_EQ (0, node->network.size ()); ASSERT_EQ (0, node->stats.count (nano::stat::type::tcp, nano::stat::detail::tcp_max_per_subnetwork, nano::stat::dir::out)); - ASSERT_TRUE (node->network.tcp_channels.reachout (nano::endpoint (boost::asio::ip::make_address_v6 ("::ffff:127.0.0.1"), system.get_available_port ()))); + ASSERT_FALSE (node->network.tcp_channels.track_reachout (nano::endpoint (boost::asio::ip::make_address_v6 ("::ffff:127.0.0.1"), system.get_available_port ()))); ASSERT_EQ (1, node->stats.count (nano::stat::type::tcp, nano::stat::detail::tcp_max_per_subnetwork, nano::stat::dir::out)); } } @@ -974,7 +974,7 @@ TEST (network, tcp_no_connect_excluded_peers) ASSERT_EQ (nullptr, node0->network.find_node_id (node1->get_node_id ())); // Should not actively reachout to excluded peers - ASSERT_TRUE (node0->network.reachout (node1->network.endpoint (), true)); + ASSERT_FALSE (node0->network.track_reachout (node1->network.endpoint ())); // Erasing from excluded peers should allow a connection node0->network.excluded_peers.remove (endpoint1_tcp); @@ -1080,7 +1080,7 @@ TEST (network, cleanup_purge) ASSERT_EQ (1, node1.network.size ()); node1.network.cleanup (std::chrono::steady_clock::now ()); - ASSERT_EQ (0, node1.network.size ()); + ASSERT_TIMELY_EQ (5s, 0, node1.network.size ()); } TEST (network, loopback_channel) diff --git a/nano/core_test/peer_container.cpp b/nano/core_test/peer_container.cpp index 6518cca74..c0f04d9d9 100644 --- a/nano/core_test/peer_container.cpp +++ b/nano/core_test/peer_container.cpp @@ -217,18 +217,19 @@ TEST (peer_container, reachout) auto outer_node1 = nano::test::add_outer_node (system); ASSERT_NE (nullptr, nano::test::establish_tcp (system, node1, outer_node1->network.endpoint ())); // Make sure having been contacted by them already indicates we shouldn't reach out - ASSERT_TRUE (node1.network.reachout (outer_node1->network.endpoint ())); + ASSERT_FALSE (node1.network.track_reachout (outer_node1->network.endpoint ())); auto outer_node2 = nano::test::add_outer_node (system); - ASSERT_FALSE (node1.network.reachout (outer_node2->network.endpoint ())); + ASSERT_TRUE (node1.network.track_reachout (outer_node2->network.endpoint ())); ASSERT_NE (nullptr, nano::test::establish_tcp (system, node1, outer_node2->network.endpoint ())); // Reaching out to them once should signal we shouldn't reach out again. - ASSERT_TRUE (node1.network.reachout (outer_node2->network.endpoint ())); + ASSERT_FALSE (node1.network.track_reachout (outer_node2->network.endpoint ())); // Make sure we don't purge new items node1.network.cleanup (std::chrono::steady_clock::now () - std::chrono::seconds (10)); - ASSERT_TRUE (node1.network.reachout (outer_node2->network.endpoint ())); + ASSERT_FALSE (node1.network.track_reachout (outer_node2->network.endpoint ())); // Make sure we purge old items node1.network.cleanup (std::chrono::steady_clock::now () + std::chrono::seconds (10)); - ASSERT_FALSE (node1.network.reachout (outer_node2->network.endpoint ())); + ASSERT_TIMELY (5s, node1.network.empty ()); + ASSERT_TRUE (node1.network.track_reachout (outer_node2->network.endpoint ())); } // This test is similar to network.filter_invalid_version_using with the difference that diff --git a/nano/core_test/socket.cpp b/nano/core_test/socket.cpp index eaa31f761..1ec356391 100644 --- a/nano/core_test/socket.cpp +++ b/nano/core_test/socket.cpp @@ -420,7 +420,7 @@ TEST (socket, drop_policy) }); auto client = std::make_shared (*node); - nano::transport::channel_tcp channel{ *node, client }; + auto channel = std::make_shared (*node, client); nano::test::counted_completion write_completion (static_cast (total_message_count)); client->async_connect (boost::asio::ip::tcp::endpoint (boost::asio::ip::address_v6::loopback (), listener->endpoint ().port ()), @@ -428,7 +428,7 @@ TEST (socket, drop_policy) for (int i = 0; i < total_message_count; i++) { std::vector buff (1); - channel.send_buffer ( + channel->send_buffer ( nano::shared_const_buffer (std::move (buff)), [&write_completion, client] (boost::system::error_code const & ec, size_t size_a) mutable { client.reset (); write_completion.increment (); diff --git a/nano/lib/logging_enums.hpp b/nano/lib/logging_enums.hpp index 822f73621..a5782edd7 100644 --- a/nano/lib/logging_enums.hpp +++ b/nano/lib/logging_enums.hpp @@ -56,6 +56,7 @@ enum class type tcp, tcp_server, tcp_listener, + tcp_channels, prunning, conf_processor_bounded, conf_processor_unbounded, diff --git a/nano/lib/random.hpp b/nano/lib/random.hpp new file mode 100644 index 000000000..f2f35a7e2 --- /dev/null +++ b/nano/lib/random.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +namespace nano +{ +/** + * Not safe for any crypto related code, use for non-crypto PRNG only. + */ +class random_generator final +{ +public: + /// Generate a random number in the range [min, max) + auto random (auto min, auto max) + { + release_assert (min < max); + std::uniform_int_distribution dist (min, max - 1); + return dist (rng); + } + + /// Generate a random number in the range [0, max) + auto random (auto max) + { + return random (decltype (max){ 0 }, max); + } + +private: + std::random_device device; + std::default_random_engine rng{ device () }; +}; +} \ No newline at end of file diff --git a/nano/lib/stats_enums.hpp b/nano/lib/stats_enums.hpp index 68679b2c5..de7b6d020 100644 --- a/nano/lib/stats_enums.hpp +++ b/nano/lib/stats_enums.hpp @@ -24,6 +24,7 @@ enum class type : uint8_t http_callback, ipc, tcp, + tcp_channels, channel, socket, confirmation_height, @@ -70,7 +71,6 @@ enum class detail : uint8_t ok, loop, loop_cleanup, - loop_keepalive, total, process, processed, @@ -216,6 +216,11 @@ enum class detail : uint8_t message_size_too_big, outdated_version, + // network + loop_keepalive, + loop_reachout, + merge_peer, + // tcp tcp_accept_success, tcp_accept_failure, diff --git a/nano/lib/thread_roles.cpp b/nano/lib/thread_roles.cpp index 6c66748d6..7c75351c4 100644 --- a/nano/lib/thread_roles.cpp +++ b/nano/lib/thread_roles.cpp @@ -115,6 +115,9 @@ std::string nano::thread_role::get_string (nano::thread_role::name role) case nano::thread_role::name::network_keepalive: thread_role_name_string = "Net keepalive"; break; + case nano::thread_role::name::network_reachout: + thread_role_name_string = "Net reachout"; + break; default: debug_assert (false && "nano::thread_role::get_string unhandled thread role"); } diff --git a/nano/lib/thread_roles.hpp b/nano/lib/thread_roles.hpp index 8fd4175ae..a00a55226 100644 --- a/nano/lib/thread_roles.hpp +++ b/nano/lib/thread_roles.hpp @@ -47,6 +47,7 @@ enum class name rep_tiers, network_cleanup, network_keepalive, + network_reachout, }; /* diff --git a/nano/node/network.cpp b/nano/node/network.cpp index 528739133..95381bcad 100644 --- a/nano/node/network.cpp +++ b/nano/node/network.cpp @@ -48,6 +48,11 @@ void nano::network::start () run_keepalive (); }); + reachout_thread = std::thread ([this] () { + nano::thread_role::set (nano::thread_role::name::network_reachout); + run_reachout (); + }); + if (!node.flags.disable_tcp_realtime) { tcp_channels.start (); @@ -87,6 +92,10 @@ void nano::network::stop () { cleanup_thread.join (); } + if (reachout_thread.joinable ()) + { + reachout_thread.join (); + } port = 0; } @@ -126,12 +135,11 @@ void nano::network::run_cleanup () while (!stopped) { condition.wait_for (lock, node.network_params.network.is_dev_network () ? 1s : 5s); - lock.unlock (); - if (stopped) { return; } + lock.unlock (); node.stats.inc (nano::stat::type::network, nano::stat::detail::loop_cleanup); @@ -154,18 +162,54 @@ void nano::network::run_keepalive () while (!stopped) { condition.wait_for (lock, node.network_params.network.keepalive_period); - lock.unlock (); - if (stopped) { return; } + lock.unlock (); node.stats.inc (nano::stat::type::network, nano::stat::detail::loop_keepalive); flood_keepalive (0.75f); flood_keepalive_self (0.25f); + tcp_channels.keepalive (); + + lock.lock (); + } +} + +void nano::network::run_reachout () +{ + nano::unique_lock lock{ mutex }; + while (!stopped) + { + condition.wait_for (lock, node.network_params.network.merge_period); + if (stopped) + { + return; + } + lock.unlock (); + + node.stats.inc (nano::stat::type::network, nano::stat::detail::loop_reachout); + + auto keepalive = tcp_channels.sample_keepalive (); + if (keepalive) + { + for (auto const & peer : keepalive->peers) + { + if (stopped) + { + return; + } + + merge_peer (peer); + + // Throttle reachout attempts + std::this_thread::sleep_for (node.network_params.network.merge_period); + } + } + lock.lock (); } } @@ -411,10 +455,11 @@ void nano::network::merge_peers (std::array const & peers_a) void nano::network::merge_peer (nano::endpoint const & peer_a) { - if (!reachout (peer_a, node.config.allow_local_peers)) + if (track_reachout (peer_a)) { - std::weak_ptr node_w (node.shared ()); - node.network.tcp_channels.start_tcp (peer_a); + node.stats.inc (nano::stat::type::network, nano::stat::detail::merge_peer); + + tcp_channels.start_tcp (peer_a); } } @@ -436,15 +481,14 @@ bool nano::network::not_a_peer (nano::endpoint const & endpoint_a, bool allow_lo return result; } -bool nano::network::reachout (nano::endpoint const & endpoint_a, bool allow_local_peers) +bool nano::network::track_reachout (nano::endpoint const & endpoint_a) { // Don't contact invalid IPs - bool error = not_a_peer (endpoint_a, allow_local_peers); - if (!error) + if (not_a_peer (endpoint_a, node.config.allow_local_peers)) { - error = tcp_channels.reachout (endpoint_a); + return false; } - return error; + return tcp_channels.track_reachout (endpoint_a); } std::deque> nano::network::list (std::size_t count_a, uint8_t minimum_version_a, bool include_tcp_temporary_channels_a) diff --git a/nano/node/network.hpp b/nano/node/network.hpp index d51aedaa4..c10ebc5cc 100644 --- a/nano/node/network.hpp +++ b/nano/node/network.hpp @@ -78,9 +78,9 @@ public: void send_keepalive_self (std::shared_ptr const &); std::shared_ptr find_node_id (nano::account const &); std::shared_ptr find_channel (nano::endpoint const &); - bool not_a_peer (nano::endpoint const &, bool); - // Should we reach out to this endpoint with a keepalive message - bool reachout (nano::endpoint const &, bool = false); + bool not_a_peer (nano::endpoint const &, bool allow_local_peers); + // Should we reach out to this endpoint with a keepalive message? If yes, register a new reachout attempt + bool track_reachout (nano::endpoint const &); std::deque> list (std::size_t max_count = 0, uint8_t = 0, bool = true); std::deque> list_non_pr (std::size_t); // Desired fanout for a given scale @@ -111,6 +111,7 @@ private: void run_processing (); void run_cleanup (); void run_keepalive (); + void run_reachout (); void process_message (nano::message const &, std::shared_ptr const &); private: // Dependencies @@ -137,6 +138,7 @@ private: std::vector processing_threads; // Using boost::thread to enable increased stack size std::thread cleanup_thread; std::thread keepalive_thread; + std::thread reachout_thread; public: static unsigned const broadcast_interval_ms = 10; diff --git a/nano/node/node.cpp b/nano/node/node.cpp index b0de6bc93..054e3669f 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -684,7 +684,6 @@ void nano::node::stop () generator.stop (); final_generator.stop (); confirmation_height_processor.stop (); - network.stop (); telemetry.stop (); websocket.stop (); bootstrap_server.stop (); @@ -696,6 +695,8 @@ void nano::node::stop () epoch_upgrader.stop (); workers.stop (); local_block_broadcaster.stop (); + network.stop (); // Stop network last to avoid killing in-use sockets + // work pool is not stopped on purpose due to testing setup } @@ -1116,15 +1117,22 @@ void nano::node::add_initial_peers () return; } - auto transaction (store.tx_begin_read ()); - for (auto i (store.peer.begin (transaction)), n (store.peer.end ()); i != n; ++i) + std::vector initial_peers; { - nano::endpoint endpoint (boost::asio::ip::address_v6 (i->first.address_bytes ()), i->first.port ()); - if (!network.reachout (endpoint, config.allow_local_peers)) + auto transaction = store.tx_begin_read (); + for (auto i (store.peer.begin (transaction)), n (store.peer.end ()); i != n; ++i) { - network.tcp_channels.start_tcp (endpoint); + nano::endpoint endpoint (boost::asio::ip::address_v6 (i->first.address_bytes ()), i->first.port ()); + initial_peers.push_back (endpoint); } } + + logger.info (nano::log::type::node, "Adding cached initial peers: {}", initial_peers.size ()); + + for (auto const & peer : initial_peers) + { + network.merge_peer (peer); + } } void nano::node::start_election (std::shared_ptr const & block) diff --git a/nano/node/transport/channel.hpp b/nano/node/transport/channel.hpp index bede756ce..35f8f37ba 100644 --- a/nano/node/transport/channel.hpp +++ b/nano/node/transport/channel.hpp @@ -41,6 +41,8 @@ public: nano::transport::traffic_type = nano::transport::traffic_type::generic) = 0; + virtual void close () = 0; + virtual std::string to_string () const = 0; virtual nano::endpoint get_endpoint () const = 0; virtual nano::tcp_endpoint get_tcp_endpoint () const = 0; @@ -50,6 +52,7 @@ public: { return false; } + virtual bool alive () const { return true; diff --git a/nano/node/transport/fake.hpp b/nano/node/transport/fake.hpp index 809c5b98a..5c720158b 100644 --- a/nano/node/transport/fake.hpp +++ b/nano/node/transport/fake.hpp @@ -49,7 +49,7 @@ namespace transport return nano::transport::transport_type::fake; } - void close () + void close () override { closed = true; } diff --git a/nano/node/transport/inproc.hpp b/nano/node/transport/inproc.hpp index c6012bc1a..fc318ef51 100644 --- a/nano/node/transport/inproc.hpp +++ b/nano/node/transport/inproc.hpp @@ -43,6 +43,11 @@ namespace transport return nano::transport::transport_type::loopback; } + void close () override + { + // Can't be closed + } + private: nano::node & destination; nano::endpoint const endpoint; diff --git a/nano/node/transport/tcp.cpp b/nano/node/transport/tcp.cpp index 6dab45020..ac7ba411f 100644 --- a/nano/node/transport/tcp.cpp +++ b/nano/node/transport/tcp.cpp @@ -54,12 +54,12 @@ void nano::transport::channel_tcp::send_buffer (nano::shared_const_buffer const if (!socket_l->max (traffic_type) || (policy_a == nano::transport::buffer_drop_policy::no_socket_drop && !socket_l->full (traffic_type))) { socket_l->async_write ( - buffer_a, [endpoint_a = socket_l->remote_endpoint (), node = std::weak_ptr (node.shared ()), callback_a] (boost::system::error_code const & ec, std::size_t size_a) { + buffer_a, [this_s = shared_from_this (), endpoint_a = socket_l->remote_endpoint (), node = std::weak_ptr{ node.shared () }, callback_a] (boost::system::error_code const & ec, std::size_t size_a) { if (auto node_l = node.lock ()) { if (!ec) { - node_l->network.tcp_channels.update (endpoint_a); + this_s->set_last_packet_sent (std::chrono::steady_clock::now ()); } if (ec == boost::system::errc::host_unreachable) { @@ -131,6 +131,48 @@ nano::transport::tcp_channels::tcp_channels (nano::node & node, std::function lock{ mutex }; + stopped = true; + } + condition.notify_all (); + + message_manager.stop (); + + close (); +} + +void nano::transport::tcp_channels::close () +{ + nano::lock_guard lock{ mutex }; + + for (auto const & channel : channels) + { + if (channel.socket) + { + channel.socket->close (); + } + // Remove response server + if (channel.response_server) + { + channel.response_server->stop (); + } + } + + channels.clear (); +} + bool nano::transport::tcp_channels::insert (std::shared_ptr const & channel_a, std::shared_ptr const & socket_a, std::shared_ptr const & server_a) { auto endpoint (channel_a->get_tcp_endpoint ()); @@ -189,15 +231,13 @@ std::unordered_set> nano::transport::t nano::lock_guard lock{ mutex }; // Stop trying to fill result with random samples after this many attempts auto random_cutoff (count_a * 2); - auto peers_size (channels.size ()); // Usually count_a will be much smaller than peers.size() // Otherwise make sure we have a cutoff on attempting to randomly fill if (!channels.empty ()) { for (auto i (0); i < random_cutoff && result.size () < count_a; ++i) { - auto index (nano::random_pool::generate_word32 (0, static_cast (peers_size - 1))); - + auto index = rng.random (channels.size ()); auto channel = channels.get ()[index].channel; if (!channel->alive ()) { @@ -279,7 +319,7 @@ nano::tcp_endpoint nano::transport::tcp_channels::bootstrap_peer () if (i->channel->get_network_version () >= node.network_params.network.protocol_version_min) { result = nano::transport::map_endpoint_to_tcp (i->channel->get_peering_endpoint ()); - channels.get ().modify (i, [] (channel_tcp_wrapper & wrapper_a) { + channels.get ().modify (i, [] (channel_entry & wrapper_a) { wrapper_a.channel->set_last_bootstrap_attempt (std::chrono::steady_clock::now ()); }); i = n; @@ -355,35 +395,6 @@ void nano::transport::tcp_channels::process_message (nano::message const & messa } } -void nano::transport::tcp_channels::start () -{ - ongoing_keepalive (); - ongoing_merge (0); -} - -void nano::transport::tcp_channels::stop () -{ - stopped = true; - nano::unique_lock lock{ mutex }; - - message_manager.stop (); - - // Close all TCP sockets - for (auto const & channel : channels) - { - if (channel.socket) - { - channel.socket->close (); - } - // Remove response server - if (channel.response_server) - { - channel.response_server->stop (); - } - } - channels.clear (); -} - bool nano::transport::tcp_channels::max_ip_connections (nano::tcp_endpoint const & endpoint_a) { if (node.flags.disable_max_peers_per_ip) @@ -431,20 +442,33 @@ bool nano::transport::tcp_channels::max_ip_or_subnetwork_connections (nano::tcp_ return max_ip_connections (endpoint_a) || max_subnetwork_connections (endpoint_a); } -bool nano::transport::tcp_channels::reachout (nano::endpoint const & endpoint_a) +bool nano::transport::tcp_channels::track_reachout (nano::endpoint const & endpoint_a) { - auto tcp_endpoint (nano::transport::map_endpoint_to_tcp (endpoint_a)); + auto const tcp_endpoint = nano::transport::map_endpoint_to_tcp (endpoint_a); + // Don't overload single IP - bool error = node.network.excluded_peers.check (tcp_endpoint) || max_ip_or_subnetwork_connections (tcp_endpoint); - if (!error && !node.flags.disable_tcp_realtime) + if (max_ip_or_subnetwork_connections (tcp_endpoint)) { - // Don't keepalive to nodes that already sent us something - error |= find_channel (tcp_endpoint) != nullptr; - nano::lock_guard lock{ mutex }; - auto inserted (attempts.emplace (tcp_endpoint)); - error |= !inserted.second; + return false; } - return error; + if (node.network.excluded_peers.check (tcp_endpoint)) + { + return false; + } + if (node.flags.disable_tcp_realtime) + { + return false; + } + + // Don't keepalive to nodes that already sent us something + if (find_channel (tcp_endpoint) != nullptr) + { + return false; + } + + nano::lock_guard lock{ mutex }; + auto [it, inserted] = attempts.emplace (tcp_endpoint); + return inserted; } std::unique_ptr nano::transport::tcp_channels::collect_container_info (std::string const & name) @@ -464,125 +488,99 @@ std::unique_ptr nano::transport::tcp_channels::c return composite; } -void nano::transport::tcp_channels::purge (std::chrono::steady_clock::time_point const & cutoff_a) +void nano::transport::tcp_channels::purge (std::chrono::steady_clock::time_point cutoff_deadline) { nano::lock_guard lock{ mutex }; - // Remove channels with dead underlying sockets - erase_if (channels, [] (auto const & entry) { - return !entry.channel->alive (); + node.logger.debug (nano::log::type::tcp_channels, "Performing periodic channel cleanup, cutoff: {}s", nano::log::seconds_delta (cutoff_deadline)); + + auto should_close = [this, cutoff_deadline] (auto const & channel) { + // Remove channels that haven't successfully sent a message within the cutoff time + if (auto last = channel->get_last_packet_sent (); last < cutoff_deadline) + { + node.logger.debug (nano::log::type::tcp_channels, "Closing idle channel: {} (idle for {}s)", + channel->to_string (), + nano::log::seconds_delta (last)); + + return true; // Close + } + // Check if any tcp channels belonging to old protocol versions which may still be alive due to async operations + if (channel->get_network_version () < node.network_params.network.protocol_version_min) + { + node.logger.debug (nano::log::type::tcp_channels, "Closing channel with old protocol version: {}", channel->to_string ()); + + return true; // Close + } + return false; + }; + + for (auto const & entry : channels) + { + if (should_close (entry.channel)) + { + entry.channel->close (); + } + } + + erase_if (channels, [this] (auto const & entry) { + if (!entry.channel->alive ()) + { + node.logger.debug (nano::log::type::tcp_channels, "Removing dead channel: {}", entry.channel->to_string ()); + return true; // Erase + } + return false; }); - auto disconnect_cutoff (channels.get ().lower_bound (cutoff_a)); - channels.get ().erase (channels.get ().begin (), disconnect_cutoff); - // Remove keepalive attempt tracking for attempts older than cutoff - auto attempts_cutoff (attempts.get ().lower_bound (cutoff_a)); + auto attempts_cutoff (attempts.get ().lower_bound (cutoff_deadline)); attempts.get ().erase (attempts.get ().begin (), attempts_cutoff); - - // Check if any tcp channels belonging to old protocol versions which may still be alive due to async operations - auto lower_bound = channels.get ().lower_bound (node.network_params.network.protocol_version_min); - channels.get ().erase (channels.get ().begin (), lower_bound); } -void nano::transport::tcp_channels::ongoing_keepalive () +void nano::transport::tcp_channels::keepalive () { nano::keepalive message{ node.network_params.network }; node.network.random_fill (message.peers); + nano::unique_lock lock{ mutex }; + + auto const cutoff_time = std::chrono::steady_clock::now () - node.network_params.network.keepalive_period; + // Wake up channels - std::vector> send_list; - auto keepalive_sent_cutoff (channels.get ().lower_bound (std::chrono::steady_clock::now () - node.network_params.network.keepalive_period)); - for (auto i (channels.get ().begin ()); i != keepalive_sent_cutoff; ++i) + std::vector> to_wakeup; + for (auto const & entry : channels) { - send_list.push_back (i->channel); + if (entry.channel->get_last_packet_sent () < cutoff_time) + { + to_wakeup.push_back (entry.channel); + } } + lock.unlock (); - for (auto & channel : send_list) + + for (auto & channel : to_wakeup) { channel->send (message); } - std::weak_ptr node_w (node.shared ()); - node.workers.add_timed_task (std::chrono::steady_clock::now () + node.network_params.network.keepalive_period, [node_w] () { - if (auto node_l = node_w.lock ()) - { - if (!node_l->network.tcp_channels.stopped) - { - node_l->network.tcp_channels.ongoing_keepalive (); - } - } - }); } -void nano::transport::tcp_channels::ongoing_merge (size_t channel_index) +std::optional nano::transport::tcp_channels::sample_keepalive () { - nano::unique_lock lock{ mutex }; - std::optional keepalive; - size_t count = 0; - while (!keepalive && channels.size () > 0 && count++ < channels.size ()) - { - ++channel_index; - if (channels.size () <= channel_index) - { - channel_index = 0; - } - auto server = channels.get ()[channel_index].response_server; - if (server && server->last_keepalive) - { - keepalive = std::move (server->last_keepalive); - server->last_keepalive = std::nullopt; - } - } - lock.unlock (); - if (keepalive) - { - ongoing_merge (channel_index, *keepalive, 1); - } - else - { - std::weak_ptr node_w = node.shared (); - node.workers.add_timed_task (std::chrono::steady_clock::now () + node.network_params.network.merge_period, [node_w, channel_index] () { - if (auto node_l = node_w.lock ()) - { - if (!node_l->network.tcp_channels.stopped) - { - node_l->network.tcp_channels.ongoing_merge (channel_index); - } - } - }); - } -} + nano::lock_guard lock{ mutex }; -void nano::transport::tcp_channels::ongoing_merge (size_t channel_index, nano::keepalive keepalive, size_t peer_index) -{ - debug_assert (peer_index < keepalive.peers.size ()); - node.network.merge_peer (keepalive.peers[peer_index++]); - if (peer_index < keepalive.peers.size ()) + size_t counter = 0; + while (counter++ < channels.size ()) { - std::weak_ptr node_w = node.shared (); - node.workers.add_timed_task (std::chrono::steady_clock::now () + node.network_params.network.merge_period, [node_w, channel_index, keepalive, peer_index] () { - if (auto node_l = node_w.lock ()) + auto index = rng.random (channels.size ()); + if (auto server = channels.get ()[index].response_server) + { + if (auto keepalive = server->pop_last_keepalive ()) { - if (!node_l->network.tcp_channels.stopped) - { - node_l->network.tcp_channels.ongoing_merge (channel_index, keepalive, peer_index); - } + return keepalive; } - }); - } - else - { - std::weak_ptr node_w = node.shared (); - node.workers.add_timed_task (std::chrono::steady_clock::now () + node.network_params.network.merge_period, [node_w, channel_index] () { - if (auto node_l = node_w.lock ()) - { - if (!node_l->network.tcp_channels.stopped) - { - node_l->network.tcp_channels.ongoing_merge (channel_index); - } - } - }); + } } + + return std::nullopt; } void nano::transport::tcp_channels::list (std::deque> & deque_a, uint8_t minimum_version_a, bool include_temporary_channels_a) @@ -601,24 +599,12 @@ void nano::transport::tcp_channels::modify (std::shared_ptr ().find (channel_a->get_tcp_endpoint ())); if (existing != channels.get ().end ()) { - channels.get ().modify (existing, [modify_callback = std::move (modify_callback_a)] (channel_tcp_wrapper & wrapper_a) { + channels.get ().modify (existing, [modify_callback = std::move (modify_callback_a)] (channel_entry & wrapper_a) { modify_callback (wrapper_a.channel); }); } } -void nano::transport::tcp_channels::update (nano::tcp_endpoint const & endpoint_a) -{ - nano::lock_guard lock{ mutex }; - auto existing (channels.get ().find (endpoint_a)); - if (existing != channels.get ().end ()) - { - channels.get ().modify (existing, [] (channel_tcp_wrapper & wrapper_a) { - wrapper_a.channel->set_last_packet_sent (std::chrono::steady_clock::now ()); - }); - } -} - void nano::transport::tcp_channels::start_tcp (nano::endpoint const & endpoint_a) { auto socket = std::make_shared (node); diff --git a/nano/node/transport/tcp.hpp b/nano/node/transport/tcp.hpp index fd588eb45..0402b915a 100644 --- a/nano/node/transport/tcp.hpp +++ b/nano/node/transport/tcp.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -11,6 +12,8 @@ #include #include +#include +#include #include namespace mi = boost::multi_index; @@ -52,7 +55,7 @@ namespace transport class tcp_server; class tcp_channels; - class channel_tcp : public nano::transport::channel + class channel_tcp : public nano::transport::channel, public std::enable_shared_from_this { friend class nano::transport::tcp_channels; @@ -71,10 +74,6 @@ namespace transport { return &node == &other_a.node && socket.lock () == other_a.socket.lock (); } - std::weak_ptr socket; - /* Mark for temporary channels. Usually remote ports of these channels are ephemeral and received from incoming connections to server. - If remote part has open listening port, temporary channel will be replaced with direct connection to listening port soon. But if other side is behing NAT or firewall this connection can be pemanent. */ - std::atomic temporary{ false }; void set_endpoint (); @@ -94,7 +93,7 @@ namespace transport return nano::transport::transport_type::tcp; } - virtual bool max (nano::transport::traffic_type traffic_type) override + bool max (nano::transport::traffic_type traffic_type) override { bool result = true; if (auto socket_l = socket.lock ()) @@ -104,7 +103,7 @@ namespace transport return result; } - virtual bool alive () const override + bool alive () const override { if (auto socket_l = socket.lock ()) { @@ -113,6 +112,21 @@ namespace transport return false; } + void close () override + { + if (auto socket_l = socket.lock ()) + { + socket_l->close (); + } + } + + public: + std::weak_ptr socket; + + /* Mark for temporary channels. Usually remote ports of these channels are ephemeral and received from incoming connections to server. + If remote part has open listening port, temporary channel will be replaced with direct connection to listening port soon. But if other side is behing NAT or firewall this connection can be pemanent. */ + std::atomic temporary{ false }; + private: nano::tcp_endpoint endpoint{ boost::asio::ip::address_v6::any (), 0 }; @@ -124,9 +138,15 @@ namespace transport { friend class nano::transport::channel_tcp; friend class telemetry_simultaneous_requests_Test; + friend class network_peer_max_tcp_attempts_subnetwork_Test; public: - explicit tcp_channels (nano::node &, std::function const &)> = nullptr); + explicit tcp_channels (nano::node &, std::function const &)> sink = nullptr); + ~tcp_channels (); + + void start (); + void stop (); + bool insert (std::shared_ptr const &, std::shared_ptr const &, std::shared_ptr const &); void erase (nano::tcp_endpoint const &); std::size_t size () const; @@ -137,24 +157,20 @@ namespace transport std::shared_ptr find_node_id (nano::account const &); // Get the next peer for attempting a tcp connection nano::tcp_endpoint bootstrap_peer (); - void receive (); - void start (); - void stop (); void process_messages (); void process_message (nano::message const &, nano::tcp_endpoint const &, nano::account const &, std::shared_ptr const &); bool max_ip_connections (nano::tcp_endpoint const & endpoint_a); bool max_subnetwork_connections (nano::tcp_endpoint const & endpoint_a); bool max_ip_or_subnetwork_connections (nano::tcp_endpoint const & endpoint_a); - // Should we reach out to this endpoint with a keepalive message - bool reachout (nano::endpoint const &); + // Should we reach out to this endpoint with a keepalive message? If yes, register a new reachout attempt + bool track_reachout (nano::endpoint const &); std::unique_ptr collect_container_info (std::string const &); - void purge (std::chrono::steady_clock::time_point const &); - void ongoing_keepalive (); - void ongoing_merge (size_t channel_index); - void ongoing_merge (size_t channel_index, nano::keepalive keepalive, size_t peer_index); + void purge (std::chrono::steady_clock::time_point cutoff_deadline); void list (std::deque> &, uint8_t = 0, bool = true); void modify (std::shared_ptr const &, std::function const &)>); - void update (nano::tcp_endpoint const &); + void keepalive (); + std::optional sample_keepalive (); + // Connection start void start_tcp (nano::endpoint const &); void start_tcp_receive_node_id (std::shared_ptr const &, nano::endpoint const &, std::shared_ptr> const &); @@ -166,41 +182,18 @@ namespace transport nano::tcp_message_manager message_manager; private: - class endpoint_tag - { - }; - class ip_address_tag - { - }; - class subnetwork_tag - { - }; - class random_access_tag - { - }; - class last_packet_sent_tag - { - }; - class last_bootstrap_attempt_tag - { - }; - class last_attempt_tag - { - }; - class node_id_tag - { - }; - class version_tag - { - }; + void close (); - class channel_tcp_wrapper final + private: + class channel_entry final { public: std::shared_ptr channel; std::shared_ptr socket; std::shared_ptr response_server; - channel_tcp_wrapper (std::shared_ptr channel_a, std::shared_ptr socket_a, std::shared_ptr server_a) : + + public: + channel_entry (std::shared_ptr channel_a, std::shared_ptr socket_a, std::shared_ptr server_a) : channel (std::move (channel_a)), socket (std::move (socket_a)), response_server (std::move (server_a)) { } @@ -208,10 +201,6 @@ namespace transport { return channel->get_tcp_endpoint (); } - std::chrono::steady_clock::time_point last_packet_sent () const - { - return channel->get_last_packet_sent (); - } std::chrono::steady_clock::time_point last_bootstrap_attempt () const { return channel->get_last_bootstrap_attempt (); @@ -234,7 +223,8 @@ namespace transport return channel->get_network_version (); } }; - class tcp_endpoint_attempt final + + class attempt_entry final { public: nano::tcp_endpoint endpoint; @@ -242,51 +232,65 @@ namespace transport boost::asio::ip::address subnetwork; std::chrono::steady_clock::time_point last_attempt{ std::chrono::steady_clock::now () }; - explicit tcp_endpoint_attempt (nano::tcp_endpoint const & endpoint_a) : + public: + explicit attempt_entry (nano::tcp_endpoint const & endpoint_a) : endpoint (endpoint_a), address (nano::transport::ipv4_address_or_ipv6_subnet (endpoint_a.address ())), subnetwork (nano::transport::map_address_to_subnetwork (endpoint_a.address ())) { } }; - mutable nano::mutex mutex; + // clang-format off - boost::multi_index_container>, mi::ordered_non_unique, - mi::const_mem_fun>, + mi::const_mem_fun>, mi::hashed_unique, - mi::const_mem_fun>, + mi::const_mem_fun>, mi::hashed_non_unique, - mi::const_mem_fun>, - mi::ordered_non_unique, - mi::const_mem_fun>, + mi::const_mem_fun>, mi::ordered_non_unique, - mi::const_mem_fun>, + mi::const_mem_fun>, mi::hashed_non_unique, - mi::const_mem_fun>, + mi::const_mem_fun>, mi::hashed_non_unique, - mi::const_mem_fun>>> + mi::const_mem_fun>>> channels; - boost::multi_index_container, - mi::member>, + mi::member>, mi::hashed_non_unique, - mi::member>, + mi::member>, mi::hashed_non_unique, - mi::member>, + mi::member>, mi::ordered_non_unique, - mi::member>>> + mi::member>>> attempts; // clang-format on private: std::function const &)> sink; - std::atomic stopped{ false }; - friend class network_peer_max_tcp_attempts_subnetwork_Test; + std::atomic stopped{ false }; + nano::condition_variable condition; + mutable nano::mutex mutex; + + mutable nano::random_generator rng; }; } // namespace transport } // namespace nano diff --git a/nano/node/transport/tcp_server.cpp b/nano/node/transport/tcp_server.cpp index edd7f9663..bcad6d5b0 100644 --- a/nano/node/transport/tcp_server.cpp +++ b/nano/node/transport/tcp_server.cpp @@ -796,6 +796,14 @@ void nano::transport::tcp_server::set_last_keepalive (nano::keepalive const & me } } +std::optional nano::transport::tcp_server::pop_last_keepalive () +{ + std::lock_guard lock{ mutex }; + auto result = last_keepalive; + last_keepalive = std::nullopt; + return result; +} + bool nano::transport::tcp_server::to_bootstrap_connection () { auto node = this->node.lock (); diff --git a/nano/node/transport/tcp_server.hpp b/nano/node/transport/tcp_server.hpp index 536925854..076024116 100644 --- a/nano/node/transport/tcp_server.hpp +++ b/nano/node/transport/tcp_server.hpp @@ -63,6 +63,7 @@ public: void timeout (); void set_last_keepalive (nano::keepalive const & message); + std::optional pop_last_keepalive (); std::shared_ptr const socket; std::weak_ptr const node; @@ -73,7 +74,6 @@ public: nano::tcp_endpoint remote_endpoint{ boost::asio::ip::address_v6::any (), 0 }; nano::account remote_node_id{}; std::chrono::steady_clock::time_point last_telemetry_req{}; - std::optional last_keepalive; private: void send_handshake_response (nano::node_id_handshake::query_payload const & query, bool v2); @@ -90,9 +90,10 @@ private: bool is_bootstrap_connection () const; bool is_realtime_connection () const; +private: + bool const allow_bootstrap; std::shared_ptr message_deserializer; - - bool allow_bootstrap; + std::optional last_keepalive; private: class handshake_message_visitor : public nano::message_visitor From b7888ac2652ed719b75839f605e542cf493d6950 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Sat, 16 Mar 2024 16:08:09 +0000 Subject: [PATCH 029/128] Add iterators for receivable entries to nano::ledger. --- nano/core_test/ledger.cpp | 95 ++++++++++++++++++++++++++++++++++++ nano/secure/ledger.cpp | 30 ++++++++++++ nano/secure/ledger.hpp | 10 ++++ nano/secure/pending_info.cpp | 45 +++++++++++++++++ nano/secure/pending_info.hpp | 29 +++++++++++ 5 files changed, 209 insertions(+) diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 4c021abdd..e8ef9b67a 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -5559,3 +5559,98 @@ TEST (ledger, head_block) auto tx = store.tx_begin_read (); ASSERT_EQ (*nano::dev::genesis, *ledger.head_block (tx, nano::dev::genesis_key.pub)); } + +// Test that nullopt can be returned when there are no receivable entries +TEST (ledger_receivable, upper_bound_account_none) +{ + auto ctx = nano::test::context::ledger_empty (); + ASSERT_EQ (ctx.ledger ().receivable_end (), ctx.ledger ().receivable_upper_bound (ctx.store ().tx_begin_read (), 0)); +} + +// Test behavior of ledger::receivable_upper_bound when there are receivable entries for multiple accounts +TEST (ledger_receivable, upper_bound_account_key) +{ + auto ctx = nano::test::context::ledger_empty (); + nano::block_builder builder; + nano::keypair key; + auto send1 = builder + .state () + .account (nano::dev::genesis_key.pub) + .previous (nano::dev::genesis->hash ()) + .representative (nano::dev::genesis_key.pub) + .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) + .link (key.pub) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*ctx.pool ().generate (nano::dev::genesis->hash ())) + .build (); + ASSERT_EQ (nano::block_status::progress, ctx.ledger ().process (ctx.store ().tx_begin_write (), send1)); + auto send2 = builder + .state () + .account (nano::dev::genesis_key.pub) + .previous (send1->hash ()) + .representative (nano::dev::genesis_key.pub) + .balance (nano::dev::constants.genesis_amount - 2 * nano::Gxrb_ratio) + .link (nano::dev::genesis_key.pub) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*ctx.pool ().generate (send1->hash ())) + .build (); + ASSERT_EQ (nano::block_status::progress, ctx.ledger ().process (ctx.store ().tx_begin_write (), send2)); + auto tx = ctx.store ().tx_begin_read (); + auto & ledger = ctx.ledger (); + auto next1 = ledger.receivable_upper_bound (tx, nano::dev::genesis_key.pub); + auto next2 = ledger.receivable_upper_bound (tx, key.pub); + // Depending on which is greater but only one should have a value + ASSERT_TRUE (next1 == ledger.receivable_end () xor next2 == ledger.receivable_end ()); + // The account returned should be after the one we searched for + ASSERT_TRUE (next1 == ledger.receivable_end () || next1->first.account == key.pub); + ASSERT_TRUE (next2 == ledger.receivable_end () || next2->first.account == nano::dev::genesis_key.pub); + auto next3 = ledger.receivable_upper_bound (tx, nano::dev::genesis_key.pub, 0); + auto next4 = ledger.receivable_upper_bound (tx, key.pub, 0); + // Neither account has more than one receivable + ASSERT_TRUE (next3 != ledger.receivable_end () && next4 != ledger.receivable_end ()); + auto next5 = ledger.receivable_upper_bound (tx, next3->first.account, next3->first.hash); + auto next6 = ledger.receivable_upper_bound (tx, next4->first.account, next4->first.hash); + ASSERT_TRUE (next5 == ledger.receivable_end () && next6 == ledger.receivable_end ()); + ASSERT_EQ (ledger.receivable_end (), ++next3); + ASSERT_EQ (ledger.receivable_end (), ++next4); +} + +// Test that multiple receivable entries for the same account +TEST (ledger_receivable, key_two) +{ + auto ctx = nano::test::context::ledger_empty (); + nano::block_builder builder; + nano::keypair key; + auto send1 = builder + .state () + .account (nano::dev::genesis_key.pub) + .previous (nano::dev::genesis->hash ()) + .representative (nano::dev::genesis_key.pub) + .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) + .link (key.pub) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*ctx.pool ().generate (nano::dev::genesis->hash ())) + .build (); + ASSERT_EQ (nano::block_status::progress, ctx.ledger ().process (ctx.store ().tx_begin_write (), send1)); + auto send2 = builder + .state () + .account (nano::dev::genesis_key.pub) + .previous (send1->hash ()) + .representative (nano::dev::genesis_key.pub) + .balance (nano::dev::constants.genesis_amount - 2 * nano::Gxrb_ratio) + .link (key.pub) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*ctx.pool ().generate (send1->hash ())) + .build (); + ASSERT_EQ (nano::block_status::progress, ctx.ledger ().process (ctx.store ().tx_begin_write (), send2)); + auto tx = ctx.store ().tx_begin_read (); + auto & ledger = ctx.ledger (); + auto next1 = ledger.receivable_upper_bound (tx, key.pub, 0); + ASSERT_TRUE (next1 != ledger.receivable_end () && next1->first.account == key.pub); + auto next2 = ledger.receivable_upper_bound (tx, key.pub, next1->first.hash); + ASSERT_TRUE (next2 != ledger.receivable_end () && next2->first.account == key.pub); + ASSERT_NE (next1->first.hash, next2->first.hash); + ASSERT_EQ (next2, ++next1); + ASSERT_EQ (ledger.receivable_end (), ++next1); + ASSERT_EQ (ledger.receivable_end (), ++next2); +} diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 8b412717b..cc256bdf9 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -1545,6 +1545,36 @@ uint64_t nano::ledger::height (store::transaction const & transaction, nano::blo return block_l->sideband ().height; } +std::optional> nano::ledger::receivable_lower_bound (store::transaction const & tx, nano::account const & account, nano::block_hash const & hash) const +{ + auto result = store.pending.begin (tx, { account, hash }); + if (result == store.pending.end ()) + { + return std::nullopt; + } + return *result; +} + +nano::receivable_iterator nano::ledger::receivable_end () const +{ + return nano::receivable_iterator{}; +} + +nano::receivable_iterator nano::ledger::receivable_upper_bound (store::transaction const & tx, nano::account const & account) const +{ + return receivable_iterator{ *this, tx, receivable_lower_bound (tx, account.number () + 1, 0) }; +} + +nano::receivable_iterator nano::ledger::receivable_upper_bound (store::transaction const & tx, nano::account const & account, nano::block_hash const & hash) const +{ + auto result = receivable_lower_bound (tx, account, hash.number () + 1); + if (!result || result.value ().first.account != account) + { + return nano::receivable_iterator{ *this, tx, std::nullopt }; + } + return nano::receivable_iterator{ *this, tx, result }; +} + nano::uncemented_info::uncemented_info (nano::block_hash const & cemented_frontier, nano::block_hash const & frontier, nano::account const & account) : cemented_frontier (cemented_frontier), frontier (frontier), account (account) { diff --git a/nano/secure/ledger.hpp b/nano/secure/ledger.hpp index f5009b664..8ceaedf36 100644 --- a/nano/secure/ledger.hpp +++ b/nano/secure/ledger.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -36,6 +37,8 @@ public: 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{}); /** @@ -84,6 +87,11 @@ public: static nano::epoch version (nano::block const & block); nano::epoch version (store::transaction const & transaction, nano::block_hash const & hash) const; uint64_t height (store::transaction const & transaction, nano::block_hash const & hash) const; + nano::receivable_iterator receivable_end () const; + // Returns the next receivable entry for an account greater than 'account' + nano::receivable_iterator receivable_upper_bound (store::transaction const & tx, nano::account const & account) const; + // Returns the next receivable entry for the account 'account' with hash greater than 'hash' + nano::receivable_iterator receivable_upper_bound (store::transaction const & tx, nano::account const & account, nano::block_hash const & hash) const; static nano::uint128_t const unit; nano::ledger_constants & constants; nano::store::component & store; @@ -95,6 +103,8 @@ public: bool pruning{ false }; private: + // Returns the next receivable entry equal or greater than 'key' + std::optional> receivable_lower_bound (store::transaction const & tx, nano::account const & account, nano::block_hash const & hash) const; void initialize (nano::generate_cache_flags const &); }; diff --git a/nano/secure/pending_info.cpp b/nano/secure/pending_info.cpp index 637f4adf7..61241f216 100644 --- a/nano/secure/pending_info.cpp +++ b/nano/secure/pending_info.cpp @@ -1,3 +1,4 @@ +#include #include nano::pending_info::pending_info (nano::account const & source_a, nano::amount const & amount_a, nano::epoch epoch_a) : @@ -70,3 +71,47 @@ bool nano::pending_key::operator< (nano::pending_key const & other_a) const { return account == other_a.account ? hash < other_a.hash : account < other_a.account; } + +nano::receivable_iterator::receivable_iterator (nano::ledger const & ledger, nano::store::transaction const & tx, std::optional> item) : + ledger{ &ledger }, + tx{ &tx }, + item{ item } +{ + if (item.has_value ()) + { + account = item.value ().first.account; + } +} + +bool nano::receivable_iterator::operator== (receivable_iterator const & other) const +{ + debug_assert (ledger == nullptr || other.ledger == nullptr || ledger == other.ledger); + debug_assert (tx == nullptr || other.tx == nullptr || tx == other.tx); + debug_assert (account.is_zero () || other.account.is_zero () || account == other.account); + return item == other.item; +} + +bool nano::receivable_iterator::operator!= (receivable_iterator const & other) const +{ + return !(*this == other); +} + +nano::receivable_iterator & nano::receivable_iterator::operator++ () +{ + item = ledger->receivable_lower_bound (*tx, item.value ().first.account, item.value ().first.hash.number () + 1); + if (item && item.value ().first.account != account) + { + item = std::nullopt; + } + return *this; +} + +std::pair const & nano::receivable_iterator::operator* () const +{ + return item.value (); +} + +std::pair const * nano::receivable_iterator::operator->() const +{ + return &item.value (); +} diff --git a/nano/secure/pending_info.hpp b/nano/secure/pending_info.hpp index 584337e95..0136da020 100644 --- a/nano/secure/pending_info.hpp +++ b/nano/secure/pending_info.hpp @@ -4,6 +4,16 @@ #include #include +namespace nano +{ +class ledger; +} + +namespace nano::store +{ +class transaction; +} + namespace nano { /** @@ -33,6 +43,25 @@ public: nano::account account{}; nano::block_hash hash{ 0 }; }; +// This class iterates receivable enttries for an account +class receivable_iterator +{ +public: + receivable_iterator () = default; + receivable_iterator (nano::ledger const & ledger, nano::store::transaction const & tx, std::optional> item); + bool operator== (receivable_iterator const & other) const; + bool operator!= (receivable_iterator const & other) const; + // Advances to the next receivable entry for the same account + receivable_iterator & operator++ (); + std::pair const & operator* () const; + std::pair const * operator->() const; + +private: + nano::ledger const * ledger{ nullptr }; + nano::store::transaction const * tx{ nullptr }; + nano::account account{ 0 }; + std::optional> item; +}; } // namespace nano namespace std From 994bef05d4796c530653ed7b3330b53e44c11b5c Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Sat, 16 Mar 2024 16:24:58 +0000 Subject: [PATCH 030/128] Add receivable_any function to nano::ledger which returns whether there is any receivable entry for an account. --- nano/core_test/ledger.cpp | 26 ++++++++++++++++++++++++++ nano/secure/ledger.cpp | 8 +++++++- nano/secure/ledger.hpp | 2 ++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index e8ef9b67a..9557954f6 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -5654,3 +5654,29 @@ TEST (ledger_receivable, key_two) ASSERT_EQ (ledger.receivable_end (), ++next1); ASSERT_EQ (ledger.receivable_end (), ++next2); } + +TEST (ledger_receivable, any_none) +{ + auto ctx = nano::test::context::ledger_empty (); + ASSERT_FALSE (ctx.ledger ().receivable_any (ctx.store ().tx_begin_read (), nano::dev::genesis_key.pub)); +} + +TEST (ledger_receivable, any_one) +{ + auto ctx = nano::test::context::ledger_empty (); + nano::block_builder builder; + nano::keypair key; + auto send1 = builder + .state () + .account (nano::dev::genesis_key.pub) + .previous (nano::dev::genesis->hash ()) + .representative (nano::dev::genesis_key.pub) + .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) + .link (nano::dev::genesis_key.pub) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*ctx.pool ().generate (nano::dev::genesis->hash ())) + .build (); + ASSERT_EQ (nano::block_status::progress, ctx.ledger ().process (ctx.store ().tx_begin_write (), send1)); + ASSERT_TRUE (ctx.ledger ().receivable_any (ctx.store ().tx_begin_read (), nano::dev::genesis_key.pub)); + ASSERT_FALSE (ctx.ledger ().receivable_any (ctx.store ().tx_begin_read (), key.pub)); +} diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index cc256bdf9..e539b4500 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -413,7 +413,7 @@ void ledger_processor::epoch_block_impl (nano::state_block & block_a) // Non-exisitng account should have pending entries if (result == nano::block_status::progress) { - bool pending_exists = ledger.store.pending.any (transaction, block_a.hashables.account); + bool pending_exists = ledger.receivable_any (transaction, block_a.hashables.account); result = pending_exists ? nano::block_status::progress : nano::block_status::gap_epoch_open_pending; } } @@ -1545,6 +1545,12 @@ uint64_t nano::ledger::height (store::transaction const & transaction, nano::blo return block_l->sideband ().height; } +bool nano::ledger::receivable_any (store::transaction const & tx, nano::account const & account) const +{ + auto next = receivable_upper_bound (tx, account, 0); + return next != receivable_end (); +} + std::optional> nano::ledger::receivable_lower_bound (store::transaction const & tx, nano::account const & account, nano::block_hash const & hash) const { auto result = store.pending.begin (tx, { account, hash }); diff --git a/nano/secure/ledger.hpp b/nano/secure/ledger.hpp index 8ceaedf36..6c325a029 100644 --- a/nano/secure/ledger.hpp +++ b/nano/secure/ledger.hpp @@ -87,6 +87,8 @@ public: static nano::epoch version (nano::block const & block); nano::epoch version (store::transaction const & transaction, nano::block_hash const & hash) const; uint64_t height (store::transaction const & transaction, nano::block_hash const & hash) const; + // Returns whether there are any receivable entries for 'account' + bool receivable_any (store::transaction const & tx, nano::account const & account) const; nano::receivable_iterator receivable_end () const; // Returns the next receivable entry for an account greater than 'account' nano::receivable_iterator receivable_upper_bound (store::transaction const & tx, nano::account const & account) const; From a5e150068088e3a36bd3128542cdf71518d2119d Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Mon, 18 Mar 2024 12:48:20 +0000 Subject: [PATCH 031/128] Implement ledger::account_receivable in terms of receivable iterators --- nano/secure/ledger.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index e539b4500..3df9fd9cf 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -855,19 +855,11 @@ nano::uint128_t nano::ledger::account_balance (store::transaction const & transa nano::uint128_t nano::ledger::account_receivable (store::transaction const & transaction_a, nano::account const & account_a, bool only_confirmed_a) { - nano::uint128_t result (0); - nano::account end (account_a.number () + 1); - for (auto i (store.pending.begin (transaction_a, nano::pending_key (account_a, 0))), n (store.pending.begin (transaction_a, nano::pending_key (end, 0))); i != n; ++i) + nano::uint128_t result{ 0 }; + for (auto i = receivable_upper_bound (transaction_a, account_a, 0), n = receivable_end (); i != n; ++i) { - nano::pending_info const & info (i->second); - if (only_confirmed_a) - { - if (block_confirmed (transaction_a, i->first.hash)) - { - result += info.amount.number (); - } - } - else + auto const & [key, info] = *i; + if (!only_confirmed_a || block_confirmed (transaction_a, key.hash)) { result += info.amount.number (); } From b08d6ff5228f71abed7785fdab187a6e8a5525cc Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Fri, 15 Mar 2024 23:00:15 +0000 Subject: [PATCH 032/128] Reimplement json_handler::accounts_receivable in terms of receivable iterators. --- nano/node/json_handler.cpp | 55 +++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 78eb9502f..d69e57e58 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -1041,42 +1041,41 @@ void nano::json_handler::accounts_receivable () bool const sorting = request.get ("sorting", false); auto simple (threshold.is_zero () && !source && !sorting); // if simple, response is a list of hashes for each account boost::property_tree::ptree pending; - auto transaction (node.store.tx_begin_read ()); + auto transaction = node.store.tx_begin_read (); for (auto & accounts : request.get_child ("accounts")) { auto account (account_impl (accounts.second.data ())); if (!ec) { boost::property_tree::ptree peers_l; - for (auto i (node.store.pending.begin (transaction, nano::pending_key (account, 0))), n (node.store.pending.end ()); i != n && nano::pending_key (i->first).account == account && peers_l.size () < count; ++i) + for (auto current = node.ledger.receivable_upper_bound (transaction, account, 0), end = node.ledger.receivable_end (); current != end; ++current) { - nano::pending_key const & key (i->first); - if (block_confirmed (node, transaction, key.hash, include_active, include_only_confirmed)) + auto const & [key, info] = *current; + if (include_only_confirmed && !node.ledger.block_confirmed (transaction, key.hash)) { - if (simple) - { - boost::property_tree::ptree entry; - entry.put ("", key.hash.to_string ()); - peers_l.push_back (std::make_pair ("", entry)); - } - else - { - nano::pending_info const & info (i->second); - if (info.amount.number () >= threshold.number ()) - { - if (source) - { - boost::property_tree::ptree pending_tree; - pending_tree.put ("amount", info.amount.number ().convert_to ()); - pending_tree.put ("source", info.source.to_account ()); - peers_l.add_child (key.hash.to_string (), pending_tree); - } - else - { - peers_l.put (key.hash.to_string (), info.amount.number ().convert_to ()); - } - } - } + continue; + } + if (simple) + { + boost::property_tree::ptree entry; + entry.put ("", key.hash.to_string ()); + peers_l.push_back (std::make_pair ("", entry)); + continue; + } + if (info.amount.number () < threshold.number ()) + { + continue; + } + if (source) + { + boost::property_tree::ptree pending_tree; + pending_tree.put ("amount", info.amount.number ().template convert_to ()); + pending_tree.put ("source", info.source.to_account ()); + peers_l.add_child (key.hash.to_string (), pending_tree); + } + else + { + peers_l.put (key.hash.to_string (), info.amount.number ().template convert_to ()); } } if (sorting && !simple) From 9d962e6e462c3ec1a2d26b9720b9f06d718d39a4 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Fri, 15 Mar 2024 23:10:11 +0000 Subject: [PATCH 033/128] Rewrite json_handler::receivable in terms of receivable iterators --- nano/node/json_handler.cpp | 95 +++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 48 deletions(-) diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index d69e57e58..d956ea9cb 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -3051,66 +3051,65 @@ void nano::json_handler::receivable () { auto offset_counter = offset; boost::property_tree::ptree peers_l; - auto transaction (node.store.tx_begin_read ()); + auto transaction = node.store.tx_begin_read (); // The ptree container is used if there are any children nodes (e.g source/min_version) otherwise the amount container is used. std::vector> hash_ptree_pairs; std::vector> hash_amount_pairs; - for (auto i (node.store.pending.begin (transaction, nano::pending_key (account, 0))), n (node.store.pending.end ()); i != n && nano::pending_key (i->first).account == account && (should_sort || peers_l.size () < count); ++i) + for (auto current = node.ledger.receivable_upper_bound (transaction, account, 0), end = node.ledger.receivable_end (); current != end && (should_sort || peers_l.size () < count); ++current) { - nano::pending_key const & key (i->first); - if (block_confirmed (node, transaction, key.hash, include_active, include_only_confirmed)) + auto const & [key, info] = *current; + if (include_only_confirmed && !node.ledger.block_confirmed (transaction, key.hash)) { - if (!should_sort && offset_counter > 0) + continue; + } + if (!should_sort && offset_counter > 0) + { + --offset_counter; + continue; + } + + if (simple) + { + boost::property_tree::ptree entry; + entry.put ("", key.hash.to_string ()); + peers_l.push_back (std::make_pair ("", entry)); + continue; + } + if (info.amount.number () < threshold.number ()) + { + continue; + } + if (source || min_version) + { + boost::property_tree::ptree pending_tree; + pending_tree.put ("amount", info.amount.number ().template convert_to ()); + if (source) { - --offset_counter; - continue; + pending_tree.put ("source", info.source.to_account ()); + } + if (min_version) + { + pending_tree.put ("min_version", epoch_as_string (info.epoch)); } - if (simple) + if (should_sort) { - boost::property_tree::ptree entry; - entry.put ("", key.hash.to_string ()); - peers_l.push_back (std::make_pair ("", entry)); + hash_ptree_pairs.emplace_back (key.hash.to_string (), pending_tree); } else { - nano::pending_info const & info (i->second); - if (info.amount.number () >= threshold.number ()) - { - if (source || min_version) - { - boost::property_tree::ptree pending_tree; - pending_tree.put ("amount", info.amount.number ().convert_to ()); - if (source) - { - pending_tree.put ("source", info.source.to_account ()); - } - if (min_version) - { - pending_tree.put ("min_version", epoch_as_string (info.epoch)); - } - - if (should_sort) - { - hash_ptree_pairs.emplace_back (key.hash.to_string (), pending_tree); - } - else - { - peers_l.add_child (key.hash.to_string (), pending_tree); - } - } - else - { - if (should_sort) - { - hash_amount_pairs.emplace_back (key.hash.to_string (), info.amount.number ()); - } - else - { - peers_l.put (key.hash.to_string (), info.amount.number ().convert_to ()); - } - } - } + peers_l.add_child (key.hash.to_string (), pending_tree); + } + } + else + { + if (should_sort) + { + hash_amount_pairs.emplace_back (key.hash.to_string (), info.amount.number ()); + } + else + { + peers_l.put (key.hash.to_string (), info.amount.number ().template convert_to ()); } } } From 873760f345bfb15b4b991fc1de4bd604ed0c5ee4 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Fri, 15 Mar 2024 23:18:03 +0000 Subject: [PATCH 034/128] Reimplement json_handler::wallet_receivable in terms of receivable iterators --- nano/node/json_handler.cpp | 61 +++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index d956ea9cb..c5b6d5056 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -4788,47 +4788,46 @@ void nano::json_handler::wallet_receivable () { boost::property_tree::ptree pending; auto transaction (node.wallets.tx_begin_read ()); - auto block_transaction (node.store.tx_begin_read ()); + auto block_transaction = node.store.tx_begin_read (); for (auto i (wallet->store.begin (transaction)), n (wallet->store.end ()); i != n; ++i) { nano::account const & account (i->first); boost::property_tree::ptree peers_l; - for (auto ii (node.store.pending.begin (block_transaction, nano::pending_key (account, 0))), nn (node.store.pending.end ()); ii != nn && nano::pending_key (ii->first).account == account && peers_l.size () < count; ++ii) + for (auto current = node.ledger.receivable_upper_bound (block_transaction, account, 0), end = node.ledger.receivable_end (); current != end && (peers_l.size () < count); ++current) { - nano::pending_key key (ii->first); - if (block_confirmed (node, block_transaction, key.hash, include_active, include_only_confirmed)) + auto const & [key, info] = *current; + if (include_only_confirmed && !node.ledger.block_confirmed (block_transaction, key.hash)) { - if (threshold.is_zero () && !source) + continue; + } + if (threshold.is_zero () && !source) + { + boost::property_tree::ptree entry; + entry.put ("", key.hash.to_string ()); + peers_l.push_back (std::make_pair ("", entry)); + continue; + } + if (info.amount.number () < threshold.number ()) + { + continue; + } + if (source || min_version) + { + boost::property_tree::ptree pending_tree; + pending_tree.put ("amount", info.amount.number ().template convert_to ()); + if (source) { - boost::property_tree::ptree entry; - entry.put ("", key.hash.to_string ()); - peers_l.push_back (std::make_pair ("", entry)); + pending_tree.put ("source", info.source.to_account ()); } - else + if (min_version) { - nano::pending_info info (ii->second); - if (info.amount.number () >= threshold.number ()) - { - if (source || min_version) - { - boost::property_tree::ptree pending_tree; - pending_tree.put ("amount", info.amount.number ().convert_to ()); - if (source) - { - pending_tree.put ("source", info.source.to_account ()); - } - if (min_version) - { - pending_tree.put ("min_version", epoch_as_string (info.epoch)); - } - peers_l.add_child (key.hash.to_string (), pending_tree); - } - else - { - peers_l.put (key.hash.to_string (), info.amount.number ().convert_to ()); - } - } + pending_tree.put ("min_version", epoch_as_string (info.epoch)); } + peers_l.add_child (key.hash.to_string (), pending_tree); + } + else + { + peers_l.put (key.hash.to_string (), info.amount.number ().template convert_to ()); } } if (!peers_l.empty ()) From 30a3cc2462a7083c710f7314dfdaed5c3915312d Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Fri, 15 Mar 2024 23:31:57 +0000 Subject: [PATCH 035/128] Rewrite wallet::search_receivable in terms of receivable iterators --- nano/node/wallet.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/nano/node/wallet.cpp b/nano/node/wallet.cpp index 6d7b6cd7a..927d22f52 100644 --- a/nano/node/wallet.cpp +++ b/nano/node/wallet.cpp @@ -1182,15 +1182,14 @@ bool nano::wallet::search_receivable (store::transaction const & wallet_transact // Don't search pending for watch-only accounts if (!nano::wallet_value (i->second).key.is_zero ()) { - for (auto j (wallets.node.store.pending.begin (block_transaction, nano::pending_key (account, 0))), k (wallets.node.store.pending.end ()); j != k && nano::pending_key (j->first).account == account; ++j) + for (auto i = wallets.node.ledger.receivable_upper_bound (block_transaction, account, 0), n = wallets.node.ledger.receivable_end (); i != n; ++i) { - nano::pending_key key (j->first); - auto hash (key.hash); - nano::pending_info pending (j->second); - auto amount (pending.amount.number ()); + auto const & [key, info] = *i; + auto hash = key.hash; + auto amount = info.amount.number (); if (wallets.node.config.receive_minimum.number () <= amount) { - wallets.node.logger.info (nano::log::type::wallet, "Found a receivable block {} for account {}", hash.to_string (), pending.source.to_account ()); + wallets.node.logger.info (nano::log::type::wallet, "Found a receivable block {} for account {}", hash.to_string (), info.source.to_account ()); if (wallets.node.ledger.block_confirmed (block_transaction, hash)) { From 8087454387d510ee112d0e13e3367c6ba91295b1 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Fri, 15 Mar 2024 23:41:21 +0000 Subject: [PATCH 036/128] Rewrite wallet::deterministic_check in terms of receivable iterators --- nano/node/wallet.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nano/node/wallet.cpp b/nano/node/wallet.cpp index 927d22f52..864ca4f01 100644 --- a/nano/node/wallet.cpp +++ b/nano/node/wallet.cpp @@ -1248,11 +1248,11 @@ uint32_t nano::wallet::deterministic_check (store::transaction const & transacti else { // Check if there are pending blocks for account - for (auto ii (wallets.node.store.pending.begin (block_transaction, nano::pending_key (pair.pub, 0))), nn (wallets.node.store.pending.end ()); ii != nn && nano::pending_key (ii->first).account == pair.pub; ++ii) + auto current = wallets.node.ledger.receivable_upper_bound (block_transaction, pair.pub, 0); + if (current != wallets.node.ledger.receivable_end ()) { index = i; n = i + 64 + (i / 64); - break; } } } From b34317004258d30fe38bdc64325a6ae20c8790c2 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Sat, 16 Mar 2024 18:07:30 +0000 Subject: [PATCH 037/128] Rewrite system::generate_receive in terms of receivable iterators --- nano/test_common/system.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/nano/test_common/system.cpp b/nano/test_common/system.cpp index 19a57c812..f4522c62b 100644 --- a/nano/test_common/system.cpp +++ b/nano/test_common/system.cpp @@ -416,11 +416,10 @@ void nano::test::system::generate_receive (nano::node & node_a) auto transaction (node_a.store.tx_begin_read ()); nano::account random_account; random_pool::generate_block (random_account.bytes.data (), sizeof (random_account.bytes)); - auto i (node_a.store.pending.begin (transaction, nano::pending_key (random_account, 0))); - if (i != node_a.store.pending.end ()) + auto item = node_a.ledger.receivable_upper_bound (transaction, random_account); + if (item != node_a.ledger.receivable_end ()) { - nano::pending_key const & send_hash (i->first); - send_block = node_a.ledger.block (transaction, send_hash.hash); + send_block = node_a.ledger.block (transaction, item->first.hash); } } if (send_block != nullptr) From 4439f46d0c2f932e06f4c2ad984914c31ba348e6 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Sat, 16 Mar 2024 13:18:14 +0000 Subject: [PATCH 038/128] Rewrite bulk_pull_account_server in terms of receivable iterators --- nano/node/bootstrap/bootstrap_bulk_pull.cpp | 26 +++++---------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/nano/node/bootstrap/bootstrap_bulk_pull.cpp b/nano/node/bootstrap/bootstrap_bulk_pull.cpp index c5ca04b28..65f392b58 100644 --- a/nano/node/bootstrap/bootstrap_bulk_pull.cpp +++ b/nano/node/bootstrap/bootstrap_bulk_pull.cpp @@ -753,30 +753,16 @@ std::pair, std::unique_ptrstore.tx_begin_read ()); - auto stream (node->store.pending.begin (stream_transaction, current_key)); + auto tx = node->store.tx_begin_read (); + auto & ledger = node->ledger; + auto stream = ledger.receivable_upper_bound (tx, current_key.account, current_key.hash); - if (stream == store::iterator (nullptr)) - { - break; - } - - nano::pending_key key (stream->first); - nano::pending_info info (stream->second); - - /* - * Get the key for the next value, to use in the next call or iteration - */ - current_key.account = key.account; - current_key.hash = key.hash.number () + 1; - - /* - * Finish up if the response is for a different account - */ - if (key.account != request->account) + if (stream == ledger.receivable_end ()) { break; } + auto const & [key, info] = *stream; + current_key = key; /* * Skip entries where the amount is less than the requested From 35ab9d408f0ba881c55c6766d1fb230f04b36090 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Sat, 16 Mar 2024 19:07:32 +0000 Subject: [PATCH 039/128] Rewrite bootstrap database_iterator in terms of receivable iterators --- nano/node/bootstrap_ascending/iterators.cpp | 24 ++++++++++----------- nano/node/bootstrap_ascending/iterators.hpp | 13 +++++++---- nano/node/bootstrap_ascending/service.cpp | 2 +- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/nano/node/bootstrap_ascending/iterators.cpp b/nano/node/bootstrap_ascending/iterators.cpp index d35c68256..c940218a3 100644 --- a/nano/node/bootstrap_ascending/iterators.cpp +++ b/nano/node/bootstrap_ascending/iterators.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -9,8 +10,8 @@ * database_iterator */ -nano::bootstrap_ascending::database_iterator::database_iterator (nano::store::component & store_a, table_type table_a) : - store{ store_a }, +nano::bootstrap_ascending::database_iterator::database_iterator (nano::ledger & ledger, table_type table_a) : + ledger{ ledger }, table{ table_a } { } @@ -27,8 +28,8 @@ void nano::bootstrap_ascending::database_iterator::next (store::transaction & tx case table_type::account: { auto i = current.number () + 1; - auto item = store.account.begin (tx, i); - if (item != store.account.end ()) + auto item = ledger.store.account.begin (tx, i); + if (item != ledger.store.account.end ()) { current = item->first; } @@ -40,9 +41,8 @@ void nano::bootstrap_ascending::database_iterator::next (store::transaction & tx } case table_type::pending: { - auto i = current.number () + 1; - auto item = store.pending.begin (tx, nano::pending_key{ i, 0 }); - if (item != store.pending.end ()) + auto item = ledger.receivable_upper_bound (tx, current); + if (item != ledger.receivable_end ()) { current = item->first.account; } @@ -59,10 +59,10 @@ void nano::bootstrap_ascending::database_iterator::next (store::transaction & tx * buffered_iterator */ -nano::bootstrap_ascending::buffered_iterator::buffered_iterator (nano::store::component & store_a) : - store{ store_a }, - accounts_iterator{ store, database_iterator::table_type::account }, - pending_iterator{ store, database_iterator::table_type::pending } +nano::bootstrap_ascending::buffered_iterator::buffered_iterator (nano::ledger & ledger) : + ledger{ ledger }, + accounts_iterator{ ledger, database_iterator::table_type::account }, + pending_iterator{ ledger, database_iterator::table_type::pending } { } @@ -95,7 +95,7 @@ void nano::bootstrap_ascending::buffered_iterator::fill () debug_assert (buffer.empty ()); // Fill half from accounts table and half from pending table - auto transaction = store.tx_begin_read (); + auto transaction = ledger.store.tx_begin_read (); for (int n = 0; n < size / 2; ++n) { diff --git a/nano/node/bootstrap_ascending/iterators.hpp b/nano/node/bootstrap_ascending/iterators.hpp index 58d7c6c69..e963164de 100644 --- a/nano/node/bootstrap_ascending/iterators.hpp +++ b/nano/node/bootstrap_ascending/iterators.hpp @@ -4,6 +4,11 @@ #include +namespace nano +{ +class ledger; +} + namespace nano::store { class component; @@ -21,12 +26,12 @@ public: pending }; - explicit database_iterator (nano::store::component & store, table_type); + explicit database_iterator (nano::ledger & ledger, table_type); nano::account operator* () const; void next (nano::store::transaction & tx); private: - nano::store::component & store; + nano::ledger & ledger; nano::account current{ 0 }; const table_type table; }; @@ -34,7 +39,7 @@ private: class buffered_iterator { public: - explicit buffered_iterator (nano::store::component & store); + explicit buffered_iterator (nano::ledger & ledger); nano::account operator* () const; nano::account next (); // Indicates if a full ledger iteration has taken place e.g. warmed up @@ -44,7 +49,7 @@ private: void fill (); private: - nano::store::component & store; + nano::ledger & ledger; std::deque buffer; bool warmup_m{ true }; diff --git a/nano/node/bootstrap_ascending/service.cpp b/nano/node/bootstrap_ascending/service.cpp index e75a26c24..c3e9bd2d1 100644 --- a/nano/node/bootstrap_ascending/service.cpp +++ b/nano/node/bootstrap_ascending/service.cpp @@ -24,7 +24,7 @@ nano::bootstrap_ascending::service::service (nano::node_config & config_a, nano: network{ network_a }, stats{ stat_a }, accounts{ stats }, - iterator{ ledger.store }, + iterator{ ledger }, throttle{ compute_throttle_size () }, scoring{ config.bootstrap_ascending, config.network_params.network }, database_limiter{ config.bootstrap_ascending.database_requests_limit, 1.0 } From 773cd91f1bff8da6f097203c38881924d29d7000 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Sat, 16 Mar 2024 19:13:51 +0000 Subject: [PATCH 040/128] Rewrite epoch_upgrader in terms of receivable iterators. --- nano/node/epoch_upgrader.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/nano/node/epoch_upgrader.cpp b/nano/node/epoch_upgrader.cpp index 58db6213d..495d09dc5 100644 --- a/nano/node/epoch_upgrader.cpp +++ b/nano/node/epoch_upgrader.cpp @@ -210,14 +210,13 @@ void nano::epoch_upgrader::upgrade_impl (nano::raw_key const & prv_a, nano::epoc std::atomic upgraded_pending (0); uint64_t workers (0); uint64_t attempts (0); - auto transaction (store.tx_begin_read ()); - for (auto i (store.pending.begin (transaction, nano::pending_key (1, 0))), n (store.pending.end ()); i != n && attempts < upgrade_batch_size && attempts < count_limit && !stopped;) + auto transaction = store.tx_begin_read (); + for (auto current = ledger.receivable_upper_bound (transaction, 0), end = ledger.receivable_end (); current != end && attempts < upgrade_batch_size && attempts < count_limit && !stopped;) { bool to_next_account (false); - nano::pending_key const & key (i->first); + auto const & [key, info] = *current; if (!store.account.exists (transaction, key.account)) { - nano::pending_info const & info (i->second); if (info.epoch < epoch_a) { ++attempts; @@ -272,13 +271,13 @@ void nano::epoch_upgrader::upgrade_impl (nano::raw_key const & prv_a, nano::epoc } else { - i = store.pending.begin (transaction, nano::pending_key (key.account.number () + 1, 0)); + current = ledger.receivable_upper_bound (transaction, key.account); } } else { // Move to next pending item - ++i; + current = ledger.receivable_upper_bound (transaction, key.account, key.hash); } } { From cc9006563031f830aa1c3eb8bebf5db29eb1f022 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Sat, 16 Mar 2024 19:16:48 +0000 Subject: [PATCH 041/128] Fuse branches in epoch_upgrader::upgrade_impl --- nano/node/epoch_upgrader.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/nano/node/epoch_upgrader.cpp b/nano/node/epoch_upgrader.cpp index 495d09dc5..c4f683d49 100644 --- a/nano/node/epoch_upgrader.cpp +++ b/nano/node/epoch_upgrader.cpp @@ -213,7 +213,6 @@ void nano::epoch_upgrader::upgrade_impl (nano::raw_key const & prv_a, nano::epoc auto transaction = store.tx_begin_read (); for (auto current = ledger.receivable_upper_bound (transaction, 0), end = ledger.receivable_end (); current != end && attempts < upgrade_batch_size && attempts < count_limit && !stopped;) { - bool to_next_account (false); auto const & [key, info] = *current; if (!store.account.exists (transaction, key.account)) { @@ -257,12 +256,10 @@ void nano::epoch_upgrader::upgrade_impl (nano::raw_key const & prv_a, nano::epoc upgrader_process (upgraded_pending, epoch, difficulty, signer, root, account); } } + // Move to next pending item + current = ledger.receivable_upper_bound (transaction, key.account, key.hash); } else - { - to_next_account = true; - } - if (to_next_account) { // Move to next account if pending account exists or was upgraded if (key.account.number () == std::numeric_limits::max ()) @@ -274,11 +271,6 @@ void nano::epoch_upgrader::upgrade_impl (nano::raw_key const & prv_a, nano::epoc current = ledger.receivable_upper_bound (transaction, key.account); } } - else - { - // Move to next pending item - current = ledger.receivable_upper_bound (transaction, key.account, key.hash); - } } { nano::unique_lock lock{ upgrader_mutex }; From d58316451abd507a5a328ffc024651fa8a129ba2 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Sat, 16 Mar 2024 19:24:17 +0000 Subject: [PATCH 042/128] Rewrite json_handler::unopened in terms of receivable iterators The algorithm had a non-trivial change to implementation. The intent is to sum the amount of balance receivable for given set of accounts. The region selected starts at "account" and ends when "count" is reached. --- nano/node/json_handler.cpp | 50 ++++++++++++-------------------------- 1 file changed, 15 insertions(+), 35 deletions(-) diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index c5b6d5056..230638b71 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -4238,7 +4238,7 @@ void nano::json_handler::unopened () { auto count (count_optional_impl ()); auto threshold (threshold_optional_impl ()); - nano::account start (1); // exclude burn account by default + nano::account start{ 1 }; // exclude burn account by default boost::optional account_text (request.get_optional ("account")); if (account_text.is_initialized ()) { @@ -4246,48 +4246,28 @@ void nano::json_handler::unopened () } if (!ec) { - auto transaction (node.store.tx_begin_read ()); - auto iterator (node.store.pending.begin (transaction, nano::pending_key (start, 0))); - auto end (node.store.pending.end ()); - nano::account current_account (start); - nano::uint128_t current_account_sum{ 0 }; + auto transaction = node.store.tx_begin_read (); + auto & ledger = node.ledger; boost::property_tree::ptree accounts; - while (iterator != end && accounts.size () < count) + for (auto iterator = ledger.receivable_upper_bound (transaction, start, 0), end = ledger.receivable_end (); iterator != end && accounts.size () < count;) { - nano::pending_key key (iterator->first); - nano::account account (key.account); - nano::pending_info info (iterator->second); - if (node.store.account.exists (transaction, account)) + auto const & [key, info] = *iterator; + nano::account account = key.account; + if (!node.store.account.exists (transaction, account)) { - if (account.number () == std::numeric_limits::max ()) + nano::uint128_t current_account_sum{ 0 }; + while (iterator != end) { - break; + auto const & [key, info] = *iterator; + current_account_sum += info.amount.number (); + ++iterator; } - // Skip existing accounts - iterator = node.store.pending.begin (transaction, nano::pending_key (account.number () + 1, 0)); - } - else - { - if (account != current_account) + if (current_account_sum >= threshold.number ()) { - if (current_account_sum > 0) - { - if (current_account_sum >= threshold.number ()) - { - accounts.put (current_account.to_account (), current_account_sum.convert_to ()); - } - current_account_sum = 0; - } - current_account = account; + accounts.put (account.to_account (), current_account_sum.convert_to ()); } - current_account_sum += info.amount.number (); - ++iterator; } - } - // last one after iterator reaches end - if (accounts.size () < count && current_account_sum > 0 && current_account_sum >= threshold.number ()) - { - accounts.put (current_account.to_account (), current_account_sum.convert_to ()); + iterator = ledger.receivable_upper_bound (transaction, account); } response_l.add_child ("accounts", accounts); } From 65913505856bc3c227a242840ba29f2da90b452f Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Sat, 16 Mar 2024 14:09:18 +0000 Subject: [PATCH 043/128] Rewrite bootstrap.trace_base in terms of receivable iterators --- nano/core_test/bootstrap_ascending.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nano/core_test/bootstrap_ascending.cpp b/nano/core_test/bootstrap_ascending.cpp index 257b35c60..59c0eb381 100644 --- a/nano/core_test/bootstrap_ascending.cpp +++ b/nano/core_test/bootstrap_ascending.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -251,7 +252,7 @@ TEST (bootstrap_ascending, trace_base) // std::cerr << "--------------- Start ---------------\n"; ASSERT_EQ (nano::block_status::progress, node0.process (send1)); ASSERT_EQ (nano::block_status::progress, node0.process (receive1)); - ASSERT_EQ (node1.store.pending.begin (node1.store.tx_begin_read (), nano::pending_key{ key.pub, 0 }), node1.store.pending.end ()); + ASSERT_EQ (node1.ledger.receivable_end (), node1.ledger.receivable_upper_bound (node1.store.tx_begin_read (), key.pub, 0)); // std::cerr << "node0: " << node0.network.endpoint () << std::endl; // std::cerr << "node1: " << node1.network.endpoint () << std::endl; ASSERT_TIMELY (10s, node1.block (receive1->hash ()) != nullptr); From fc3c4e34eca98b72af2a0a132deca6d34d3525d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20W=C3=B3jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Mon, 18 Mar 2024 19:12:38 +0100 Subject: [PATCH 044/128] Fix `tcp_server` constructor (#4499) --- nano/node/transport/tcp_server.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/nano/node/transport/tcp_server.cpp b/nano/node/transport/tcp_server.cpp index bcad6d5b0..bea5f4f9b 100644 --- a/nano/node/transport/tcp_server.cpp +++ b/nano/node/transport/tcp_server.cpp @@ -279,8 +279,8 @@ std::unique_ptr nano::transport::collect_contain */ nano::transport::tcp_server::tcp_server (std::shared_ptr socket_a, std::shared_ptr node_a, bool allow_bootstrap_a) : - socket{ std::move (socket_a) }, - node{ std::move (node_a) }, + socket{ socket_a }, + node{ node_a }, allow_bootstrap{ allow_bootstrap_a }, message_deserializer{ std::make_shared (node_a->network_params.network, node_a->network.publish_filter, node_a->block_uniquer, node_a->vote_uniquer, @@ -335,11 +335,14 @@ void nano::transport::tcp_server::start () debug_assert (remote_endpoint.port () != 0); } - if (auto node_l = node.lock (); node_l) + auto node = this->node.lock (); + if (!node) { - node_l->logger.debug (nano::log::type::tcp_server, "Starting TCP server ({})", nano::util::to_str (remote_endpoint)); + return; } + node->logger.debug (nano::log::type::tcp_server, "Starting TCP server ({})", nano::util::to_str (remote_endpoint)); + receive_message (); } From 88d27d8ef89dede48d0b0ae1e926aaa390b45f1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20W=C3=B3jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Mon, 18 Mar 2024 20:18:05 +0100 Subject: [PATCH 045/128] Cleanup strand binding in socket class (#4498) * Avoid double binding to strand * Cleanup * Unused code --- nano/node/transport/socket.cpp | 50 +++++++++++++++------------------- nano/node/transport/socket.hpp | 1 - 2 files changed, 22 insertions(+), 29 deletions(-) diff --git a/nano/node/transport/socket.cpp b/nano/node/transport/socket.cpp index 427190553..abf3bc045 100644 --- a/nano/node/transport/socket.cpp +++ b/nano/node/transport/socket.cpp @@ -50,12 +50,11 @@ void nano::transport::socket::async_connect (nano::tcp_endpoint const & endpoint debug_assert (endpoint_type () == endpoint_type_t::client); start (); - auto this_l (shared_from_this ()); set_default_timeout (); - this_l->tcp_socket.async_connect (endpoint_a, - boost::asio::bind_executor (this_l->strand, - [this_l, callback = std::move (callback_a), endpoint_a] (boost::system::error_code const & ec) { + tcp_socket.async_connect (endpoint_a, + boost::asio::bind_executor (strand, + [this_l = shared_from_this (), callback = std::move (callback_a), endpoint_a] (boost::system::error_code const & ec) { this_l->remote = endpoint_a; if (ec) { @@ -82,14 +81,15 @@ void nano::transport::socket::async_read (std::shared_ptr> if (size_a <= buffer_a->size ()) { - auto this_l (shared_from_this ()); if (!closed) { set_default_timeout (); - boost::asio::post (strand, boost::asio::bind_executor (strand, [buffer_a, callback = std::move (callback_a), size_a, this_l] () mutable { + boost::asio::post (strand, [this_l = shared_from_this (), buffer_a, callback = std::move (callback_a), size_a] () mutable { boost::asio::async_read (this_l->tcp_socket, boost::asio::buffer (buffer_a->data (), size_a), boost::asio::bind_executor (this_l->strand, [this_l, buffer_a, cbk = std::move (callback)] (boost::system::error_code const & ec, std::size_t size_a) { + debug_assert (this_l->strand.running_in_this_thread ()); + if (ec) { this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_read_error, nano::stat::dir::in); @@ -103,7 +103,7 @@ void nano::transport::socket::async_read (std::shared_ptr> } cbk (ec, size_a); })); - })); + }); } } else @@ -139,17 +139,19 @@ void nano::transport::socket::async_write (nano::shared_const_buffer const & buf return; } - boost::asio::post (strand, boost::asio::bind_executor (strand, [this_s = shared_from_this (), buffer_a, callback_a, traffic_type] () { + boost::asio::post (strand, [this_s = shared_from_this (), buffer_a, callback_a] () { if (!this_s->write_in_progress) { this_s->write_queued_messages (); } - })); + }); } // Must be called from strand void nano::transport::socket::write_queued_messages () { + debug_assert (strand.running_in_this_thread ()); + if (closed) { return; @@ -165,18 +167,19 @@ void nano::transport::socket::write_queued_messages () write_in_progress = true; nano::async_write (tcp_socket, next->buffer, - boost::asio::bind_executor (strand, [this_s = shared_from_this (), next /* `next` object keeps buffer in scope */] (boost::system::error_code ec, std::size_t size) { - this_s->write_in_progress = false; + boost::asio::bind_executor (strand, [this_l = shared_from_this (), next /* `next` object keeps buffer in scope */] (boost::system::error_code ec, std::size_t size) { + debug_assert (this_l->strand.running_in_this_thread ()); + this_l->write_in_progress = false; if (ec) { - this_s->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_write_error, nano::stat::dir::in); - this_s->close (); + this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_write_error, nano::stat::dir::in); + this_l->close (); } else { - this_s->node.stats.add (nano::stat::type::traffic_tcp, nano::stat::dir::out, size); - this_s->set_last_completion (); + this_l->node.stats.add (nano::stat::type::traffic_tcp, nano::stat::dir::out, size); + this_l->set_last_completion (); } if (next->callback) @@ -186,7 +189,7 @@ void nano::transport::socket::write_queued_messages () if (!ec) { - this_s->write_queued_messages (); + this_l->write_queued_messages (); } })); } @@ -301,20 +304,11 @@ std::chrono::seconds nano::transport::socket::get_default_timeout_value () const return default_timeout; } -void nano::transport::socket::set_silent_connection_tolerance_time (std::chrono::seconds tolerance_time_a) -{ - auto this_l (shared_from_this ()); - boost::asio::dispatch (strand, boost::asio::bind_executor (strand, [this_l, tolerance_time_a] () { - this_l->silent_connection_tolerance_time = tolerance_time_a; - })); -} - void nano::transport::socket::close () { - auto this_l (shared_from_this ()); - boost::asio::dispatch (strand, boost::asio::bind_executor (strand, [this_l] { + boost::asio::dispatch (strand, [this_l = shared_from_this ()] { this_l->close_internal (); - })); + }); } // This must be called from a strand or the destructor @@ -328,9 +322,9 @@ void nano::transport::socket::close_internal () send_queue.clear (); default_timeout = std::chrono::seconds (0); - boost::system::error_code ec; // Ignore error code for shutdown as it is best-effort + boost::system::error_code ec; tcp_socket.shutdown (boost::asio::ip::tcp::socket::shutdown_both, ec); tcp_socket.close (ec); diff --git a/nano/node/transport/socket.hpp b/nano/node/transport/socket.hpp index 91f7d008f..b0b109607 100644 --- a/nano/node/transport/socket.hpp +++ b/nano/node/transport/socket.hpp @@ -86,7 +86,6 @@ public: void set_default_timeout_value (std::chrono::seconds); std::chrono::seconds get_default_timeout_value () const; void set_timeout (std::chrono::seconds); - void set_silent_connection_tolerance_time (std::chrono::seconds tolerance_time_a); bool max (nano::transport::traffic_type = nano::transport::traffic_type::generic) const; bool full (nano::transport::traffic_type = nano::transport::traffic_type::generic) const; From 7a322ee92b25d4ea6a4a86b7124f847089e0cc41 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Mon, 18 Mar 2024 19:56:12 +0000 Subject: [PATCH 046/128] Replace direct access to store.pending.exists with calls to ledger::pending_info --- nano/core_test/ledger.cpp | 44 +++++++++++++++++++------------------- nano/node/json_handler.cpp | 8 +++---- nano/rpc_test/rpc.cpp | 2 +- nano/secure/ledger.cpp | 2 +- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 9557954f6..773057e89 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -2373,7 +2373,7 @@ TEST (ledger, state_send_receive) ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.balance (transaction, send1->hash ())); ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, send1->hash ())); ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::dev::genesis_key.pub)); - ASSERT_TRUE (store.pending.exists (transaction, nano::pending_key (nano::dev::genesis_key.pub, send1->hash ()))); + ASSERT_TRUE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); ASSERT_EQ (2, send2->sideband ().height); ASSERT_TRUE (send2->is_send ()); ASSERT_FALSE (send2->is_receive ()); @@ -2396,7 +2396,7 @@ TEST (ledger, state_send_receive) ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.balance (transaction, receive1->hash ())); ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, receive1->hash ())); ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub)); - ASSERT_FALSE (store.pending.exists (transaction, nano::pending_key (nano::dev::genesis_key.pub, send1->hash ()))); + ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); ASSERT_EQ (3, receive2->sideband ().height); ASSERT_FALSE (receive2->is_send ()); @@ -2513,7 +2513,7 @@ TEST (ledger, state_open) ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.balance (transaction, send1->hash ())); ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, send1->hash ())); ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::dev::genesis_key.pub)); - ASSERT_TRUE (store.pending.exists (transaction, nano::pending_key (destination.pub, send1->hash ()))); + ASSERT_TRUE (ledger.pending_info (transaction, nano::pending_key{ destination.pub, send1->hash () })); auto open1 = builder .state () .account (destination.pub) @@ -2525,7 +2525,7 @@ TEST (ledger, state_open) .work (*pool.generate (destination.pub)) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open1)); - ASSERT_FALSE (store.pending.exists (transaction, nano::pending_key (destination.pub, send1->hash ()))); + ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ destination.pub, send1->hash () })); ASSERT_TRUE (ledger.block_exists (transaction, open1->hash ())); auto open2 = ledger.block (transaction, open1->hash ()); ASSERT_NE (nullptr, open2); @@ -3138,7 +3138,7 @@ TEST (ledger, state_rollback_send) ASSERT_FALSE (ledger.block_exists (transaction, send1->hash ())); ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.account_balance (transaction, nano::dev::genesis_key.pub)); ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub)); - ASSERT_FALSE (store.pending.exists (transaction, nano::pending_key (nano::dev::genesis_key.pub, send1->hash ()))); + ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); ASSERT_FALSE (ledger.successor (transaction, nano::dev::genesis->hash ())); ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); } @@ -3173,7 +3173,7 @@ TEST (ledger, state_rollback_receive) .work (*pool.generate (send1->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive1)); - ASSERT_FALSE (store.pending.exists (transaction, nano::pending_key (nano::dev::genesis_key.pub, receive1->hash ()))); + ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, receive1->hash () })); ASSERT_FALSE (ledger.rollback (transaction, receive1->hash ())); auto info = ledger.pending_info (transaction, nano::pending_key (nano::dev::genesis_key.pub, send1->hash ())); ASSERT_TRUE (info); @@ -3216,9 +3216,9 @@ TEST (ledger, state_rollback_received_send) .work (*pool.generate (key.pub)) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive1)); - ASSERT_FALSE (store.pending.exists (transaction, nano::pending_key (nano::dev::genesis_key.pub, receive1->hash ()))); + ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, receive1->hash () })); ASSERT_FALSE (ledger.rollback (transaction, send1->hash ())); - ASSERT_FALSE (store.pending.exists (transaction, nano::pending_key (nano::dev::genesis_key.pub, send1->hash ()))); + ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); ASSERT_FALSE (ledger.block_exists (transaction, send1->hash ())); ASSERT_FALSE (ledger.block_exists (transaction, receive1->hash ())); ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.account_balance (transaction, nano::dev::genesis_key.pub)); @@ -4855,7 +4855,7 @@ TEST (ledger, pruning_action) auto send1_stored (store->block.get (transaction, send1->hash ())); ASSERT_NE (nullptr, send1_stored); ASSERT_EQ (*send1, *send1_stored); - ASSERT_TRUE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis_key.pub, send1->hash ()))); + ASSERT_TRUE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); auto send2 = builder .state () .account (nano::dev::genesis_key.pub) @@ -4871,7 +4871,7 @@ TEST (ledger, pruning_action) // Pruning action ASSERT_EQ (1, ledger.pruning_action (transaction, send1->hash (), 1)); ASSERT_EQ (0, ledger.pruning_action (transaction, nano::dev::genesis->hash (), 1)); - ASSERT_TRUE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis_key.pub, send1->hash ()))); + ASSERT_TRUE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); ASSERT_FALSE (store->block.exists (transaction, send1->hash ())); ASSERT_TRUE (ledger.block_or_pruned_exists (transaction, send1->hash ())); // Pruned ledger start without proper flags emulation @@ -4897,7 +4897,7 @@ TEST (ledger, pruning_action) auto receive1_stored (store->block.get (transaction, receive1->hash ())); ASSERT_NE (nullptr, receive1_stored); ASSERT_EQ (*receive1, *receive1_stored); - ASSERT_FALSE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis_key.pub, send1->hash ()))); + ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); ASSERT_EQ (4, receive1_stored->sideband ().height); ASSERT_FALSE (receive1_stored->is_send ()); ASSERT_TRUE (receive1_stored->is_receive ()); @@ -5001,7 +5001,7 @@ TEST (ledger, pruning_source_rollback) .work (*pool.generate (epoch1->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send1)); - ASSERT_TRUE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis_key.pub, send1->hash ()))); + ASSERT_TRUE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); auto send2 = builder .state () .account (nano::dev::genesis_key.pub) @@ -5038,7 +5038,7 @@ TEST (ledger, pruning_source_rollback) .work (*pool.generate (send2->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive1)); - ASSERT_FALSE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis_key.pub, send1->hash ()))); + ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); ASSERT_EQ (2, ledger.cache.pruned_count); ASSERT_EQ (5, ledger.cache.block_count); // Rollback receive block @@ -5050,7 +5050,7 @@ TEST (ledger, pruning_source_rollback) ASSERT_EQ (nano::epoch::epoch_1, info2->epoch); // Process receive block again ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive1)); - ASSERT_FALSE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis_key.pub, send1->hash ()))); + ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); ASSERT_EQ (2, ledger.cache.pruned_count); ASSERT_EQ (5, ledger.cache.block_count); } @@ -5076,7 +5076,7 @@ TEST (ledger, pruning_source_rollback_legacy) .work (*pool.generate (nano::dev::genesis->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send1)); - ASSERT_TRUE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis_key.pub, send1->hash ()))); + ASSERT_TRUE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); nano::keypair key1; auto send2 = builder .send () @@ -5088,7 +5088,7 @@ TEST (ledger, pruning_source_rollback_legacy) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2)); ASSERT_TRUE (store->block.exists (transaction, send2->hash ())); - ASSERT_TRUE (store->pending.exists (transaction, nano::pending_key (key1.pub, send2->hash ()))); + ASSERT_TRUE (ledger.pending_info (transaction, nano::pending_key{ key1.pub, send2->hash () })); auto send3 = builder .send () .previous (send2->hash ()) @@ -5099,7 +5099,7 @@ TEST (ledger, pruning_source_rollback_legacy) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send3)); ASSERT_TRUE (store->block.exists (transaction, send3->hash ())); - ASSERT_TRUE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis_key.pub, send3->hash ()))); + ASSERT_TRUE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send3->hash () })); // Pruning action ASSERT_EQ (2, ledger.pruning_action (transaction, send2->hash (), 1)); ASSERT_FALSE (store->block.exists (transaction, send2->hash ())); @@ -5126,7 +5126,7 @@ TEST (ledger, pruning_source_rollback_legacy) .work (*pool.generate (send3->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive1)); - ASSERT_FALSE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis_key.pub, send1->hash ()))); + ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); ASSERT_EQ (2, ledger.cache.pruned_count); ASSERT_EQ (5, ledger.cache.block_count); // Rollback receive block @@ -5138,7 +5138,7 @@ TEST (ledger, pruning_source_rollback_legacy) ASSERT_EQ (nano::epoch::epoch_0, info3->epoch); // Process receive block again ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive1)); - ASSERT_FALSE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis_key.pub, send1->hash ()))); + ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); ASSERT_EQ (2, ledger.cache.pruned_count); ASSERT_EQ (5, ledger.cache.block_count); // Receiving pruned block (open) @@ -5151,7 +5151,7 @@ TEST (ledger, pruning_source_rollback_legacy) .work (*pool.generate (key1.pub)) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open1)); - ASSERT_FALSE (store->pending.exists (transaction, nano::pending_key (key1.pub, send2->hash ()))); + ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ key1.pub, send2->hash () })); ASSERT_EQ (2, ledger.cache.pruned_count); ASSERT_EQ (6, ledger.cache.block_count); // Rollback open block @@ -5163,7 +5163,7 @@ TEST (ledger, pruning_source_rollback_legacy) ASSERT_EQ (nano::epoch::epoch_0, info4->epoch); // Process open block again ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open1)); - ASSERT_FALSE (store->pending.exists (transaction, nano::pending_key (key1.pub, send2->hash ()))); + ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ key1.pub, send2->hash () })); ASSERT_EQ (2, ledger.cache.pruned_count); ASSERT_EQ (6, ledger.cache.block_count); } @@ -5237,7 +5237,7 @@ TEST (ledger, pruning_legacy_blocks) .work (*pool.generate (nano::dev::genesis->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send1)); - ASSERT_TRUE (store->pending.exists (transaction, nano::pending_key (nano::dev::genesis_key.pub, send1->hash ()))); + ASSERT_TRUE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); auto receive1 = builder .receive () .previous (send1->hash ()) diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 230638b71..1d98e73cb 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -417,7 +417,7 @@ uint64_t nano::json_handler::difficulty_ledger (nano::block const & block_a) { auto block_link = node.ledger.block (transaction, link.value ().as_block_hash ()); auto account = block_a.account_field ().value (); // Link is non-zero therefore it's a state block and has an account field; - if (block_link != nullptr && node.store.pending.exists (transaction, nano::pending_key (account, link.value ().as_block_hash ()))) + if (block_link != nullptr && node.ledger.pending_info (transaction, nano::pending_key{ account, link.value ().as_block_hash () })) { details.epoch = std::max (details.epoch, block_link->sideband ().details.epoch); details.is_receive = true; @@ -1353,7 +1353,7 @@ void nano::json_handler::blocks_info () entry.put ("receive_hash", nano::block_hash (0).to_string ()); } } - else if (node.store.pending.exists (transaction, nano::pending_key (block->destination (), hash))) + else if (node.ledger.pending_info (transaction, nano::pending_key{ block->destination (), hash })) { if (receivable) { @@ -3162,7 +3162,7 @@ void nano::json_handler::receivable_exists () auto exists (false); if (block->is_send ()) { - exists = node.store.pending.exists (transaction, nano::pending_key (block->destination (), hash)); + exists = node.ledger.pending_info (transaction, nano::pending_key{ block->destination (), hash }).has_value (); } exists = exists && (block_confirmed (node, transaction, block->hash (), include_active, include_only_confirmed)); response_l.put ("exists", exists ? "1" : "0"); @@ -3667,7 +3667,7 @@ void nano::json_handler::republish () auto destination = block_b->destination (); if (!destination.is_zero ()) { - if (!node.store.pending.exists (transaction, nano::pending_key (destination, hash))) + if (!node.ledger.pending_info (transaction, nano::pending_key{ destination, hash })) { nano::block_hash previous (node.ledger.latest (transaction, destination)); auto block_d = node.ledger.block (transaction, previous); diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 7ca31f164..8eef6f5d3 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -3310,7 +3310,7 @@ TEST (rpc, pending_exists) request.put ("hash", hash0.to_string ()); ASSERT_TRUE (pending_exists ("0")); - node->store.pending.exists (node->store.tx_begin_read (), nano::pending_key (nano::dev::genesis_key.pub, block1->hash ())); + node->ledger.pending_info (node->store.tx_begin_read (), nano::pending_key{ nano::dev::genesis_key.pub, block1->hash () }); request.put ("hash", block1->hash ().to_string ()); ASSERT_TRUE (pending_exists ("1")); diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 3df9fd9cf..55570fcf8 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -145,7 +145,7 @@ public: if (is_send) { nano::pending_key key (block_a.hashables.link.as_account (), hash); - while (!error && !ledger.store.pending.exists (transaction, key)) + while (!error && !ledger.pending_info (transaction, key)) { error = ledger.rollback (transaction, ledger.latest (transaction, block_a.hashables.link.as_account ()), list); } From aea421798ffee5cc3b314b230ca474f29957ff78 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Mon, 18 Mar 2024 19:57:35 +0000 Subject: [PATCH 047/128] Replace some direct queries to store.pending.get with ledger::pending_info --- nano/qt/qt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nano/qt/qt.cpp b/nano/qt/qt.cpp index 78dcaf88e..6c578f06b 100644 --- a/nano/qt/qt.cpp +++ b/nano/qt/qt.cpp @@ -2322,7 +2322,7 @@ void nano_qt::block_creation::create_receive () if (!destination.is_zero ()) { nano::pending_key pending_key (destination, source_l); - if (auto pending = wallet.node.store.pending.get (block_transaction, pending_key)) + if (auto pending = wallet.node.ledger.pending_info (block_transaction, pending_key)) { nano::account_info info; auto error (wallet.node.store.account.get (block_transaction, pending_key.account, info)); @@ -2486,7 +2486,7 @@ void nano_qt::block_creation::create_open () if (!destination.is_zero ()) { nano::pending_key pending_key (destination, source_l); - if (auto pending = wallet.node.store.pending.get (block_transaction, pending_key)) + if (auto pending = wallet.node.ledger.pending_info (block_transaction, pending_key)) { nano::account_info info; auto error (wallet.node.store.account.get (block_transaction, pending_key.account, info)); From 0f8e2cfb4598f9f9733a1b6470dcc51cc9a2f56d Mon Sep 17 00:00:00 2001 From: Gustav Schauwecker Date: Fri, 8 Mar 2024 17:03:13 +0100 Subject: [PATCH 048/128] Add data store for representative weights Representative weights are currently store in memory and loaded from the accounts table when the node starts. Because the number of representatives has grown significantly, we have to store the weights on disk instead. This commit introduces the store class `rep_weight` for storing the rep weights. --- nano/core_test/CMakeLists.txt | 1 + nano/core_test/rep_weight_store.cpp | 75 +++++++++++++++++++++++++++++ nano/node/make_store.cpp | 1 + nano/store/CMakeLists.txt | 4 ++ nano/store/component.cpp | 6 ++- nano/store/component.hpp | 5 +- nano/store/lmdb/lmdb.cpp | 7 ++- nano/store/lmdb/lmdb.hpp | 3 ++ nano/store/lmdb/rep_weight.cpp | 68 ++++++++++++++++++++++++++ nano/store/lmdb/rep_weight.hpp | 34 +++++++++++++ nano/store/rep_weight.hpp | 32 ++++++++++++ nano/store/rocksdb/rep_weight.cpp | 67 ++++++++++++++++++++++++++ nano/store/rocksdb/rep_weight.hpp | 27 +++++++++++ nano/store/rocksdb/rocksdb.cpp | 24 +++++++-- nano/store/rocksdb/rocksdb.hpp | 3 ++ nano/store/tables.hpp | 3 +- nano/test_common/CMakeLists.txt | 2 + nano/test_common/make_store.cpp | 11 +++++ nano/test_common/make_store.hpp | 13 +++++ 19 files changed, 378 insertions(+), 8 deletions(-) create mode 100644 nano/core_test/rep_weight_store.cpp create mode 100644 nano/store/lmdb/rep_weight.cpp create mode 100644 nano/store/lmdb/rep_weight.hpp create mode 100644 nano/store/rep_weight.hpp create mode 100644 nano/store/rocksdb/rep_weight.cpp create mode 100644 nano/store/rocksdb/rep_weight.hpp create mode 100644 nano/test_common/make_store.cpp create mode 100644 nano/test_common/make_store.hpp diff --git a/nano/core_test/CMakeLists.txt b/nano/core_test/CMakeLists.txt index ba92b4268..23c227442 100644 --- a/nano/core_test/CMakeLists.txt +++ b/nano/core_test/CMakeLists.txt @@ -38,6 +38,7 @@ add_executable( processor_service.cpp rep_crawler.cpp peer_container.cpp + rep_weight_store.cpp scheduler_buckets.cpp request_aggregator.cpp signal_manager.cpp diff --git a/nano/core_test/rep_weight_store.cpp b/nano/core_test/rep_weight_store.cpp new file mode 100644 index 000000000..90d8db2c2 --- /dev/null +++ b/nano/core_test/rep_weight_store.cpp @@ -0,0 +1,75 @@ +#include +#include +#include +#include + +#include + +#include +#include + +TEST (rep_weight_store, empty) +{ + auto store = nano::test::make_store (); + ASSERT_TRUE (!store->init_error ()); + auto txn{ store->tx_begin_read () }; + ASSERT_EQ (0, store->rep_weight.count (txn)); +} + +TEST (rep_weight_store, add_item) +{ + auto store = nano::test::make_store (); + ASSERT_TRUE (!store->init_error ()); + auto txn{ store->tx_begin_write () }; + + nano::account representative{ 123 }; + nano::uint128_t weight{ 456 }; + store->rep_weight.put (txn, representative, weight); + + ASSERT_EQ (1, store->rep_weight.count (txn)); + ASSERT_EQ (weight, store->rep_weight.get (txn, representative)); +} + +TEST (rep_weight_store, del) +{ + auto store = nano::test::make_store (); + ASSERT_TRUE (!store->init_error ()); + auto txn{ store->tx_begin_write () }; + + store->rep_weight.put (txn, 1, 100); + store->rep_weight.put (txn, 2, 200); + store->rep_weight.put (txn, 3, 300); + + store->rep_weight.del (txn, 2); + + ASSERT_EQ (2, store->rep_weight.count (txn)); + ASSERT_EQ (0, store->rep_weight.get (txn, 200)); +} + +TEST (rep_weight_store, for_each_par) +{ + auto store = nano::test::make_store (); + ASSERT_TRUE (!store->init_error ()); + { + auto txn{ store->tx_begin_write () }; + for (auto i = 0; i < 50; ++i) + { + store->rep_weight.put (txn, i, 100); + } + } + + std::atomic_size_t rep_total{ 0 }; + std::atomic_size_t weight_total{ 0 }; + + store->rep_weight.for_each_par ( + [&rep_total, &weight_total] (auto const &, auto i, auto n) { + for (; i != n; ++i) + { + rep_total.fetch_add (static_cast (i->first.number ())); + weight_total.fetch_add (static_cast (i->second.number ())); + } + }); + + ASSERT_EQ (1225, rep_total.load ()); + ASSERT_EQ (50 * 100, weight_total.load ()); +} diff --git a/nano/node/make_store.cpp b/nano/node/make_store.cpp index c3c8c2810..87c15700f 100644 --- a/nano/node/make_store.cpp +++ b/nano/node/make_store.cpp @@ -1,3 +1,4 @@ +#include #include #include #include diff --git a/nano/store/CMakeLists.txt b/nano/store/CMakeLists.txt index bf79b03e3..9ea6c5c73 100644 --- a/nano/store/CMakeLists.txt +++ b/nano/store/CMakeLists.txt @@ -25,6 +25,7 @@ add_library( lmdb/peer.hpp lmdb/pending.hpp lmdb/pruned.hpp + lmdb/rep_weight.hpp lmdb/transaction_impl.hpp lmdb/version.hpp lmdb/wallet_value.hpp @@ -43,6 +44,7 @@ add_library( rocksdb/peer.hpp rocksdb/pending.hpp rocksdb/pruned.hpp + rocksdb/rep_weight.hpp rocksdb/rocksdb.hpp rocksdb/iterator.hpp rocksdb/transaction_impl.hpp @@ -73,6 +75,7 @@ add_library( lmdb/peer.cpp lmdb/pending.cpp lmdb/pruned.cpp + lmdb/rep_weight.cpp lmdb/version.cpp lmdb/wallet_value.cpp online_weight.cpp @@ -89,6 +92,7 @@ add_library( rocksdb/peer.cpp rocksdb/pending.cpp rocksdb/pruned.cpp + rocksdb/rep_weight.cpp rocksdb/rocksdb.cpp rocksdb/transaction.cpp rocksdb/version.cpp diff --git a/nano/store/component.cpp b/nano/store/component.cpp index 838a7b9cb..9e4b7e9db 100644 --- a/nano/store/component.cpp +++ b/nano/store/component.cpp @@ -6,8 +6,9 @@ #include #include #include +#include -nano::store::component::component (nano::store::block & block_store_a, nano::store::frontier & frontier_store_a, nano::store::account & account_store_a, nano::store::pending & pending_store_a, nano::store::online_weight & online_weight_store_a, nano::store::pruned & pruned_store_a, nano::store::peer & peer_store_a, nano::store::confirmation_height & confirmation_height_store_a, nano::store::final_vote & final_vote_store_a, nano::store::version & version_store_a) : +nano::store::component::component (nano::store::block & block_store_a, nano::store::frontier & frontier_store_a, nano::store::account & account_store_a, nano::store::pending & pending_store_a, nano::store::online_weight & online_weight_store_a, nano::store::pruned & pruned_store_a, nano::store::peer & peer_store_a, nano::store::confirmation_height & confirmation_height_store_a, nano::store::final_vote & final_vote_store_a, nano::store::version & version_store_a, nano::store::rep_weight & rep_weight_a) : block (block_store_a), frontier (frontier_store_a), account (account_store_a), @@ -17,7 +18,8 @@ nano::store::component::component (nano::store::block & block_store_a, nano::sto peer (peer_store_a), confirmation_height (confirmation_height_store_a), final_vote (final_vote_store_a), - version (version_store_a) + version (version_store_a), + rep_weight (rep_weight_a) { } diff --git a/nano/store/component.hpp b/nano/store/component.hpp index a675ca562..eaa8faf95 100644 --- a/nano/store/component.hpp +++ b/nano/store/component.hpp @@ -27,6 +27,7 @@ namespace store class pending; class pruned; class version; + class rep_weight; } class ledger_cache; @@ -52,7 +53,8 @@ namespace store nano::store::peer &, nano::store::confirmation_height &, nano::store::final_vote &, - nano::store::version & + nano::store::version &, + nano::store::rep_weight & ); // clang-format on virtual ~component () = default; @@ -68,6 +70,7 @@ namespace store store::frontier & frontier; store::account & account; store::pending & pending; + store::rep_weight & rep_weight; static int constexpr version_minimum{ 21 }; static int constexpr version_current{ 22 }; diff --git a/nano/store/lmdb/lmdb.cpp b/nano/store/lmdb/lmdb.cpp index 4dd9e6440..b5397b50f 100644 --- a/nano/store/lmdb/lmdb.cpp +++ b/nano/store/lmdb/lmdb.cpp @@ -24,7 +24,8 @@ nano::store::lmdb::component::component (nano::logger & logger_a, std::filesyste peer_store, confirmation_height_store, final_vote_store, - version_store + version_store, + rep_weight_store }, // clang-format on block_store{ *this }, @@ -37,6 +38,7 @@ nano::store::lmdb::component::component (nano::logger & logger_a, std::filesyste confirmation_height_store{ *this }, final_vote_store{ *this }, version_store{ *this }, + rep_weight_store{ *this }, logger{ logger_a }, env (error, path_a, nano::store::lmdb::env::options::make ().set_config (lmdb_config_a).set_use_no_mem_init (true)), mdb_txn_tracker (logger_a, txn_tracking_config_a, block_processor_batch_max_time_a), @@ -204,6 +206,7 @@ void nano::store::lmdb::component::open_databases (bool & error_a, store::transa pending_store.pending_handle = pending_store.pending_v0_handle; error_a |= mdb_dbi_open (env.tx (transaction_a), "final_votes", flags, &final_vote_store.final_votes_handle) != 0; error_a |= mdb_dbi_open (env.tx (transaction_a), "blocks", MDB_CREATE, &block_store.blocks_handle) != 0; + mdb_dbi_open (env.tx (transaction_a), "rep_weights", flags, &rep_weight_store.rep_weights_handle); } bool nano::store::lmdb::component::do_upgrades (store::write_transaction & transaction_a, nano::ledger_constants & constants, bool & needs_vacuuming) @@ -339,6 +342,8 @@ MDB_dbi nano::store::lmdb::component::table_to_dbi (tables table_a) const return confirmation_height_store.confirmation_height_handle; case tables::final_votes: return final_vote_store.final_votes_handle; + case tables::rep_weights: + return rep_weight_store.rep_weights_handle; default: release_assert (false); return peer_store.peers_handle; diff --git a/nano/store/lmdb/lmdb.hpp b/nano/store/lmdb/lmdb.hpp index a1f5dda6c..90bbc8ca6 100644 --- a/nano/store/lmdb/lmdb.hpp +++ b/nano/store/lmdb/lmdb.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,7 @@ private: nano::store::lmdb::pending pending_store; nano::store::lmdb::pruned pruned_store; nano::store::lmdb::version version_store; + nano::store::lmdb::rep_weight rep_weight_store; friend class nano::store::lmdb::account; friend class nano::store::lmdb::block; @@ -61,6 +63,7 @@ private: friend class nano::store::lmdb::pending; friend class nano::store::lmdb::pruned; friend class nano::store::lmdb::version; + friend class nano::store::lmdb::rep_weight; public: component (nano::logger &, std::filesystem::path const &, nano::ledger_constants & constants, nano::txn_tracking_config const & txn_tracking_config_a = nano::txn_tracking_config{}, std::chrono::milliseconds block_processor_batch_max_time_a = std::chrono::milliseconds (5000), nano::lmdb_config const & lmdb_config_a = nano::lmdb_config{}, bool backup_before_upgrade = false); diff --git a/nano/store/lmdb/rep_weight.cpp b/nano/store/lmdb/rep_weight.cpp new file mode 100644 index 000000000..529efae68 --- /dev/null +++ b/nano/store/lmdb/rep_weight.cpp @@ -0,0 +1,68 @@ +#include +#include +#include +#include + +#include +#include + +nano::store::lmdb::rep_weight::rep_weight (nano::store::lmdb::component & store_a) : + store{ store_a } +{ +} + +uint64_t nano::store::lmdb::rep_weight::count (store::transaction const & txn_a) +{ + return store.count (txn_a, tables::rep_weights); +} + +nano::uint128_t nano::store::lmdb::rep_weight::get (store::transaction const & txn_a, nano::account const & representative_a) +{ + nano::store::lmdb::db_val value; + auto status = store.get (txn_a, tables::rep_weights, representative_a, value); + release_assert (store.success (status) || store.not_found (status)); + nano::uint128_t weight{ 0 }; + if (store.success (status)) + { + nano::uint128_union weight_union{ value }; + weight = weight_union.number (); + } + return weight; +} + +void nano::store::lmdb::rep_weight::put (store::write_transaction const & txn_a, nano::account const & representative_a, nano::uint128_t const & weight_a) +{ + nano::uint128_union weight{ weight_a }; + auto status = store.put (txn_a, tables::rep_weights, representative_a, weight); + store.release_assert_success (status); +} + +void nano::store::lmdb::rep_weight::del (store::write_transaction const & txn_a, nano::account const & representative_a) +{ + auto status = store.del (txn_a, tables::rep_weights, representative_a); + store.release_assert_success (status); +} + +nano::store::iterator nano::store::lmdb::rep_weight::begin (store::transaction const & transaction_a, nano::account const & representative_a) const +{ + return store.make_iterator (transaction_a, tables::rep_weights, representative_a); +} + +nano::store::iterator nano::store::lmdb::rep_weight::begin (store::transaction const & transaction_a) const +{ + return store.make_iterator (transaction_a, tables::rep_weights); +} + +nano::store::iterator nano::store::lmdb::rep_weight::end () const +{ + return nano::store::iterator (nullptr); +} + +void nano::store::lmdb::rep_weight::for_each_par (std::function, store::iterator)> const & action_a) const +{ + parallel_traversal ( + [&action_a, this] (nano::uint256_t const & start, nano::uint256_t const & end, bool const is_last) { + auto transaction (this->store.tx_begin_read ()); + action_a (transaction, this->begin (transaction, start), !is_last ? this->begin (transaction, end) : this->end ()); + }); +} diff --git a/nano/store/lmdb/rep_weight.hpp b/nano/store/lmdb/rep_weight.hpp new file mode 100644 index 000000000..2a6ef53c5 --- /dev/null +++ b/nano/store/lmdb/rep_weight.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include + +namespace nano::store::lmdb +{ +class component; + +class rep_weight : public nano::store::rep_weight +{ +private: + nano::store::lmdb::component & store; + +public: + explicit rep_weight (nano::store::lmdb::component & store_a); + + uint64_t count (store::transaction const & txn) override; + nano::uint128_t get (store::transaction const & txn_a, nano::account const & representative_a) override; + void put (store::write_transaction const & txn_a, nano::account const & representative_a, nano::uint128_t const & weight_a) override; + void del (store::write_transaction const &, nano::account const & representative_a) override; + store::iterator begin (store::transaction const & transaction_a, nano::account const & representative_a) const override; + store::iterator begin (store::transaction const & transaction_a) const override; + store::iterator end () const override; + void for_each_par (std::function, store::iterator)> const & action_a) const override; + + /** + * Representative weights + * nano::account -> uint128_t + */ + MDB_dbi rep_weights_handle{ 0 }; +}; +} diff --git a/nano/store/rep_weight.hpp b/nano/store/rep_weight.hpp new file mode 100644 index 000000000..72ac9f84a --- /dev/null +++ b/nano/store/rep_weight.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace nano +{ +// class account; +} +namespace nano::store +{ +/** + * A lookup table of all representatives and their vote weight + */ +class rep_weight +{ +public: + virtual ~rep_weight (){}; + virtual uint64_t count (store::transaction const & txn_a) = 0; + virtual nano::uint128_t get (store::transaction const & txn_a, nano::account const & representative_a) = 0; + virtual void put (store::write_transaction const & txn_a, nano::account const & representative_a, nano::uint128_t const & weight_a) = 0; + virtual void del (store::write_transaction const &, nano::account const & representative_a) = 0; + virtual store::iterator begin (store::transaction const & transaction_a, nano::account const & representative_a) const = 0; + virtual store::iterator begin (store::transaction const & transaction_a) const = 0; + virtual store::iterator end () const = 0; + virtual void for_each_par (std::function, store::iterator)> const & action_a) const = 0; +}; +} diff --git a/nano/store/rocksdb/rep_weight.cpp b/nano/store/rocksdb/rep_weight.cpp new file mode 100644 index 000000000..7f29e61e4 --- /dev/null +++ b/nano/store/rocksdb/rep_weight.cpp @@ -0,0 +1,67 @@ +#include +#include +#include +#include + +#include + +nano::store::rocksdb::rep_weight::rep_weight (nano::store::rocksdb::component & store_a) : + store{ store_a } +{ +} + +uint64_t nano::store::rocksdb::rep_weight::count (store::transaction const & txn_a) +{ + return store.count (txn_a, tables::rep_weights); +} + +nano::uint128_t nano::store::rocksdb::rep_weight::get (store::transaction const & txn_a, nano::account const & representative_a) +{ + db_val value; + auto status = store.get (txn_a, tables::rep_weights, representative_a, value); + release_assert (store.success (status) || store.not_found (status)); + nano::uint128_t weight{ 0 }; + if (store.success (status)) + { + nano::uint128_union weight_union{ value }; + weight = weight_union.number (); + } + return weight; +} + +void nano::store::rocksdb::rep_weight::put (store::write_transaction const & txn_a, nano::account const & representative_a, nano::uint128_t const & weight_a) +{ + nano::uint128_union weight{ weight_a }; + auto status = store.put (txn_a, tables::rep_weights, representative_a, weight); + store.release_assert_success (status); +} + +void nano::store::rocksdb::rep_weight::del (store::write_transaction const & txn_a, nano::account const & representative_a) +{ + auto status = store.del (txn_a, tables::rep_weights, representative_a); + store.release_assert_success (status); +} + +nano::store::iterator nano::store::rocksdb::rep_weight::begin (store::transaction const & txn_a, nano::account const & representative_a) const +{ + return store.make_iterator (txn_a, tables::rep_weights, representative_a); +} + +nano::store::iterator nano::store::rocksdb::rep_weight::begin (store::transaction const & txn_a) const +{ + return store.make_iterator (txn_a, tables::rep_weights); +} + +nano::store::iterator nano::store::rocksdb::rep_weight::end () const +{ + return store::iterator (nullptr); +} + +void nano::store::rocksdb::rep_weight::for_each_par (std::function, store::iterator)> const & action_a) const +{ + parallel_traversal ( + [&action_a, this] (nano::uint256_t const & start, nano::uint256_t const & end, bool const is_last) { + auto transaction (this->store.tx_begin_read ()); + action_a (transaction, this->begin (transaction, start), !is_last ? this->begin (transaction, end) : this->end ()); + }); +} \ No newline at end of file diff --git a/nano/store/rocksdb/rep_weight.hpp b/nano/store/rocksdb/rep_weight.hpp new file mode 100644 index 000000000..c86f27150 --- /dev/null +++ b/nano/store/rocksdb/rep_weight.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +namespace nano::store::rocksdb +{ +class component; +} +namespace nano::store::rocksdb +{ +class rep_weight : public nano::store::rep_weight +{ +private: + nano::store::rocksdb::component & store; + +public: + explicit rep_weight (nano::store::rocksdb::component & store_a); + uint64_t count (store::transaction const & txn_a) override; + nano::uint128_t get (store::transaction const & txn_a, nano::account const & representative_a) override; + void put (store::write_transaction const & txn_a, nano::account const & representative_a, nano::uint128_t const & weight_a) override; + void del (store::write_transaction const &, nano::account const & representative_a) override; + store::iterator begin (store::transaction const & txn_a, nano::account const & representative_a) const override; + store::iterator begin (store::transaction const & txn_a) const override; + store::iterator end () const override; + void for_each_par (std::function, store::iterator)> const & action_a) const override; +}; +} diff --git a/nano/store/rocksdb/rocksdb.cpp b/nano/store/rocksdb/rocksdb.cpp index 43b82f2b4..515a03772 100644 --- a/nano/store/rocksdb/rocksdb.cpp +++ b/nano/store/rocksdb/rocksdb.cpp @@ -47,7 +47,8 @@ nano::store::rocksdb::component::component (nano::logger & logger_a, std::filesy peer_store, confirmation_height_store, final_vote_store, - version_store + version_store, + rep_weight_store }, // clang-format on block_store{ *this }, @@ -60,6 +61,7 @@ nano::store::rocksdb::component::component (nano::logger & logger_a, std::filesy confirmation_height_store{ *this }, final_vote_store{ *this }, version_store{ *this }, + rep_weight_store{ *this }, logger{ logger_a }, constants{ constants }, rocksdb_config{ rocksdb_config_a }, @@ -172,7 +174,8 @@ std::unordered_map nano::store::rocksdb::component:: { "peers", tables::peers }, { "confirmation_height", tables::confirmation_height }, { "pruned", tables::pruned }, - { "final_votes", tables::final_votes } }; + { "final_votes", tables::final_votes }, + { "rep_weights", tables::rep_weights } }; debug_assert (map.size () == all_tables ().size () + 1); return map; @@ -384,6 +387,11 @@ rocksdb::ColumnFamilyOptions nano::store::rocksdb::component::get_cf_options (st std::shared_ptr<::rocksdb::TableFactory> table_factory (::rocksdb::NewBlockBasedTableFactory (get_active_table_options (block_cache_size_bytes * 2))); cf_options = get_active_cf_options (table_factory, memtable_size_bytes); } + else if (cf_name_a == "rep_weights") + { + std::shared_ptr<::rocksdb::TableFactory> table_factory (::rocksdb::NewBlockBasedTableFactory (get_active_table_options (block_cache_size_bytes * 2))); + cf_options = get_active_cf_options (table_factory, memtable_size_bytes); + } else if (cf_name_a == ::rocksdb::kDefaultColumnFamilyName) { // Do nothing. @@ -509,6 +517,8 @@ rocksdb::ColumnFamilyHandle * nano::store::rocksdb::component::table_to_column_f return get_column_family ("confirmation_height"); case tables::final_votes: return get_column_family ("final_votes"); + case tables::rep_weights: + return get_column_family ("rep_weights"); default: release_assert (false); return get_column_family (""); @@ -666,6 +676,14 @@ uint64_t nano::store::rocksdb::component::count (store::transaction const & tran ++sum; } } + // rep_weights should only be used in tests otherwise there can be performance issues. + else if (table_a == tables::rep_weights) + { + for (auto i (rep_weight.begin (transaction_a)), n (rep_weight.end ()); i != n; ++i) + { + ++sum; + } + } else { debug_assert (false); @@ -850,7 +868,7 @@ void nano::store::rocksdb::component::on_flush (::rocksdb::FlushJobInfo const & std::vector nano::store::rocksdb::component::all_tables () const { - return std::vector{ tables::accounts, tables::blocks, tables::confirmation_height, tables::final_votes, tables::frontiers, tables::meta, tables::online_weight, tables::peers, tables::pending, tables::pruned, tables::vote }; + return std::vector{ tables::accounts, tables::blocks, tables::confirmation_height, tables::final_votes, tables::frontiers, tables::meta, tables::online_weight, tables::peers, tables::pending, tables::pruned, tables::vote, tables::rep_weights }; } bool nano::store::rocksdb::component::copy_db (std::filesystem::path const & destination_path) diff --git a/nano/store/rocksdb/rocksdb.hpp b/nano/store/rocksdb/rocksdb.hpp index 1c51bbbb0..f23145b02 100644 --- a/nano/store/rocksdb/rocksdb.hpp +++ b/nano/store/rocksdb/rocksdb.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -51,6 +52,7 @@ private: nano::store::rocksdb::pending pending_store; nano::store::rocksdb::pruned pruned_store; nano::store::rocksdb::version version_store; + nano::store::rocksdb::rep_weight rep_weight_store; public: friend class nano::store::rocksdb::account; @@ -63,6 +65,7 @@ public: friend class nano::store::rocksdb::pending; friend class nano::store::rocksdb::pruned; friend class nano::store::rocksdb::version; + friend class nano::store::rocksdb::rep_weight; explicit component (nano::logger &, std::filesystem::path const &, nano::ledger_constants & constants, nano::rocksdb_config const & = nano::rocksdb_config{}, bool open_read_only = false); diff --git a/nano/store/tables.hpp b/nano/store/tables.hpp index e0a03bd02..cf53a0f91 100644 --- a/nano/store/tables.hpp +++ b/nano/store/tables.hpp @@ -18,7 +18,8 @@ enum class tables peers, pending, pruned, - vote + vote, + rep_weights, }; } // namespace nano diff --git a/nano/test_common/CMakeLists.txt b/nano/test_common/CMakeLists.txt index 6271377f4..0f0b9d3c4 100644 --- a/nano/test_common/CMakeLists.txt +++ b/nano/test_common/CMakeLists.txt @@ -4,6 +4,8 @@ add_library( chains.cpp ledger.hpp ledger.cpp + make_store.hpp + make_store.cpp network.hpp network.cpp rate_observer.cpp diff --git a/nano/test_common/make_store.cpp b/nano/test_common/make_store.cpp new file mode 100644 index 000000000..9a980d851 --- /dev/null +++ b/nano/test_common/make_store.cpp @@ -0,0 +1,11 @@ +#include +#include +#include +#include +#include +#include + +std::unique_ptr nano::test::make_store () +{ + return nano::make_store (nano::default_logger (), nano::unique_path (), nano::dev::constants); +} diff --git a/nano/test_common/make_store.hpp b/nano/test_common/make_store.hpp new file mode 100644 index 000000000..084b66a1f --- /dev/null +++ b/nano/test_common/make_store.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace nano::store +{ +class component; +} + +namespace nano::test +{ +std::unique_ptr make_store (); +} From 2f69c60c6776a58795b69a98d276ba699d05526a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20W=C3=B3jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Tue, 19 Mar 2024 10:11:40 +0100 Subject: [PATCH 049/128] Remove channel comparison functions (#4500) * Remove unused channel comparison functions * Rename `set_endpoint` to `update_endpoint` --- nano/core_test/network.cpp | 2 - nano/core_test/rep_crawler.cpp | 4 +- nano/node/bootstrap/bootstrap_connections.cpp | 2 +- nano/node/transport/channel.hpp | 47 +------------------ nano/node/transport/fake.cpp | 16 ------- nano/node/transport/fake.hpp | 4 -- nano/node/transport/inproc.cpp | 11 ----- nano/node/transport/inproc.hpp | 6 --- nano/node/transport/tcp.cpp | 32 +------------ nano/node/transport/tcp.hpp | 17 +++---- 10 files changed, 15 insertions(+), 126 deletions(-) diff --git a/nano/core_test/network.cpp b/nano/core_test/network.cpp index cf5188bc5..c7152c664 100644 --- a/nano/core_test/network.cpp +++ b/nano/core_test/network.cpp @@ -1096,8 +1096,6 @@ TEST (network, loopback_channel) ASSERT_EQ (channel1.get_node_id (), node1.node_id.pub); ASSERT_EQ (channel1.get_node_id_optional ().value_or (0), node1.node_id.pub); nano::transport::inproc::channel channel2 (node2, node2); - ASSERT_EQ (channel1, channel1); - ASSERT_FALSE (channel1 == channel2); ++node1.network.port; ASSERT_NE (channel1.get_endpoint (), node1.network.endpoint ()); } diff --git a/nano/core_test/rep_crawler.cpp b/nano/core_test/rep_crawler.cpp index 06d16b895..6e78d3a67 100644 --- a/nano/core_test/rep_crawler.cpp +++ b/nano/core_test/rep_crawler.cpp @@ -106,7 +106,7 @@ TEST (rep_crawler, rep_weight) ASSERT_EQ (1, reps.size ()); ASSERT_EQ (node.balance (nano::dev::genesis_key.pub), node.ledger.weight (reps[0].account)); ASSERT_EQ (nano::dev::genesis_key.pub, reps[0].account); - ASSERT_EQ (*channel1, *reps[0].channel); + ASSERT_EQ (channel1, reps[0].channel); ASSERT_TRUE (node.rep_crawler.is_pr (channel1)); ASSERT_FALSE (node.rep_crawler.is_pr (channel2)); ASSERT_TRUE (node.rep_crawler.is_pr (channel3)); @@ -189,7 +189,7 @@ TEST (rep_crawler, rep_remove) ASSERT_EQ (1, reps.size ()); ASSERT_EQ (searching_node.minimum_principal_weight () * 2, searching_node.ledger.weight (reps[0].account)); ASSERT_EQ (keys_rep1.pub, reps[0].account); - ASSERT_EQ (*channel_rep1, *reps[0].channel); + ASSERT_EQ (channel_rep1, reps[0].channel); // When rep1 disconnects then rep1 should not be found anymore channel_rep1->close (); diff --git a/nano/node/bootstrap/bootstrap_connections.cpp b/nano/node/bootstrap/bootstrap_connections.cpp index c843148d2..cbee474aa 100644 --- a/nano/node/bootstrap/bootstrap_connections.cpp +++ b/nano/node/bootstrap/bootstrap_connections.cpp @@ -25,7 +25,7 @@ nano::bootstrap_client::bootstrap_client (std::shared_ptr const & no { ++node_a->bootstrap_initiator.connections->connections_count; receive_buffer->resize (256); - channel->set_endpoint (); + channel->update_endpoint (); } nano::bootstrap_client::~bootstrap_client () diff --git a/nano/node/transport/channel.hpp b/nano/node/transport/channel.hpp index 35f8f37ba..319229035 100644 --- a/nano/node/transport/channel.hpp +++ b/nano/node/transport/channel.hpp @@ -26,9 +26,6 @@ public: explicit channel (nano::node &); virtual ~channel () = default; - virtual std::size_t hash_code () const = 0; - virtual bool operator== (nano::transport::channel const &) const = 0; - void send (nano::message & message_a, std::function const & callback_a = nullptr, nano::transport::buffer_drop_policy policy_a = nano::transport::buffer_drop_policy::limiter, @@ -148,46 +145,4 @@ protected: public: // Logging virtual void operator() (nano::object_stream &) const; }; -} - -namespace std -{ -template <> -struct hash<::nano::transport::channel> -{ - std::size_t operator() (::nano::transport::channel const & channel_a) const - { - return channel_a.hash_code (); - } -}; -template <> -struct equal_to> -{ - bool operator() (std::reference_wrapper<::nano::transport::channel const> const & lhs, std::reference_wrapper<::nano::transport::channel const> const & rhs) const - { - return lhs.get () == rhs.get (); - } -}; -} - -namespace boost -{ -template <> -struct hash<::nano::transport::channel> -{ - std::size_t operator() (::nano::transport::channel const & channel_a) const - { - std::hash<::nano::transport::channel> hash; - return hash (channel_a); - } -}; -template <> -struct hash> -{ - std::size_t operator() (std::reference_wrapper<::nano::transport::channel const> const & channel_a) const - { - std::hash<::nano::transport::channel> hash; - return hash (channel_a.get ()); - } -}; -} +} \ No newline at end of file diff --git a/nano/node/transport/fake.cpp b/nano/node/transport/fake.cpp index 02d705ee3..aec913adf 100644 --- a/nano/node/transport/fake.cpp +++ b/nano/node/transport/fake.cpp @@ -26,22 +26,6 @@ void nano::transport::fake::channel::send_buffer (nano::shared_const_buffer cons } } -std::size_t nano::transport::fake::channel::hash_code () const -{ - std::hash<::nano::endpoint> hash; - return hash (endpoint); -} - -bool nano::transport::fake::channel::operator== (nano::transport::channel const & other_a) const -{ - return endpoint == other_a.get_endpoint (); -} - -bool nano::transport::fake::channel::operator== (nano::transport::fake::channel const & other_a) const -{ - return endpoint == other_a.get_endpoint (); -} - std::string nano::transport::fake::channel::to_string () const { return boost::str (boost::format ("%1%") % endpoint); diff --git a/nano/node/transport/fake.hpp b/nano/node/transport/fake.hpp index 5c720158b..34d0a2328 100644 --- a/nano/node/transport/fake.hpp +++ b/nano/node/transport/fake.hpp @@ -18,7 +18,6 @@ namespace transport explicit channel (nano::node &); std::string to_string () const override; - std::size_t hash_code () const override; void send_buffer ( nano::shared_const_buffer const &, @@ -26,9 +25,6 @@ namespace transport nano::transport::buffer_drop_policy = nano::transport::buffer_drop_policy::limiter, nano::transport::traffic_type = nano::transport::traffic_type::generic) override; - bool operator== (nano::transport::channel const &) const override; - bool operator== (nano::transport::fake::channel const & other_a) const; - void set_endpoint (nano::endpoint const & endpoint_a) { endpoint = endpoint_a; diff --git a/nano/node/transport/inproc.cpp b/nano/node/transport/inproc.cpp index 2dfb36d12..e1c8383f0 100644 --- a/nano/node/transport/inproc.cpp +++ b/nano/node/transport/inproc.cpp @@ -14,17 +14,6 @@ nano::transport::inproc::channel::channel (nano::node & node, nano::node & desti set_network_version (node.network_params.network.protocol_version); } -std::size_t nano::transport::inproc::channel::hash_code () const -{ - std::hash<::nano::endpoint> hash; - return hash (endpoint); -} - -bool nano::transport::inproc::channel::operator== (nano::transport::channel const & other_a) const -{ - return endpoint == other_a.get_endpoint (); -} - /** * Send the buffer to the peer and call the callback function when done. The call never fails. * Note that the inbound message visitor will be called before the callback because it is called directly whereas the callback is spawned in the background. diff --git a/nano/node/transport/inproc.hpp b/nano/node/transport/inproc.hpp index fc318ef51..092ab9239 100644 --- a/nano/node/transport/inproc.hpp +++ b/nano/node/transport/inproc.hpp @@ -16,17 +16,11 @@ namespace transport { public: explicit channel (nano::node & node, nano::node & destination); - std::size_t hash_code () const override; - bool operator== (nano::transport::channel const &) const override; // TODO: investigate clang-tidy warning about default parameters on virtual/override functions void send_buffer (nano::shared_const_buffer const &, std::function const & = nullptr, nano::transport::buffer_drop_policy = nano::transport::buffer_drop_policy::limiter, nano::transport::traffic_type = nano::transport::traffic_type::generic) override; std::string to_string () const override; - bool operator== (nano::transport::inproc::channel const & other_a) const - { - return endpoint == other_a.get_endpoint (); - } nano::endpoint get_endpoint () const override { diff --git a/nano/node/transport/tcp.cpp b/nano/node/transport/tcp.cpp index ac7ba411f..1313b77d8 100644 --- a/nano/node/transport/tcp.cpp +++ b/nano/node/transport/tcp.cpp @@ -30,23 +30,6 @@ nano::transport::channel_tcp::~channel_tcp () } } -std::size_t nano::transport::channel_tcp::hash_code () const -{ - std::hash<::nano::tcp_endpoint> hash; - return hash (get_tcp_endpoint ()); -} - -bool nano::transport::channel_tcp::operator== (nano::transport::channel const & other_a) const -{ - bool result (false); - auto other_l (dynamic_cast (&other_a)); - if (other_l != nullptr) - { - return *this == *other_l; - } - return result; -} - void nano::transport::channel_tcp::send_buffer (nano::shared_const_buffer const & buffer_a, std::function const & callback_a, nano::transport::buffer_drop_policy policy_a, nano::transport::traffic_type traffic_type) { if (auto socket_l = socket.lock ()) @@ -102,17 +85,6 @@ std::string nano::transport::channel_tcp::to_string () const return nano::util::to_str (get_tcp_endpoint ()); } -void nano::transport::channel_tcp::set_endpoint () -{ - nano::lock_guard lk{ channel_mutex }; - debug_assert (endpoint == nano::tcp_endpoint (boost::asio::ip::address_v6::any (), 0)); // Not initialized endpoint value - // Calculate TCP socket endpoint - if (auto socket_l = socket.lock ()) - { - endpoint = socket_l->remote_endpoint (); - } -} - void nano::transport::channel_tcp::operator() (nano::object_stream & obs) const { nano::transport::channel::operator() (obs); // Write common data @@ -367,7 +339,7 @@ void nano::transport::tcp_channels::process_message (nano::message const & messa { // Add temporary channel auto temporary_channel (std::make_shared (node, socket_a)); - temporary_channel->set_endpoint (); + temporary_channel->update_endpoint (); debug_assert (endpoint_a == temporary_channel->get_tcp_endpoint ()); temporary_channel->set_node_id (node_id_a); temporary_channel->set_network_version (message_a.header.version_using); @@ -625,7 +597,7 @@ void nano::transport::tcp_channels::start_tcp (nano::endpoint const & endpoint_a nano::util::to_str (endpoint_a), (query ? query->cookie.to_string () : "")); - channel->set_endpoint (); + channel->update_endpoint (); std::shared_ptr> receive_buffer (std::make_shared> ()); receive_buffer->resize (256); channel->send (message, [node_w, channel, endpoint_a, receive_buffer] (boost::system::error_code const & ec, std::size_t size_a) { diff --git a/nano/node/transport/tcp.hpp b/nano/node/transport/tcp.hpp index 0402b915a..a1430171a 100644 --- a/nano/node/transport/tcp.hpp +++ b/nano/node/transport/tcp.hpp @@ -63,19 +63,20 @@ namespace transport channel_tcp (nano::node &, std::weak_ptr); ~channel_tcp () override; - std::size_t hash_code () const override; - bool operator== (nano::transport::channel const &) const override; - // TODO: investigate clang-tidy warning about default parameters on virtual/override functions// void send_buffer (nano::shared_const_buffer const &, std::function const & = nullptr, nano::transport::buffer_drop_policy = nano::transport::buffer_drop_policy::limiter, nano::transport::traffic_type = nano::transport::traffic_type::generic) override; std::string to_string () const override; - bool operator== (nano::transport::channel_tcp const & other_a) const - { - return &node == &other_a.node && socket.lock () == other_a.socket.lock (); - } - void set_endpoint (); + void update_endpoint () + { + nano::lock_guard lk (channel_mutex); + debug_assert (endpoint == nano::tcp_endpoint (boost::asio::ip::address_v6::any (), 0)); // Not initialized endpoint value + if (auto socket_l = socket.lock ()) + { + endpoint = socket_l->remote_endpoint (); + } + } nano::endpoint get_endpoint () const override { From 57104053652658d3abc01611cebf3c8cf7933201 Mon Sep 17 00:00:00 2001 From: clemahieu Date: Tue, 19 Mar 2024 09:48:55 +0000 Subject: [PATCH 050/128] Update googletest to v1.14 (#4503) Fixes cmake warning about version 3.5 --- submodules/gtest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/gtest b/submodules/gtest index e2239ee60..f8d7d77c0 160000 --- a/submodules/gtest +++ b/submodules/gtest @@ -1 +1 @@ -Subproject commit e2239ee6043f73722e7aa812a459f54a28552929 +Subproject commit f8d7d77c06936315286eb55f8de22cd23c188571 From f64d9653214aa7dedc34663e812dac8f6085048a Mon Sep 17 00:00:00 2001 From: clemahieu Date: Tue, 19 Mar 2024 12:56:05 +0000 Subject: [PATCH 051/128] Remove final vote canary code as final votes are now always enabled. (#4502) --- nano/node/active_transactions.cpp | 10 ---------- nano/node/election.cpp | 4 ++-- nano/secure/common.cpp | 20 +------------------- nano/secure/common.hpp | 10 ---------- nano/secure/ledger.cpp | 7 ------- nano/secure/ledger_cache.hpp | 1 - nano/store/component.cpp | 1 - 7 files changed, 3 insertions(+), 50 deletions(-) diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 4afc45e2a..f2b9b61eb 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -190,16 +190,6 @@ void nano::active_transactions::notify_observers (nano::election_status const & void nano::active_transactions::handle_final_votes_confirmation (std::shared_ptr const & block, nano::store::read_transaction const & transaction, nano::election_status_type status) { auto account = block->account (); - - bool is_canary_not_set = !node.ledger.cache.final_votes_confirmation_canary.load (); - bool is_canary_account = account == node.network_params.ledger.final_votes_canary_account; - bool is_height_above_threshold = block->sideband ().height >= node.network_params.ledger.final_votes_canary_height; - - if (is_canary_not_set && is_canary_account && is_height_above_threshold) - { - node.ledger.cache.final_votes_confirmation_canary.store (true); - } - bool cemented_bootstrap_count_reached = node.ledger.cache.cemented_count >= node.ledger.bootstrap_weight_max_blocks; bool was_active = status == nano::election_status_type::active_confirmed_quorum || status == nano::election_status_type::active_confirmation_height; diff --git a/nano/node/election.cpp b/nano/node/election.cpp index e0eebd181..d179dabdb 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -397,11 +397,11 @@ void nano::election::confirm_if_quorum (nano::unique_lock & lock_a) } if (have_quorum (tally_l)) { - if (node.ledger.cache.final_votes_confirmation_canary.load () && !is_quorum.exchange (true) && node.config.enable_voting && node.wallets.reps ().voting > 0) + if (!is_quorum.exchange (true) && node.config.enable_voting && node.wallets.reps ().voting > 0) { node.final_generator.add (root, status.winner->hash ()); } - if (!node.ledger.cache.final_votes_confirmation_canary.load () || final_weight >= node.online_reps.delta ()) + if (final_weight >= node.online_reps.delta ()) { confirm_once (lock_a, nano::election_status_type::active_confirmed_quorum); } diff --git a/nano/secure/common.cpp b/nano/secure/common.cpp index d1943ba2c..385205332 100644 --- a/nano/secure/common.cpp +++ b/nano/secure/common.cpp @@ -70,10 +70,6 @@ std::shared_ptr parse_block_from_genesis_data (std::string const & boost::property_tree::read_json (istream, tree); return nano::deserialize_block_json (tree); } - -char const * beta_canary_public_key_data = "259a438a8f9f9226130c84d902c237af3e57c0981c7d709c288046b110d8c8ac"; // nano_33nefchqmo4ifr3bpfw4ecwjcg87semfhit8prwi7zzd8shjr8c9qdxeqmnx -char const * live_canary_public_key_data = "7CBAF192A3763DAEC9F9BAC1B2CDF665D8369F8400B4BC5AB4BA31C00BAA4404"; // nano_1z7ty8bc8xjxou6zmgp3pd8zesgr8thra17nqjfdbgjjr17tnj16fjntfqfn -std::string const test_canary_public_key_data = nano::get_env_or_default ("NANO_TEST_CANARY_PUB", "3BAD2C554ACE05F5E528FBBCE79D51E552C55FA765CCFD89B289C4835DE5F04A"); // nano_1gxf7jcnomi7yqkkjyxwwygo5sckrohtgsgezp6u74g6ifgydw4cajwbk8bf } nano::keypair nano::dev::genesis_key{ dev_private_key_data }; @@ -111,21 +107,7 @@ nano::ledger_constants::ledger_constants (nano::work_thresholds & work, nano::ne : network_a == nano::networks::nano_test_network ? nano_test_genesis : nano_live_genesis), genesis_amount{ std::numeric_limits::max () }, - burn_account{}, - nano_dev_final_votes_canary_account (dev_public_key_data), - nano_beta_final_votes_canary_account (beta_canary_public_key_data), - nano_live_final_votes_canary_account (live_canary_public_key_data), - nano_test_final_votes_canary_account (test_canary_public_key_data), - final_votes_canary_account (network_a == nano::networks::nano_dev_network ? nano_dev_final_votes_canary_account : network_a == nano::networks::nano_beta_network ? nano_beta_final_votes_canary_account - : network_a == nano::networks::nano_test_network ? nano_test_final_votes_canary_account - : nano_live_final_votes_canary_account), - nano_dev_final_votes_canary_height (1), - nano_beta_final_votes_canary_height (1), - nano_live_final_votes_canary_height (1), - nano_test_final_votes_canary_height (1), - final_votes_canary_height (network_a == nano::networks::nano_dev_network ? nano_dev_final_votes_canary_height : network_a == nano::networks::nano_beta_network ? nano_beta_final_votes_canary_height - : network_a == nano::networks::nano_test_network ? nano_test_final_votes_canary_height - : nano_live_final_votes_canary_height) + burn_account{} { nano_beta_genesis->sideband_set (nano::block_sideband (nano_beta_genesis->account_field ().value (), 0, std::numeric_limits::max (), 1, nano::seconds_since_epoch (), nano::epoch::epoch_0, false, false, false, nano::epoch::epoch_0)); nano_dev_genesis->sideband_set (nano::block_sideband (nano_dev_genesis->account_field ().value (), 0, std::numeric_limits::max (), 1, nano::seconds_since_epoch (), nano::epoch::epoch_0, false, false, false, nano::epoch::epoch_0)); diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index e1d43fec8..0928289fd 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -249,16 +249,6 @@ public: std::shared_ptr genesis; nano::uint128_t genesis_amount; nano::account burn_account; - nano::account nano_dev_final_votes_canary_account; - nano::account nano_beta_final_votes_canary_account; - nano::account nano_live_final_votes_canary_account; - nano::account nano_test_final_votes_canary_account; - nano::account final_votes_canary_account; - uint64_t nano_dev_final_votes_canary_height; - uint64_t nano_beta_final_votes_canary_height; - uint64_t nano_live_final_votes_canary_height; - uint64_t nano_test_final_votes_canary_height; - uint64_t final_votes_canary_height; nano::epochs epochs; }; diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 55570fcf8..15fb1b81b 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -796,13 +796,6 @@ void nano::ledger::initialize (nano::generate_cache_flags const & generate_cache auto transaction (store.tx_begin_read ()); cache.pruned_count = store.pruned.count (transaction); - - // Final votes requirement for confirmation canary block - nano::confirmation_height_info confirmation_height_info; - if (!store.confirmation_height.get (transaction, constants.final_votes_canary_account, confirmation_height_info)) - { - cache.final_votes_confirmation_canary = (confirmation_height_info.height >= constants.final_votes_canary_height); - } } // Balance for account containing hash diff --git a/nano/secure/ledger_cache.hpp b/nano/secure/ledger_cache.hpp index 726a1e111..4cc0670ba 100644 --- a/nano/secure/ledger_cache.hpp +++ b/nano/secure/ledger_cache.hpp @@ -15,6 +15,5 @@ public: std::atomic block_count{ 0 }; std::atomic pruned_count{ 0 }; std::atomic account_count{ 0 }; - std::atomic final_votes_confirmation_canary{ false }; }; } diff --git a/nano/store/component.cpp b/nano/store/component.cpp index 9e4b7e9db..911b0ac85 100644 --- a/nano/store/component.cpp +++ b/nano/store/component.cpp @@ -36,7 +36,6 @@ void nano::store::component::initialize (store::write_transaction const & transa ++ledger_cache_a.block_count; confirmation_height.put (transaction_a, constants.genesis->account (), nano::confirmation_height_info{ 1, constants.genesis->hash () }); ++ledger_cache_a.cemented_count; - ledger_cache_a.final_votes_confirmation_canary = (constants.final_votes_canary_account == constants.genesis->account () && 1 >= constants.final_votes_canary_height); account.put (transaction_a, constants.genesis->account (), { hash_l, constants.genesis->account (), constants.genesis->hash (), std::numeric_limits::max (), nano::seconds_since_epoch (), 1, nano::epoch::epoch_0 }); ++ledger_cache_a.account_count; ledger_cache_a.rep_weights.representation_put (constants.genesis->account (), std::numeric_limits::max ()); From ab4e51de8fdd85ae8b88f06eb21c2a34f508ff97 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Tue, 19 Mar 2024 10:24:02 +0000 Subject: [PATCH 052/128] Add block::is_epoch query --- nano/lib/blocks.cpp | 12 ++++++++++++ nano/lib/blocks.hpp | 1 + 2 files changed, 13 insertions(+) diff --git a/nano/lib/blocks.cpp b/nano/lib/blocks.cpp index 060681ac1..9d38cd404 100644 --- a/nano/lib/blocks.cpp +++ b/nano/lib/blocks.cpp @@ -162,6 +162,18 @@ bool nano::block::is_change () const noexcept } } +bool nano::block::is_epoch () const noexcept +{ + release_assert (has_sideband ()); + switch (type ()) + { + case nano::block_type::state: + return sideband ().details.is_epoch; + default: + return false; + } +} + nano::block_hash const & nano::block::hash () const { if (!cached_hash.is_zero ()) diff --git a/nano/lib/blocks.hpp b/nano/lib/blocks.hpp index d6cca7437..99abb9743 100644 --- a/nano/lib/blocks.hpp +++ b/nano/lib/blocks.hpp @@ -55,6 +55,7 @@ public: bool is_send () const noexcept; bool is_receive () const noexcept; bool is_change () const noexcept; + bool is_epoch () const noexcept; public: // Direct access to the block fields or nullopt if the block type does not have the specified field // Returns account field or account from sideband From 328c340529aee3ea65f3b464b767a740a21e416a Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Tue, 19 Mar 2024 10:45:16 +0000 Subject: [PATCH 053/128] Use active_transactions::notify_observers for both active/inactive confirmation. Previously the account_balance observers would not be notified if a block was indirectly confirmed without an election --- nano/node/active_transactions.cpp | 3 ++- nano/node/node_observers.hpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index f2b9b61eb..7620cb2f5 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -126,7 +126,8 @@ void nano::active_transactions::process_inactive_confirmation (nano::store::read bool is_state_epoch = false; nano::account pending_account{}; node.process_confirmed_data (transaction, block, block->hash (), account, amount, is_state_send, is_state_epoch, pending_account); - node.observers.blocks.notify (nano::election_status{ block, 0, 0, std::chrono::duration_cast (std::chrono::system_clock::now ().time_since_epoch ()), std::chrono::duration_values::zero (), 0, 1, 0, nano::election_status_type::inactive_confirmation_height }, {}, account, amount, is_state_send, is_state_epoch); + nano::election_status status{ block, 0, 0, std::chrono::duration_cast (std::chrono::system_clock::now ().time_since_epoch ()), std::chrono::duration_values::zero (), 0, 1, 0, nano::election_status_type::inactive_confirmation_height }; + notify_observers (status, {}, account, amount, is_state_send, is_state_epoch, pending_account); } void nano::active_transactions::process_active_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr const & block, nano::election_status_type status_type) diff --git a/nano/node/node_observers.hpp b/nano/node/node_observers.hpp index 5526c957c..8825655c5 100644 --- a/nano/node/node_observers.hpp +++ b/nano/node/node_observers.hpp @@ -12,7 +12,7 @@ class node_observers final { public: using blocks_t = nano::observer_set const &, nano::account const &, nano::uint128_t const &, bool, bool>; - blocks_t blocks; + blocks_t blocks; // Notification upon election completion or cancellation nano::observer_set wallet; nano::observer_set, std::shared_ptr, nano::vote_code> vote; nano::observer_set active_started; From c3b3614223baa1f738806d0fec61b53581940628 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Tue, 19 Mar 2024 11:08:25 +0000 Subject: [PATCH 054/128] Inlining single usage of handle_block_confirmation in to caller. --- nano/node/active_transactions.cpp | 13 ++++--------- nano/node/active_transactions.hpp | 1 - 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 7620cb2f5..8ec3815d7 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -158,20 +158,15 @@ void nano::active_transactions::handle_confirmation (nano::store::read_transacti bool is_state_epoch = false; nano::account pending_account; - handle_block_confirmation (transaction, block, hash, account, amount, is_state_send, is_state_epoch, pending_account); - - auto status = election->set_status_type (status_type); - auto votes = election->votes_with_weight (); - notify_observers (status, votes, account, amount, is_state_send, is_state_epoch, pending_account); -} - -void nano::active_transactions::handle_block_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr const & block, nano::block_hash const & hash, nano::account & account, nano::uint128_t & amount, bool & is_state_send, bool & is_state_epoch, nano::account & pending_account) -{ if (block->is_send ()) { node.receive_confirmed (transaction, hash, block->destination ()); } node.process_confirmed_data (transaction, block, hash, account, amount, is_state_send, is_state_epoch, pending_account); + + auto status = election->set_status_type (status_type); + auto votes = election->votes_with_weight (); + notify_observers (status, votes, account, amount, is_state_send, is_state_epoch, pending_account); } void nano::active_transactions::notify_observers (nano::election_status const & status, std::vector const & votes, nano::account const & account, nano::uint128_t amount, bool is_state_send, bool is_state_epoch, nano::account const & pending_account) diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index c2b945a9a..791054879 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -201,7 +201,6 @@ private: void handle_final_votes_confirmation (std::shared_ptr const & block, nano::store::read_transaction const & transaction, nano::election_status_type status); void handle_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr const & block, std::shared_ptr election, nano::election_status_type status); void activate_successors (const nano::account & account, std::shared_ptr const & block, nano::store::read_transaction const & transaction); - void handle_block_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr const & block, nano::block_hash const & hash, nano::account & account, nano::uint128_t & amount, bool & is_state_send, bool & is_state_epoch, nano::account & pending_account); void notify_observers (nano::election_status const & status, std::vector const & votes, nano::account const & account, nano::uint128_t amount, bool is_state_send, bool is_state_epoch, nano::account const & pending_account); private: // Dependencies From 37a27400f1d40ddb9806cd9f02f057a37bf760d6 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Tue, 19 Mar 2024 11:18:49 +0000 Subject: [PATCH 055/128] Remove node::process_confirmed_data which only served as a helper for active_transactions. This data can now be easily obtained directly from block objects. --- nano/node/active_transactions.cpp | 29 ++++++++-------------- nano/node/active_transactions.hpp | 2 +- nano/node/node.cpp | 41 ------------------------------- nano/node/node.hpp | 1 - 4 files changed, 11 insertions(+), 62 deletions(-) diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 8ec3815d7..6b001637f 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -120,14 +120,8 @@ boost::optional nano::active_transactions::election_ void nano::active_transactions::process_inactive_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr const & block) { - nano::account account; - nano::uint128_t amount{ 0 }; - bool is_state_send = false; - bool is_state_epoch = false; - nano::account pending_account{}; - node.process_confirmed_data (transaction, block, block->hash (), account, amount, is_state_send, is_state_epoch, pending_account); nano::election_status status{ block, 0, 0, std::chrono::duration_cast (std::chrono::system_clock::now ().time_since_epoch ()), std::chrono::duration_values::zero (), 0, 1, 0, nano::election_status_type::inactive_confirmation_height }; - notify_observers (status, {}, account, amount, is_state_send, is_state_epoch, pending_account); + notify_observers (transaction, status, {}); } void nano::active_transactions::process_active_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr const & block, nano::election_status_type status_type) @@ -152,33 +146,30 @@ void nano::active_transactions::handle_confirmation (nano::store::read_transacti nano::block_hash hash = block->hash (); recently_cemented.put (election->get_status ()); - nano::account account; - nano::uint128_t amount (0); - bool is_state_send = false; - bool is_state_epoch = false; - nano::account pending_account; - if (block->is_send ()) { node.receive_confirmed (transaction, hash, block->destination ()); } - node.process_confirmed_data (transaction, block, hash, account, amount, is_state_send, is_state_epoch, pending_account); - auto status = election->set_status_type (status_type); auto votes = election->votes_with_weight (); - notify_observers (status, votes, account, amount, is_state_send, is_state_epoch, pending_account); + notify_observers (transaction, status, votes); } -void nano::active_transactions::notify_observers (nano::election_status const & status, std::vector const & votes, nano::account const & account, nano::uint128_t amount, bool is_state_send, bool is_state_epoch, nano::account const & pending_account) +void nano::active_transactions::notify_observers (nano::store::read_transaction const & transaction, nano::election_status const & status, std::vector const & votes) { + auto block = status.winner; + auto account = block->account (); + auto amount = node.ledger.amount (transaction, block->hash ()).value_or (0); + auto is_state_send = block->type () == block_type::state && block->is_send (); + auto is_state_epoch = block->type () == block_type::state && block->is_epoch (); node.observers.blocks.notify (status, votes, account, amount, is_state_send, is_state_epoch); if (amount > 0) { node.observers.account_balance.notify (account, false); - if (!pending_account.is_zero ()) + if (block->is_send ()) { - node.observers.account_balance.notify (pending_account, true); + node.observers.account_balance.notify (block->destination (), true); } } } diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index 791054879..e1c4a5f8e 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -201,7 +201,7 @@ private: void handle_final_votes_confirmation (std::shared_ptr const & block, nano::store::read_transaction const & transaction, nano::election_status_type status); void handle_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr const & block, std::shared_ptr election, nano::election_status_type status); void activate_successors (const nano::account & account, std::shared_ptr const & block, nano::store::read_transaction const & transaction); - void notify_observers (nano::election_status const & status, std::vector const & votes, nano::account const & account, nano::uint128_t amount, bool is_state_send, bool is_state_epoch, nano::account const & pending_account); + void notify_observers (nano::store::read_transaction const & transaction, nano::election_status const & status, std::vector const & votes); private: // Dependencies nano::node & node; diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 054e3669f..c169e8aef 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -1212,47 +1212,6 @@ void nano::node::receive_confirmed (store::transaction const & block_transaction } } -void nano::node::process_confirmed_data (store::transaction const & transaction_a, std::shared_ptr const & block_a, nano::block_hash const & hash_a, nano::account & account_a, nano::uint128_t & amount_a, bool & is_state_send_a, bool & is_state_epoch_a, nano::account & pending_account_a) -{ - // Faster account calculation - account_a = block_a->account (); - // Faster amount calculation - auto previous (block_a->previous ()); - auto previous_balance = ledger.balance (transaction_a, previous); - auto block_balance = block_a->balance (); - if (hash_a != ledger.constants.genesis->account ()) - { - if (previous_balance) - { - amount_a = block_balance > previous_balance.value () ? block_balance.number () - previous_balance.value () : previous_balance.value () - block_balance.number (); - } - else - { - amount_a = 0; - } - } - else - { - amount_a = nano::dev::constants.genesis_amount; - } - if (auto state = dynamic_cast (block_a.get ())) - { - if (state->hashables.balance < previous_balance) - { - is_state_send_a = true; - } - if (amount_a == 0 && network_params.ledger.epochs.is_epoch_link (state->link_field ().value ())) - { - is_state_epoch_a = true; - } - pending_account_a = state->hashables.link.as_account (); - } - if (auto send = dynamic_cast (block_a.get ())) - { - pending_account_a = send->hashables.destination; - } -} - void nano::node::process_confirmed (nano::election_status const & status_a, uint64_t iteration_a) { auto hash (status_a.winner->hash ()); diff --git a/nano/node/node.hpp b/nano/node/node.hpp index 65ad016ab..0b4259316 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -83,7 +83,6 @@ public: std::shared_ptr shared (); int store_version (); void receive_confirmed (store::transaction const & block_transaction_a, nano::block_hash const & hash_a, nano::account const & destination_a); - void process_confirmed_data (store::transaction const &, std::shared_ptr const &, nano::block_hash const &, nano::account &, nano::uint128_t &, bool &, bool &, nano::account &); void process_confirmed (nano::election_status const &, uint64_t = 0); void process_active (std::shared_ptr const &); std::optional process_local (std::shared_ptr const &); From 144c602b66f217d7deae4897130bfa610fe6ae91 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Tue, 19 Mar 2024 11:37:16 +0000 Subject: [PATCH 056/128] Move wallet receiving in to its own observer. This is an interaction between the ledger when a block is confirmed and the wallets that might need to generate receive blocks. Previously this functionality was unnecessarily being handled by the active_transactions class. It could also miss notifications if the block was indirectly confirmed without an active election, or if the election had already been removed from active_transactions. --- nano/node/active_transactions.cpp | 4 ---- nano/node/node.cpp | 7 +++++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 6b001637f..d2fbb6f31 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -146,10 +146,6 @@ void nano::active_transactions::handle_confirmation (nano::store::read_transacti nano::block_hash hash = block->hash (); recently_cemented.put (election->get_status ()); - if (block->is_send ()) - { - node.receive_confirmed (transaction, hash, block->destination ()); - } auto status = election->set_status_type (status_type); auto votes = election->votes_with_weight (); notify_observers (transaction, status, votes); diff --git a/nano/node/node.cpp b/nano/node/node.cpp index c169e8aef..338bab5fc 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -435,6 +435,13 @@ nano::node::node (boost::asio::io_context & io_ctx_a, std::filesystem::path cons std::exit (1); } } + confirmation_height_processor.add_cemented_observer ([this] (auto const & block) { + if (block->is_send ()) + { + auto transaction = store.tx_begin_read (); + receive_confirmed (transaction, block->hash (), block->destination ()); + } + }); } node_initialized_latch.count_down (); } From 315b77742c9493c6950ee062b04b001fa6c576f0 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Tue, 19 Mar 2024 12:05:10 +0000 Subject: [PATCH 057/128] Remove unused transaction parameters. --- nano/node/active_transactions.cpp | 10 +++++----- nano/node/active_transactions.hpp | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index d2fbb6f31..1b9c4af78 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -82,12 +82,12 @@ void nano::active_transactions::stop () void nano::active_transactions::block_cemented_callback (std::shared_ptr const & block_a) { - auto transaction = node.store.tx_begin_read (); - auto status_type = election_status (transaction, block_a); + auto status_type = election_status (block_a); if (!status_type) return; + auto transaction = node.store.tx_begin_read (); switch (*status_type) { case nano::election_status_type::inactive_confirmation_height: @@ -102,13 +102,13 @@ void nano::active_transactions::block_cemented_callback (std::shared_ptr nano::active_transactions::election_status (nano::store::read_transaction const & transaction, std::shared_ptr const & block) +boost::optional nano::active_transactions::election_status (std::shared_ptr const & block) { boost::optional status_type; if (!confirmation_height_processor.is_processing_added_block (block->hash ())) { - status_type = confirm_block (transaction, block); + status_type = confirm_block (block); } else { @@ -654,7 +654,7 @@ bool nano::active_transactions::publish (std::shared_ptr const & bl } // Returns the type of election status requiring callbacks calling later -boost::optional nano::active_transactions::confirm_block (store::transaction const & transaction_a, std::shared_ptr const & block_a) +boost::optional nano::active_transactions::confirm_block (std::shared_ptr const & block_a) { auto const hash = block_a->hash (); std::shared_ptr election = nullptr; diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index e1c4a5f8e..3ce2772f7 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -160,7 +160,7 @@ public: bool empty () const; std::size_t size () const; bool publish (std::shared_ptr const &); - boost::optional confirm_block (store::transaction const &, std::shared_ptr const &); + boost::optional confirm_block (std::shared_ptr const &); void block_cemented_callback (std::shared_ptr const &); void block_already_cemented_callback (nano::block_hash const &); @@ -195,7 +195,7 @@ private: * TODO: Should be moved to `vote_cache` class */ void add_vote_cache (nano::block_hash const & hash, std::shared_ptr vote); - boost::optional election_status (nano::store::read_transaction const & transaction, std::shared_ptr const & block); + boost::optional election_status (std::shared_ptr const & block); void process_inactive_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr const & block); void process_active_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr const & block, nano::election_status_type status); void handle_final_votes_confirmation (std::shared_ptr const & block, nano::store::read_transaction const & transaction, nano::election_status_type status); From b2607dbabfbb9c9f55a919d9ade5dedebaf2f8aa Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Tue, 19 Mar 2024 12:48:49 +0000 Subject: [PATCH 058/128] Clean active_transactions::activate_successors signature. --- nano/node/active_transactions.cpp | 8 ++++---- nano/node/active_transactions.hpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 1b9c4af78..d9d889184 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -179,16 +179,16 @@ void nano::active_transactions::handle_final_votes_confirmation (std::shared_ptr // Next-block activations are only done for blocks with previously active elections if (cemented_bootstrap_count_reached && was_active) { - activate_successors (account, block, transaction); + activate_successors (transaction, block); } } -void nano::active_transactions::activate_successors (const nano::account & account, std::shared_ptr const & block, nano::store::read_transaction const & transaction) +void nano::active_transactions::activate_successors (nano::store::read_transaction const & transaction, std::shared_ptr const & block) { - node.scheduler.priority.activate (account, transaction); + node.scheduler.priority.activate (block->account (), transaction); // Start or vote for the next unconfirmed block in the destination account - if (block->is_send () && !block->destination ().is_zero () && block->destination () != account) + if (block->is_send () && !block->destination ().is_zero () && block->destination () != block->account ()) { node.scheduler.priority.activate (block->destination (), transaction); } diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index 3ce2772f7..9f56d5cad 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -200,7 +200,7 @@ private: void process_active_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr const & block, nano::election_status_type status); void handle_final_votes_confirmation (std::shared_ptr const & block, nano::store::read_transaction const & transaction, nano::election_status_type status); void handle_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr const & block, std::shared_ptr election, nano::election_status_type status); - void activate_successors (const nano::account & account, std::shared_ptr const & block, nano::store::read_transaction const & transaction); + void activate_successors (nano::store::read_transaction const & transaction, std::shared_ptr const & block); void notify_observers (nano::store::read_transaction const & transaction, nano::election_status const & status, std::vector const & votes); private: // Dependencies From 5e7e7fc02ec87231ea477c1c9b565876fac2357f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20W=C3=B3jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Wed, 20 Mar 2024 10:39:17 +0100 Subject: [PATCH 059/128] Rework handshake visitor (#4504) * Rework tcp_server handshake visitor * Simplify message visitor * Fixes --- nano/core_test/network.cpp | 2 +- nano/lib/stats_enums.hpp | 9 ++ nano/node/transport/tcp_server.cpp | 249 ++++++++++++++++++----------- nano/node/transport/tcp_server.hpp | 38 +++-- 4 files changed, 194 insertions(+), 104 deletions(-) diff --git a/nano/core_test/network.cpp b/nano/core_test/network.cpp index c7152c664..fe8bcfb6f 100644 --- a/nano/core_test/network.cpp +++ b/nano/core_test/network.cpp @@ -722,7 +722,7 @@ TEST (tcp_listener, tcp_listener_timeout_node_id_handshake) ASSERT_FALSE (ec); }); }); - ASSERT_TIMELY (5s, node0->stats.count (nano::stat::type::message, nano::stat::detail::node_id_handshake) != 0); + ASSERT_TIMELY (5s, node0->stats.count (nano::stat::type::tcp_server, nano::stat::detail::node_id_handshake) != 0); { nano::lock_guard guard (node0->tcp_listener->mutex); ASSERT_EQ (node0->tcp_listener->connections.size (), 1); diff --git a/nano/lib/stats_enums.hpp b/nano/lib/stats_enums.hpp index de7b6d020..b1baaa963 100644 --- a/nano/lib/stats_enums.hpp +++ b/nano/lib/stats_enums.hpp @@ -235,6 +235,15 @@ enum class detail : uint8_t tcp_read_error, tcp_write_error, + // tcp_server + handshake, + handshake_abort, + handshake_error, + handshake_network_error, + handshake_initiate, + handshake_response, + handshake_response_invalid, + // ipc invocations, diff --git a/nano/node/transport/tcp_server.cpp b/nano/node/transport/tcp_server.cpp index bea5f4f9b..a10d85dd5 100644 --- a/nano/node/transport/tcp_server.cpp +++ b/nano/node/transport/tcp_server.cpp @@ -301,7 +301,7 @@ nano::transport::tcp_server::~tcp_server () return; } - node->logger.debug (nano::log::type::tcp_server, "Exiting TCP server ({})", nano::util::to_str (remote_endpoint)); + node->logger.debug (nano::log::type::tcp_server, "Exiting TCP server ({})", fmt::streamed (remote_endpoint)); if (socket->type () == nano::transport::socket::type_t::bootstrap) { @@ -341,7 +341,7 @@ void nano::transport::tcp_server::start () return; } - node->logger.debug (nano::log::type::tcp_server, "Starting TCP server ({})", nano::util::to_str (remote_endpoint)); + node->logger.debug (nano::log::type::tcp_server, "Starting TCP server ({})", fmt::streamed (remote_endpoint)); receive_message (); } @@ -374,7 +374,7 @@ void nano::transport::tcp_server::receive_message () node->logger.debug (nano::log::type::tcp_server, "Error reading message: {}, status: {} ({})", ec.message (), to_string (this_l->message_deserializer->status), - nano::util::to_str (this_l->remote_endpoint)); + fmt::streamed (this_l->remote_endpoint)); this_l->stop (); } @@ -392,10 +392,11 @@ void nano::transport::tcp_server::received_message (std::unique_ptrstatus != transport::parse_status::success); node->stats.inc (nano::stat::type::error, to_stat_detail (message_deserializer->status)); + + // Avoid too much noise about `duplicate_publish_message` errors if (message_deserializer->status == transport::parse_status::duplicate_publish_message) { node->stats.inc (nano::stat::type::filter, nano::stat::detail::duplicate_publish_message); } else { - // Avoid too much noise about `duplicate_publish_message` errors node->logger.debug (nano::log::type::tcp_server, "Error deserializing message: {} ({})", to_string (message_deserializer->status), - nano::util::to_str (remote_endpoint)); + fmt::streamed (remote_endpoint)); } } - if (should_continue) + switch (result) { - receive_message (); + case process_result::progress: + { + receive_message (); + } + break; + case process_result::abort: + { + stop (); + } + break; + case process_result::pause: + { + // Do nothing + } + break; } } -bool nano::transport::tcp_server::process_message (std::unique_ptr message) +auto nano::transport::tcp_server::process_message (std::unique_ptr message) -> process_result { auto node = this->node.lock (); if (!node) { - return false; + return process_result::abort; } - node->stats.inc (nano::stat::type::tcp_server, to_stat_detail (message->header.type), nano::stat::dir::in); + + node->stats.inc (nano::stat::type::tcp_server, to_stat_detail (message->type ()), nano::stat::dir::in); debug_assert (is_undefined_connection () || is_realtime_connection () || is_bootstrap_connection ()); @@ -446,50 +463,68 @@ bool nano::transport::tcp_server::process_message (std::unique_ptrvisit (handshake_visitor); - if (handshake_visitor.process) - { - queue_realtime (std::move (message)); - return true; - } - else if (handshake_visitor.bootstrap) - { - if (!to_bootstrap_connection ()) - { - stop (); - return false; - } - } - else - { - // Neither handshake nor bootstrap received when in handshake mode - node->logger.debug (nano::log::type::tcp_server, "Neither handshake nor bootstrap received when in handshake mode: {} ({})", - nano::to_string (message->header.type), - nano::util::to_str (remote_endpoint)); - return true; + switch (handshake_visitor.result) + { + case handshake_status::abort: + { + node->stats.inc (nano::stat::type::tcp_server, nano::stat::detail::handshake_abort); + node->logger.debug (nano::log::type::tcp_server, "Aborting handshake: {} ({})", to_string (message->type ()), fmt::streamed (remote_endpoint)); + + return process_result::abort; + } + case handshake_status::handshake: + { + return process_result::progress; // Continue handshake + } + case handshake_status::realtime: + { + queue_realtime (std::move (message)); + return process_result::progress; // Continue receiving new messages + } + case handshake_status::bootstrap: + { + bool success = to_bootstrap_connection (); + if (!success) + { + node->stats.inc (nano::stat::type::tcp_server, nano::stat::detail::handshake_error); + node->logger.debug (nano::log::type::tcp_server, "Error switching to bootstrap mode: {} ({})", to_string (message->type ()), fmt::streamed (remote_endpoint)); + + return process_result::abort; // Switch failed, abort + } + else + { + // Fall through to process the bootstrap message + } + } } } else if (is_realtime_connection ()) { realtime_message_visitor realtime_visitor{ *this }; message->visit (realtime_visitor); + if (realtime_visitor.process) { queue_realtime (std::move (message)); } - return true; + + return process_result::progress; } - // the server will switch to bootstrap mode immediately after processing the first bootstrap message, thus no `else if` + // The server will switch to bootstrap mode immediately after processing the first bootstrap message, thus no `else if` if (is_bootstrap_connection ()) { bootstrap_message_visitor bootstrap_visitor{ shared_from_this () }; message->visit (bootstrap_visitor); - return !bootstrap_visitor.processed; // Stop receiving new messages if bootstrap serving started + + // Pause receiving new messages if bootstrap serving started + return bootstrap_visitor.processed ? process_result::pause : process_result::progress; } + debug_assert (false); - return true; // Continue receiving new messages + return process_result::abort; } void nano::transport::tcp_server::queue_realtime (std::unique_ptr message) @@ -502,63 +537,74 @@ void nano::transport::tcp_server::queue_realtime (std::unique_ptr node->network.tcp_channels.message_manager.put_message (nano::tcp_message_item{ std::move (message), remote_endpoint, remote_node_id, socket }); } -/* - * Handshake - */ - -nano::transport::tcp_server::handshake_message_visitor::handshake_message_visitor (std::shared_ptr server) : - server{ std::move (server) } +auto nano::transport::tcp_server::process_handshake (nano::node_id_handshake const & message) -> handshake_status { -} - -void nano::transport::tcp_server::handshake_message_visitor::node_id_handshake (nano::node_id_handshake const & message) -{ - auto node = server->node.lock (); + auto node = this->node.lock (); if (!node) { - return; + return handshake_status::abort; } + if (node->flags.disable_tcp_realtime) { - node->logger.debug (nano::log::type::tcp_server, "Handshake attempted with disabled realtime TCP ({})", nano::util::to_str (server->remote_endpoint)); + node->stats.inc (nano::stat::type::tcp_server, nano::stat::detail::handshake_error); + node->logger.debug (nano::log::type::tcp_server, "Handshake attempted with disabled realtime TCP ({})", fmt::streamed (remote_endpoint)); - // Stop invalid handshake - server->stop (); - return; + return handshake_status::abort; } - - if (message.query && server->handshake_query_received) + if (!message.query && !message.response) { - node->logger.debug (nano::log::type::tcp_server, "Detected multiple handshake queries ({})", nano::util::to_str (server->remote_endpoint)); + node->stats.inc (nano::stat::type::tcp_server, nano::stat::detail::handshake_error); + node->logger.debug (nano::log::type::tcp_server, "Invalid handshake message received ({})", fmt::streamed (remote_endpoint)); - // Stop invalid handshake - server->stop (); - return; + return handshake_status::abort; + } + if (message.query && handshake_received) // Second handshake message should be a response only + { + node->stats.inc (nano::stat::type::tcp_server, nano::stat::detail::handshake_error); + node->logger.debug (nano::log::type::tcp_server, "Detected multiple handshake queries ({})", fmt::streamed (remote_endpoint)); + + return handshake_status::abort; } - server->handshake_query_received = true; + handshake_received = true; - node->logger.debug (nano::log::type::tcp_server, "Handshake query received ({})", nano::util::to_str (server->remote_endpoint)); + node->stats.inc (nano::stat::type::tcp_server, nano::stat::detail::node_id_handshake, nano::stat::dir::in); + node->logger.debug (nano::log::type::tcp_server, "Handshake message received ({})", fmt::streamed (remote_endpoint)); if (message.query) { - server->send_handshake_response (*message.query, message.is_v2 ()); + // Sends response + our own query + send_handshake_response (*message.query, message.is_v2 ()); + // Fall through and continue handshake } if (message.response) { - if (node->network.verify_handshake_response (*message.response, nano::transport::map_tcp_to_endpoint (server->remote_endpoint))) + if (node->network.verify_handshake_response (*message.response, nano::transport::map_tcp_to_endpoint (remote_endpoint))) { - server->to_realtime_connection (message.response->node_id); + bool success = to_realtime_connection (message.response->node_id); + if (success) + { + return handshake_status::realtime; // Switched to realtime + } + else + { + node->stats.inc (nano::stat::type::tcp_server, nano::stat::detail::handshake_error); + node->logger.debug (nano::log::type::tcp_server, "Error switching to realtime mode ({})", fmt::streamed (remote_endpoint)); + + return handshake_status::abort; + } } else { - // Stop invalid handshake - server->stop (); - return; + node->stats.inc (nano::stat::type::tcp_server, nano::stat::detail::handshake_response_invalid); + node->logger.debug (nano::log::type::tcp_server, "Invalid handshake response received ({})", fmt::streamed (remote_endpoint)); + + return handshake_status::abort; } } - process = true; + return handshake_status::handshake; // Handshake is in progress } void nano::transport::tcp_server::send_handshake_response (nano::node_id_handshake::query_payload const & query, bool v2) @@ -568,11 +614,13 @@ void nano::transport::tcp_server::send_handshake_response (nano::node_id_handsha { return; } + auto response = node->network.prepare_handshake_response (query, v2); auto own_query = node->network.prepare_handshake_query (nano::transport::map_tcp_to_endpoint (remote_endpoint)); nano::node_id_handshake handshake_response{ node->network_params.network, own_query, response }; - // TODO: Use channel + node->logger.debug (nano::log::type::tcp_server, "Responding to handshake ({})", fmt::streamed (remote_endpoint)); + auto shared_const_buffer = handshake_response.to_shared_const_buffer (); socket->async_write (shared_const_buffer, [this_l = shared_from_this ()] (boost::system::error_code const & ec, std::size_t size_a) { auto node = this_l->node.lock (); @@ -582,47 +630,53 @@ void nano::transport::tcp_server::send_handshake_response (nano::node_id_handsha } if (ec) { - node->logger.debug (nano::log::type::tcp_server, "Error sending handshake response: {} ({})", ec.message (), nano::util::to_str (this_l->remote_endpoint)); + node->stats.inc (nano::stat::type::tcp_server, nano::stat::detail::handshake_network_error); + node->logger.debug (nano::log::type::tcp_server, "Error sending handshake response: {} ({})", ec.message (), fmt::streamed (this_l->remote_endpoint)); // Stop invalid handshake this_l->stop (); } else { - node->stats.inc (nano::stat::type::message, nano::stat::detail::node_id_handshake, nano::stat::dir::out); + node->stats.inc (nano::stat::type::tcp_server, nano::stat::detail::handshake, nano::stat::dir::out); + node->stats.inc (nano::stat::type::tcp_server, nano::stat::detail::handshake_response, nano::stat::dir::out); } }); } +/* + * handshake_message_visitor + */ + +void nano::transport::tcp_server::handshake_message_visitor::node_id_handshake (const nano::node_id_handshake & message) +{ + result = server.process_handshake (message); +} + void nano::transport::tcp_server::handshake_message_visitor::bulk_pull (const nano::bulk_pull & message) { - bootstrap = true; + result = handshake_status::bootstrap; } void nano::transport::tcp_server::handshake_message_visitor::bulk_pull_account (const nano::bulk_pull_account & message) { - bootstrap = true; + result = handshake_status::bootstrap; } void nano::transport::tcp_server::handshake_message_visitor::bulk_push (const nano::bulk_push & message) { - bootstrap = true; + result = handshake_status::bootstrap; } void nano::transport::tcp_server::handshake_message_visitor::frontier_req (const nano::frontier_req & message) { - bootstrap = true; + result = handshake_status::bootstrap; } /* - * Realtime + * realtime_message_visitor */ -nano::transport::tcp_server::realtime_message_visitor::realtime_message_visitor (nano::transport::tcp_server & server_a) : - server{ server_a } -{ -} - void nano::transport::tcp_server::realtime_message_visitor::keepalive (const nano::keepalive & message) { process = true; @@ -684,7 +738,7 @@ void nano::transport::tcp_server::realtime_message_visitor::asc_pull_ack (const } /* - * Bootstrap + * bootstrap_message_visitor */ nano::transport::tcp_server::bootstrap_message_visitor::bootstrap_message_visitor (std::shared_ptr server) : @@ -769,6 +823,10 @@ void nano::transport::tcp_server::bootstrap_message_visitor::frontier_req (const processed = true; } +/* + * + */ + // TODO: We could periodically call this (from a dedicated timeout thread for eg.) but socket already handles timeouts, // and since we only ever store tcp_server as weak_ptr, socket timeout will automatically trigger tcp_server cleanup void nano::transport::tcp_server::timeout () @@ -780,7 +838,7 @@ void nano::transport::tcp_server::timeout () } if (socket->has_timed_out ()) { - node->logger.debug (nano::log::type::tcp_server, "Closing TCP server due to timeout ({})", nano::util::to_str (remote_endpoint)); + node->logger.debug (nano::log::type::tcp_server, "Closing TCP server due to timeout ({})", fmt::streamed (remote_endpoint)); { nano::lock_guard lock{ node->tcp_listener->mutex }; @@ -834,7 +892,7 @@ bool nano::transport::tcp_server::to_bootstrap_connection () ++node->tcp_listener->bootstrap_count; socket->type_set (nano::transport::socket::type_t::bootstrap); - node->logger.debug (nano::log::type::tcp_server, "Switched to bootstrap mode ({})", nano::util::to_str (remote_endpoint)); + node->logger.debug (nano::log::type::tcp_server, "Switched to bootstrap mode ({})", fmt::streamed (remote_endpoint)); return true; } @@ -846,17 +904,22 @@ bool nano::transport::tcp_server::to_realtime_connection (nano::account const & { return false; } - if (socket->type () == nano::transport::socket::type_t::undefined && !node->flags.disable_tcp_realtime) + if (node->flags.disable_tcp_realtime) { - remote_node_id = node_id; - ++node->tcp_listener->realtime_count; - socket->type_set (nano::transport::socket::type_t::realtime); - - node->logger.debug (nano::log::type::tcp_server, "Switched to realtime mode ({})", nano::util::to_str (remote_endpoint)); - - return true; + return false; } - return false; + if (socket->type () != nano::transport::socket::type_t::undefined) + { + return false; + } + + remote_node_id = node_id; + ++node->tcp_listener->realtime_count; + socket->type_set (nano::transport::socket::type_t::realtime); + + node->logger.debug (nano::log::type::tcp_server, "Switched to realtime mode ({})", fmt::streamed (remote_endpoint)); + + return true; } bool nano::transport::tcp_server::is_undefined_connection () const diff --git a/nano/node/transport/tcp_server.hpp b/nano/node/transport/tcp_server.hpp index 076024116..e8a220bc1 100644 --- a/nano/node/transport/tcp_server.hpp +++ b/nano/node/transport/tcp_server.hpp @@ -69,19 +69,23 @@ public: std::weak_ptr const node; nano::mutex mutex; std::atomic stopped{ false }; - std::atomic handshake_query_received{ false }; + std::atomic handshake_received{ false }; // Remote enpoint used to remove response channel even after socket closing nano::tcp_endpoint remote_endpoint{ boost::asio::ip::address_v6::any (), 0 }; nano::account remote_node_id{}; std::chrono::steady_clock::time_point last_telemetry_req{}; private: - void send_handshake_response (nano::node_id_handshake::query_payload const & query, bool v2); + enum class process_result + { + abort, + progress, + pause, + }; void receive_message (); void received_message (std::unique_ptr message); - bool process_message (std::unique_ptr message); - + process_result process_message (std::unique_ptr message); void queue_realtime (std::unique_ptr message); bool to_bootstrap_connection (); @@ -90,19 +94,30 @@ private: bool is_bootstrap_connection () const; bool is_realtime_connection () const; + enum class handshake_status + { + abort, + handshake, + realtime, + bootstrap, + }; + + handshake_status process_handshake (nano::node_id_handshake const & message); + void send_handshake_response (nano::node_id_handshake::query_payload const & query, bool v2); + private: bool const allow_bootstrap; std::shared_ptr message_deserializer; std::optional last_keepalive; -private: +private: // Visitors class handshake_message_visitor : public nano::message_visitor { public: - bool process{ false }; - bool bootstrap{ false }; + handshake_status result{ handshake_status::abort }; - explicit handshake_message_visitor (std::shared_ptr); + explicit handshake_message_visitor (tcp_server & server) : + server{ server } {}; void node_id_handshake (nano::node_id_handshake const &) override; void bulk_pull (nano::bulk_pull const &) override; @@ -111,7 +126,7 @@ private: void frontier_req (nano::frontier_req const &) override; private: - std::shared_ptr server; + tcp_server & server; }; class realtime_message_visitor : public nano::message_visitor @@ -119,7 +134,8 @@ private: public: bool process{ false }; - explicit realtime_message_visitor (tcp_server &); + explicit realtime_message_visitor (tcp_server & server) : + server{ server } {}; void keepalive (nano::keepalive const &) override; void publish (nano::publish const &) override; @@ -150,5 +166,7 @@ private: private: std::shared_ptr server; }; + + friend class handshake_message_visitor; }; } From 152f4449fa745106ae68acd015d7d1cc0b72893c Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Wed, 20 Mar 2024 10:09:59 +0000 Subject: [PATCH 060/128] Simplify active_transactions::block_cemented_callback Simplifies and merges logic that was spread across multiple functions and coupled with nano::election. Behaviour changed with respect to callbacks that were *not* called for confirmed blocks in certain circumstances. This was a purely synthetic case where an election was explicitly confirmed in code but didn't have an associated election. In other cases where there was no election yet the block was confirmed indirectly, the callbacks were still called. This behaviour change calls the callback for all blocks that are confirmed. Behaviour changed with respect to entries in the recently_cemented list. Previously blocks implicitly confirmed were not placed in this list yet were reported through callbacks. This behaviour was arbitrary and could be confusing. Now all blocks that are reported through callbacks are placed in the recently_cemented list. --- nano/core_test/confirmation_height.cpp | 8 +- nano/node/active_transactions.cpp | 140 +++++++------------------ nano/node/active_transactions.hpp | 8 +- nano/secure/common.hpp | 16 +-- 4 files changed, 52 insertions(+), 120 deletions(-) diff --git a/nano/core_test/confirmation_height.cpp b/nano/core_test/confirmation_height.cpp index a150d40e4..352d04d4b 100644 --- a/nano/core_test/confirmation_height.cpp +++ b/nano/core_test/confirmation_height.cpp @@ -1444,8 +1444,9 @@ TEST (confirmation_height, pending_observer_callbacks) node->confirmation_height_processor.add (send1); - // Confirm the callback is not called under this circumstance because there is no election information - ASSERT_TIMELY (10s, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out) == 1 && node->ledger.stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out) == 1); + // Callback is performed for all blocks that are confirmed + ASSERT_TIMELY_EQ (5s, 2, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out)) + ASSERT_TIMELY_EQ (5s, 2, node->ledger.stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); ASSERT_EQ (2, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); ASSERT_EQ (2, node->stats.count (nano::stat::type::confirmation_height, get_stats_detail (mode_a), nano::stat::dir::in)); @@ -1528,7 +1529,8 @@ TEST (confirmation_height, callback_confirmed_history) ASSERT_TIMELY_EQ (10s, node->active.size (), 0); ASSERT_TIMELY_EQ (10s, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_quorum, nano::stat::dir::out), 1); - ASSERT_EQ (1, node->active.recently_cemented.list ().size ()); + // Each block that's confirmed is in the recently_cemented history + ASSERT_EQ (2, node->active.recently_cemented.list ().size ()); ASSERT_TRUE (node->active.empty ()); // Confirm the callback is not called under this circumstance diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index d9d889184..350b4c28b 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -80,75 +80,44 @@ void nano::active_transactions::stop () clear (); } -void nano::active_transactions::block_cemented_callback (std::shared_ptr const & block_a) +void nano::active_transactions::block_cemented_callback (std::shared_ptr const & block) { - auto status_type = election_status (block_a); - - if (!status_type) - return; - - auto transaction = node.store.tx_begin_read (); - switch (*status_type) + if (auto election_l = election (block->qualified_root ())) { - case nano::election_status_type::inactive_confirmation_height: - process_inactive_confirmation (transaction, block_a); - break; - - default: - process_active_confirmation (transaction, block_a, *status_type); - break; + election_l->try_confirm (block->hash ()); } - - handle_final_votes_confirmation (block_a, transaction, *status_type); -} - -boost::optional nano::active_transactions::election_status (std::shared_ptr const & block) -{ - boost::optional status_type; - - if (!confirmation_height_processor.is_processing_added_block (block->hash ())) + auto election = remove_election_winner_details (block->hash ()); + nano::election_status status; + std::vector votes; + status.winner = block; + if (election) { - status_type = confirm_block (block); + status = election->get_status (); + votes = election->votes_with_weight (); + } + if (confirmation_height_processor.is_processing_added_block (block->hash ())) + { + status.type = nano::election_status_type::active_confirmed_quorum; + } + else if (election) + { + status.type = nano::election_status_type::active_confirmation_height; } else { - status_type = nano::election_status_type::active_confirmed_quorum; + status.type = nano::election_status_type::inactive_confirmation_height; } - - return status_type; -} - -void nano::active_transactions::process_inactive_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr const & block) -{ - nano::election_status status{ block, 0, 0, std::chrono::duration_cast (std::chrono::system_clock::now ().time_since_epoch ()), std::chrono::duration_values::zero (), 0, 1, 0, nano::election_status_type::inactive_confirmation_height }; - notify_observers (transaction, status, {}); -} - -void nano::active_transactions::process_active_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr const & block, nano::election_status_type status_type) -{ - auto hash (block->hash ()); - nano::unique_lock election_winners_lk{ election_winner_details_mutex }; - auto existing = election_winner_details.find (hash); - if (existing != election_winner_details.end ()) - { - auto election = existing->second; - election_winner_details.erase (hash); - election_winners_lk.unlock (); - if (election->confirmed () && election->winner ()->hash () == hash) - { - handle_confirmation (transaction, block, election, status_type); - } - } -} - -void nano::active_transactions::handle_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr const & block, std::shared_ptr election, nano::election_status_type status_type) -{ - nano::block_hash hash = block->hash (); - recently_cemented.put (election->get_status ()); - - auto status = election->set_status_type (status_type); - auto votes = election->votes_with_weight (); + recently_cemented.put (status); + auto transaction = node.store.tx_begin_read (); notify_observers (transaction, status, votes); + bool cemented_bootstrap_count_reached = node.ledger.cache.cemented_count >= node.ledger.bootstrap_weight_max_blocks; + bool was_active = status.type == nano::election_status_type::active_confirmed_quorum || status.type == nano::election_status_type::active_confirmation_height; + + // Next-block activations are only done for blocks with previously active elections + if (cemented_bootstrap_count_reached && was_active) + { + activate_successors (transaction, block); + } } void nano::active_transactions::notify_observers (nano::store::read_transaction const & transaction, nano::election_status const & status, std::vector const & votes) @@ -170,19 +139,6 @@ void nano::active_transactions::notify_observers (nano::store::read_transaction } } -void nano::active_transactions::handle_final_votes_confirmation (std::shared_ptr const & block, nano::store::read_transaction const & transaction, nano::election_status_type status) -{ - auto account = block->account (); - bool cemented_bootstrap_count_reached = node.ledger.cache.cemented_count >= node.ledger.bootstrap_weight_max_blocks; - bool was_active = status == nano::election_status_type::active_confirmed_quorum || status == nano::election_status_type::active_confirmation_height; - - // Next-block activations are only done for blocks with previously active elections - if (cemented_bootstrap_count_reached && was_active) - { - activate_successors (transaction, block); - } -} - void nano::active_transactions::activate_successors (nano::store::read_transaction const & transaction, std::shared_ptr const & block) { node.scheduler.priority.activate (block->account (), transaction); @@ -200,10 +156,17 @@ void nano::active_transactions::add_election_winner_details (nano::block_hash co election_winner_details.emplace (hash_a, election_a); } -void nano::active_transactions::remove_election_winner_details (nano::block_hash const & hash_a) +std::shared_ptr nano::active_transactions::remove_election_winner_details (nano::block_hash const & hash_a) { nano::lock_guard guard{ election_winner_details_mutex }; - election_winner_details.erase (hash_a); + std::shared_ptr result; + auto existing = election_winner_details.find (hash_a); + if (existing != election_winner_details.end ()) + { + result = existing->second; + election_winner_details.erase (existing); + } + return result; } void nano::active_transactions::block_already_cemented_callback (nano::block_hash const & hash_a) @@ -653,33 +616,6 @@ bool nano::active_transactions::publish (std::shared_ptr const & bl return result; } -// Returns the type of election status requiring callbacks calling later -boost::optional nano::active_transactions::confirm_block (std::shared_ptr const & block_a) -{ - auto const hash = block_a->hash (); - std::shared_ptr election = nullptr; - { - nano::lock_guard guard{ mutex }; - auto existing = blocks.find (hash); - if (existing != blocks.end ()) - { - election = existing->second; - } - } - - boost::optional status_type; - if (election) - { - status_type = election->try_confirm (hash); - } - else - { - status_type = nano::election_status_type::inactive_confirmation_height; - } - - return status_type; -} - void nano::active_transactions::add_vote_cache (nano::block_hash const & hash, std::shared_ptr const vote) { if (node.ledger.weight (vote->account) > node.minimum_principal_weight ()) diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index 9f56d5cad..7323bf51d 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -160,7 +160,6 @@ public: bool empty () const; std::size_t size () const; bool publish (std::shared_ptr const &); - boost::optional confirm_block (std::shared_ptr const &); void block_cemented_callback (std::shared_ptr const &); void block_already_cemented_callback (nano::block_hash const &); @@ -177,7 +176,7 @@ public: std::size_t election_winner_details_size (); void add_election_winner_details (nano::block_hash const &, std::shared_ptr const &); - void remove_election_winner_details (nano::block_hash const &); + std::shared_ptr remove_election_winner_details (nano::block_hash const &); private: // Erase elections if we're over capacity @@ -195,11 +194,6 @@ private: * TODO: Should be moved to `vote_cache` class */ void add_vote_cache (nano::block_hash const & hash, std::shared_ptr vote); - boost::optional election_status (std::shared_ptr const & block); - void process_inactive_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr const & block); - void process_active_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr const & block, nano::election_status_type status); - void handle_final_votes_confirmation (std::shared_ptr const & block, nano::store::read_transaction const & transaction, nano::election_status_type status); - void handle_confirmation (nano::store::read_transaction const & transaction, std::shared_ptr const & block, std::shared_ptr election, nano::election_status_type status); void activate_successors (nano::store::read_transaction const & transaction, std::shared_ptr const & block); void notify_observers (nano::store::read_transaction const & transaction, nano::election_status const & status, std::vector const & votes); diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index 0928289fd..d585e1b22 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -360,14 +360,14 @@ class election_status final { public: std::shared_ptr winner; - nano::amount tally; - nano::amount final_tally; - std::chrono::milliseconds election_end; - std::chrono::milliseconds election_duration; - unsigned confirmation_request_count; - unsigned block_count; - unsigned voter_count; - election_status_type type; + nano::amount tally{ 0 }; + nano::amount final_tally{ 0 }; + std::chrono::milliseconds election_end{ std::chrono::duration_cast (std::chrono::system_clock::now ().time_since_epoch ()) }; + std::chrono::milliseconds election_duration{ std::chrono::duration_values::zero () }; + unsigned confirmation_request_count{ 0 }; + unsigned block_count{ 0 }; + unsigned voter_count{ 0 }; + election_status_type type{ nano::election_status_type::inactive_confirmation_height }; }; nano::wallet_id random_wallet_id (); From e4b491754ac098ddfc9295c982a41b7a163d8268 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Wed, 20 Mar 2024 10:36:59 +0000 Subject: [PATCH 061/128] Remove unused code related to trying to externally set election status --- nano/node/election.cpp | 35 ++++++----------------------------- nano/node/election.hpp | 7 +++---- 2 files changed, 9 insertions(+), 33 deletions(-) diff --git a/nano/node/election.cpp b/nano/node/election.cpp index d179dabdb..a41136e4c 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -34,7 +34,7 @@ nano::election::election (nano::node & node_a, std::shared_ptr cons last_blocks.emplace (block_a->hash (), block_a); } -void nano::election::confirm_once (nano::unique_lock & lock_a, nano::election_status_type type_a) +void nano::election::confirm_once (nano::unique_lock & lock_a) { debug_assert (lock_a.owns_lock ()); @@ -51,7 +51,6 @@ void nano::election::confirm_once (nano::unique_lock & lock_a, nano status.confirmation_request_count = confirmation_request_count; status.block_count = nano::narrow_cast (last_blocks.size ()); status.voter_count = nano::narrow_cast (last_votes.size ()); - status.type = type_a; auto const status_l = status; node.active.recently_confirmed.put (qualified_root, status_l.winner->hash ()); @@ -403,44 +402,22 @@ void nano::election::confirm_if_quorum (nano::unique_lock & lock_a) } if (final_weight >= node.online_reps.delta ()) { - confirm_once (lock_a, nano::election_status_type::active_confirmed_quorum); + confirm_once (lock_a); } } } -boost::optional nano::election::try_confirm (nano::block_hash const & hash) +void nano::election::try_confirm (nano::block_hash const & hash) { - boost::optional status_type; nano::unique_lock election_lock{ mutex }; auto winner = status.winner; if (winner && winner->hash () == hash) { - // Determine if the block was confirmed explicitly via election confirmation or implicitly via confirmation height if (!confirmed_locked ()) { - confirm_once (election_lock, nano::election_status_type::active_confirmation_height); - status_type = nano::election_status_type::active_confirmation_height; - } - else - { - status_type = nano::election_status_type::active_confirmed_quorum; + confirm_once (election_lock); } } - else - { - status_type = boost::optional{}; - } - return status_type; -} - -nano::election_status nano::election::set_status_type (nano::election_status_type status_type) -{ - nano::unique_lock election_lk{ mutex }; - status.type = status_type; - status.confirmation_request_count = confirmation_request_count; - nano::election_status status_l{ status }; - election_lk.unlock (); - return status_l; } std::shared_ptr nano::election::find (nano::block_hash const & hash_a) const @@ -709,11 +686,11 @@ bool nano::election::replace_by_weight (nano::unique_lock & lock_a, return replaced; } -void nano::election::force_confirm (nano::election_status_type type_a) +void nano::election::force_confirm () { release_assert (node.network_params.network.is_dev_network ()); nano::unique_lock lock{ mutex }; - confirm_once (lock, type_a); + confirm_once (lock); } std::unordered_map> nano::election::blocks () const diff --git a/nano/node/election.hpp b/nano/node/election.hpp index 63e758278..59cf2ea9e 100644 --- a/nano/node/election.hpp +++ b/nano/node/election.hpp @@ -146,8 +146,7 @@ public: // Interface bool publish (std::shared_ptr const & block_a); // Confirm this block if quorum is met void confirm_if_quorum (nano::unique_lock &); - boost::optional try_confirm (nano::block_hash const & hash); - nano::election_status set_status_type (nano::election_status_type status_type); + void try_confirm (nano::block_hash const & hash); /** * Broadcasts vote for the current winner of this election @@ -173,7 +172,7 @@ private: bool confirmed_locked () const; nano::election_extended_status current_status_locked () const; // lock_a does not own the mutex on return - void confirm_once (nano::unique_lock & lock_a, nano::election_status_type = nano::election_status_type::active_confirmed_quorum); + void confirm_once (nano::unique_lock & lock_a); bool broadcast_block_predicate () const; void broadcast_block (nano::confirmation_solicitor &); void send_confirm_req (nano::confirmation_solicitor &); @@ -217,7 +216,7 @@ private: // Constants friend class confirmation_solicitor; public: // Only used in tests - void force_confirm (nano::election_status_type = nano::election_status_type::active_confirmed_quorum); + void force_confirm (); std::unordered_map votes () const; std::unordered_map> blocks () const; From f57bcdea9dd10b7863bd2f5e5e3f5057077994f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20W=C3=B3jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Wed, 20 Mar 2024 14:21:32 +0100 Subject: [PATCH 062/128] Store `io_context` as shared pointer (#4506) --- nano/core_test/network.cpp | 10 ++--- nano/core_test/node.cpp | 8 ++-- nano/core_test/socket.cpp | 20 ++++----- nano/lib/thread_runner.cpp | 12 +++-- nano/lib/thread_runner.hpp | 6 ++- nano/load_test/entry.cpp | 6 ++- nano/nano_node/daemon.cpp | 22 ++++++--- nano/nano_node/entry.cpp | 8 ++-- nano/nano_rpc/entry.cpp | 14 ++++-- nano/nano_wallet/entry.cpp | 3 +- nano/node/ipc/ipc_server.cpp | 9 ++-- nano/node/node.cpp | 9 ++-- nano/node/node.hpp | 7 +-- nano/rpc/rpc.cpp | 9 ++-- nano/rpc/rpc.hpp | 14 +++--- nano/rpc_test/rpc.cpp | 10 ++--- nano/rpc_test/rpc_context.cpp | 4 +- nano/slow_test/bootstrap.cpp | 2 +- nano/test_common/system.cpp | 85 ++++++++++++++++++----------------- nano/test_common/system.hpp | 2 +- 20 files changed, 148 insertions(+), 112 deletions(-) diff --git a/nano/core_test/network.cpp b/nano/core_test/network.cpp index fe8bcfb6f..82c014c31 100644 --- a/nano/core_test/network.cpp +++ b/nano/core_test/network.cpp @@ -21,14 +21,14 @@ using namespace std::chrono_literals; TEST (network, tcp_connection) { nano::test::system system; - boost::asio::ip::tcp::acceptor acceptor (system.io_ctx); + boost::asio::ip::tcp::acceptor acceptor (*system.io_ctx); auto port = system.get_available_port (); boost::asio::ip::tcp::endpoint endpoint (boost::asio::ip::address_v4::any (), port); acceptor.open (endpoint.protocol ()); acceptor.set_option (boost::asio::ip::tcp::acceptor::reuse_address (true)); acceptor.bind (endpoint); acceptor.listen (); - boost::asio::ip::tcp::socket incoming (system.io_ctx); + boost::asio::ip::tcp::socket incoming (*system.io_ctx); std::atomic done1 (false); std::string message1; acceptor.async_accept (incoming, [&done1, &message1] (boost::system::error_code const & ec_a) { @@ -39,7 +39,7 @@ TEST (network, tcp_connection) } done1 = true; }); - boost::asio::ip::tcp::socket connector (system.io_ctx); + boost::asio::ip::tcp::socket connector (*system.io_ctx); std::atomic done2 (false); std::string message2; connector.async_connect (boost::asio::ip::tcp::endpoint (boost::asio::ip::address_v4::loopback (), acceptor.local_endpoint ().port ()), @@ -538,13 +538,13 @@ TEST (network, ipv6_bind_send_ipv4) std::array bytes1{}; std::atomic finish1{ false }; nano::endpoint endpoint3; - boost::asio::ip::udp::socket socket1 (system.io_ctx, endpoint1); + boost::asio::ip::udp::socket socket1 (*system.io_ctx, endpoint1); socket1.async_receive_from (boost::asio::buffer (bytes1.data (), bytes1.size ()), endpoint3, [&finish1] (boost::system::error_code const & error, size_t size_a) { ASSERT_FALSE (error); ASSERT_EQ (16, size_a); finish1 = true; }); - boost::asio::ip::udp::socket socket2 (system.io_ctx, endpoint2); + boost::asio::ip::udp::socket socket2 (*system.io_ctx, endpoint2); nano::endpoint endpoint5 (boost::asio::ip::address_v4::loopback (), socket1.local_endpoint ().port ()); nano::endpoint endpoint6 (boost::asio::ip::address_v6::v4_mapped (boost::asio::ip::address_v4::loopback ()), socket2.local_endpoint ().port ()); socket2.async_send_to (boost::asio::buffer (std::array{}, 16), endpoint5, [] (boost::system::error_code const & error, size_t size_a) { diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index cf81a51bd..03c05211b 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -36,7 +36,7 @@ TEST (node, stop) nano::test::system system (1); ASSERT_NE (system.nodes[0]->wallets.items.end (), system.nodes[0]->wallets.items.begin ()); system.nodes[0]->stop (); - system.io_ctx.run (); + system.io_ctx->run (); ASSERT_TRUE (true); } @@ -68,10 +68,10 @@ TEST (node, work_generate) TEST (node, block_store_path_failure) { nano::test::system system; - auto service (std::make_shared ()); + auto io_ctx = std::make_shared (); auto path (nano::unique_path ()); nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; - auto node (std::make_shared (*service, system.get_available_port (), path, pool)); + auto node (std::make_shared (io_ctx, system.get_available_port (), path, pool)); ASSERT_TRUE (node->wallets.items.empty ()); node->stop (); } @@ -97,7 +97,7 @@ TEST (node_DeathTest, readonly_block_store_not_exist) TEST (node, password_fanout) { nano::test::system system; - boost::asio::io_context io_ctx; + auto io_ctx = std::make_shared (); auto path (nano::unique_path ()); nano::node_config config; config.peering_port = system.get_available_port (); diff --git a/nano/core_test/socket.cpp b/nano/core_test/socket.cpp index 1ec356391..11a0862b1 100644 --- a/nano/core_test/socket.cpp +++ b/nano/core_test/socket.cpp @@ -406,7 +406,7 @@ TEST (socket, drop_policy) nano::inactive_node inactivenode (nano::unique_path (), node_flags); auto node = inactivenode.node; - nano::thread_runner runner (node->io_ctx, 1); + nano::thread_runner runner (node->io_ctx_shared, 1); std::vector> connections; @@ -469,7 +469,7 @@ TEST (socket, concurrent_writes) // This gives more realistic execution than using system#poll, allowing writes to // queue up and drain concurrently. - nano::thread_runner runner (node->io_ctx, 1); + nano::thread_runner runner (node->io_ctx_shared, 1); constexpr size_t max_connections = 4; constexpr size_t client_count = max_connections; @@ -622,13 +622,13 @@ TEST (socket_timeout, read) // create a server socket boost::asio::ip::tcp::endpoint endpoint (boost::asio::ip::address_v6::loopback (), system.get_available_port ()); - boost::asio::ip::tcp::acceptor acceptor (system.io_ctx); + boost::asio::ip::tcp::acceptor acceptor (*system.io_ctx); acceptor.open (endpoint.protocol ()); acceptor.bind (endpoint); acceptor.listen (boost::asio::socket_base::max_listen_connections); // asynchronously accept an incoming connection and create a newsock and do not send any data - boost::asio::ip::tcp::socket newsock (system.io_ctx); + boost::asio::ip::tcp::socket newsock (*system.io_ctx); acceptor.async_accept (newsock, [] (boost::system::error_code const & ec_a) { EXPECT_FALSE (ec_a); }); @@ -668,13 +668,13 @@ TEST (socket_timeout, write) // create a server socket boost::asio::ip::tcp::endpoint endpoint (boost::asio::ip::address_v6::loopback (), system.get_available_port ()); - boost::asio::ip::tcp::acceptor acceptor (system.io_ctx); + boost::asio::ip::tcp::acceptor acceptor (*system.io_ctx); acceptor.open (endpoint.protocol ()); acceptor.bind (endpoint); acceptor.listen (boost::asio::socket_base::max_listen_connections); // asynchronously accept an incoming connection and create a newsock and do not receive any data - boost::asio::ip::tcp::socket newsock (system.io_ctx); + boost::asio::ip::tcp::socket newsock (*system.io_ctx); acceptor.async_accept (newsock, [] (boost::system::error_code const & ec_a) { EXPECT_FALSE (ec_a); }); @@ -719,13 +719,13 @@ TEST (socket_timeout, read_overlapped) // create a server socket boost::asio::ip::tcp::endpoint endpoint (boost::asio::ip::address_v6::loopback (), system.get_available_port ()); - boost::asio::ip::tcp::acceptor acceptor (system.io_ctx); + boost::asio::ip::tcp::acceptor acceptor (*system.io_ctx); acceptor.open (endpoint.protocol ()); acceptor.bind (endpoint); acceptor.listen (boost::asio::socket_base::max_listen_connections); // asynchronously accept an incoming connection and send one byte only - boost::asio::ip::tcp::socket newsock (system.io_ctx); + boost::asio::ip::tcp::socket newsock (*system.io_ctx); acceptor.async_accept (newsock, [&newsock] (boost::system::error_code const & ec_a) { EXPECT_FALSE (ec_a); @@ -777,13 +777,13 @@ TEST (socket_timeout, write_overlapped) // create a server socket boost::asio::ip::tcp::endpoint endpoint (boost::asio::ip::address_v6::loopback (), system.get_available_port ()); - boost::asio::ip::tcp::acceptor acceptor (system.io_ctx); + boost::asio::ip::tcp::acceptor acceptor (*system.io_ctx); acceptor.open (endpoint.protocol ()); acceptor.bind (endpoint); acceptor.listen (boost::asio::socket_base::max_listen_connections); // asynchronously accept an incoming connection and read 2 bytes only - boost::asio::ip::tcp::socket newsock (system.io_ctx); + boost::asio::ip::tcp::socket newsock (*system.io_ctx); auto buffer = std::make_shared> (1); acceptor.async_accept (newsock, [&newsock, &buffer] (boost::system::error_code const & ec_a) { EXPECT_FALSE (ec_a); diff --git a/nano/lib/thread_runner.cpp b/nano/lib/thread_runner.cpp index 26fa307b2..363a21795 100644 --- a/nano/lib/thread_runner.cpp +++ b/nano/lib/thread_runner.cpp @@ -10,17 +10,20 @@ * thread_runner */ -nano::thread_runner::thread_runner (boost::asio::io_context & io_ctx_a, unsigned num_threads, const nano::thread_role::name thread_role_a) : - io_guard{ boost::asio::make_work_guard (io_ctx_a) }, +nano::thread_runner::thread_runner (std::shared_ptr io_ctx_a, unsigned num_threads, const nano::thread_role::name thread_role_a) : + io_ctx{ io_ctx_a }, + io_guard{ boost::asio::make_work_guard (*io_ctx_a) }, role{ thread_role_a } { + debug_assert (io_ctx != nullptr); + for (auto i (0u); i < num_threads; ++i) { - threads.emplace_back (nano::thread_attributes::get_default (), [this, &io_ctx_a] () { + threads.emplace_back (nano::thread_attributes::get_default (), [this] () { nano::thread_role::set (role); try { - run (io_ctx_a); + run (*io_ctx); } catch (std::exception const & ex) { @@ -78,6 +81,7 @@ void nano::thread_runner::join () i.join (); } } + io_ctx.reset (); } void nano::thread_runner::stop_event_processing () diff --git a/nano/lib/thread_runner.hpp b/nano/lib/thread_runner.hpp index 7fbba2e08..975beab3f 100644 --- a/nano/lib/thread_runner.hpp +++ b/nano/lib/thread_runner.hpp @@ -12,18 +12,20 @@ namespace nano class thread_runner final { public: - thread_runner (boost::asio::io_context &, unsigned num_threads, nano::thread_role::name thread_role = nano::thread_role::name::io); + thread_runner (std::shared_ptr, unsigned num_threads, nano::thread_role::name thread_role = nano::thread_role::name::io); ~thread_runner (); /** Tells the IO context to stop processing events.*/ void stop_event_processing (); + /** Wait for IO threads to complete */ void join (); private: + std::shared_ptr io_ctx; + boost::asio::executor_work_guard io_guard; nano::thread_role::name const role; std::vector threads; - boost::asio::executor_work_guard io_guard; private: void run (boost::asio::io_context &); diff --git a/nano/load_test/entry.cpp b/nano/load_test/entry.cpp index 7a012fdeb..c47989fd6 100644 --- a/nano/load_test/entry.cpp +++ b/nano/load_test/entry.cpp @@ -592,7 +592,8 @@ int main (int argc, char * const * argv) std::this_thread::sleep_for (std::chrono::seconds (7)); std::cout << "Connecting nodes..." << std::endl; - boost::asio::io_context ioc; + std::shared_ptr ioc_shared = std::make_shared (); + boost::asio::io_context & ioc{ *ioc_shared }; debug_assert (!nano::signal_handler_impl); nano::signal_handler_impl = [&ioc] () { @@ -715,7 +716,8 @@ int main (int argc, char * const * argv) // Stop main node stop_rpc (ioc, primary_node_results); }); - nano::thread_runner runner (ioc, simultaneous_process_calls); + + nano::thread_runner runner (ioc_shared, simultaneous_process_calls); t.join (); runner.join (); diff --git a/nano/nano_node/daemon.cpp b/nano/nano_node/daemon.cpp index 2becd930a..879bc5e2d 100644 --- a/nano/nano_node/daemon.cpp +++ b/nano/nano_node/daemon.cpp @@ -98,7 +98,8 @@ void nano::daemon::run (std::filesystem::path const & data_path, nano::node_flag config.node.websocket_config.tls_config = tls_config; } - boost::asio::io_context io_ctx; + std::shared_ptr io_ctx = std::make_shared (); + auto opencl = nano::opencl_work::create (config.opencl_enable, config.opencl, logger, config.node.network_params.work); nano::opencl_work_func_t opencl_work_func; if (opencl) @@ -132,7 +133,7 @@ void nano::daemon::run (std::filesystem::path const & data_path, nano::node_flag config.node.peering_port = network_params.network.default_node_port; } - auto node (std::make_shared (io_ctx, data_path, config.node, opencl_work, flags)); + auto node = std::make_shared (io_ctx, data_path, config.node, opencl_work, flags); if (!node->init_error ()) { auto network_label = node->network_params.network.get_current_network_as_string (); @@ -165,10 +166,14 @@ void nano::daemon::run (std::filesystem::path const & data_path, nano::node_flag } rpc_config.tls_config = tls_config; - rpc_handler = std::make_unique (*node, ipc_server, config.rpc, [&ipc_server, &workers = node->workers, &io_ctx] () { + rpc_handler = std::make_unique (*node, ipc_server, config.rpc, + [&ipc_server, &workers = node->workers, io_ctx_w = std::weak_ptr{ io_ctx }] () { ipc_server.stop (); - workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (3), [&io_ctx] () { - io_ctx.stop (); + workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (3), [io_ctx_w] () { + if (auto io_ctx_l = io_ctx_w.lock ()) + { + io_ctx_l->stop (); + } }); }); rpc = nano::get_rpc (io_ctx, rpc_config, *rpc_handler); @@ -189,10 +194,13 @@ void nano::daemon::run (std::filesystem::path const & data_path, nano::node_flag } debug_assert (!nano::signal_handler_impl); - nano::signal_handler_impl = [this, &io_ctx] () { + nano::signal_handler_impl = [this, io_ctx_w = std::weak_ptr{ io_ctx }] () { logger.warn (nano::log::type::daemon, "Interrupt signal received, stopping..."); - io_ctx.stop (); + if (auto io_ctx_l = io_ctx_w.lock ()) + { + io_ctx_l->stop (); + } sig_int_or_term = 1; }; diff --git a/nano/nano_node/entry.cpp b/nano/nano_node/entry.cpp index 0b3c67a56..fa4534e6e 100644 --- a/nano/nano_node/entry.cpp +++ b/nano/nano_node/entry.cpp @@ -1129,8 +1129,8 @@ int main (int argc, char * const * argv) } } std::cout << boost::str (boost::format ("Starting generating %1% blocks...\n") % (count * 2)); - boost::asio::io_context io_ctx1; - boost::asio::io_context io_ctx2; + auto io_ctx1 = std::make_shared (); + auto io_ctx2 = std::make_shared (); nano::work_pool work{ network_params.network, std::numeric_limits::max () }; auto path1 (nano::unique_path ()); auto path2 (nano::unique_path ()); @@ -1283,8 +1283,8 @@ int main (int argc, char * const * argv) auto end (std::chrono::high_resolution_clock::now ()); auto time (std::chrono::duration_cast (end - begin).count ()); std::cout << boost::str (boost::format ("%|1$ 12d| us \n%2% frontiers per second\n") % time % ((count + 1) * 1000000 / time)); - io_ctx1.stop (); - io_ctx2.stop (); + io_ctx1->stop (); + io_ctx2->stop (); runner1.join (); runner2.join (); node1->stop (); diff --git a/nano/nano_rpc/entry.cpp b/nano/nano_rpc/entry.cpp index 65e1de81e..b198d2082 100644 --- a/nano/nano_rpc/entry.cpp +++ b/nano/nano_rpc/entry.cpp @@ -49,17 +49,23 @@ void run (std::filesystem::path const & data_path, std::vector cons rpc_config.tls_config = tls_config; } - boost::asio::io_context io_ctx; + std::shared_ptr io_ctx = std::make_shared (); + nano::signal_manager sigman; try { - nano::ipc_rpc_processor ipc_rpc_processor (io_ctx, rpc_config); + nano::ipc_rpc_processor ipc_rpc_processor (*io_ctx, rpc_config); auto rpc = nano::get_rpc (io_ctx, rpc_config, ipc_rpc_processor); rpc->start (); debug_assert (!nano::signal_handler_impl); - nano::signal_handler_impl = [&io_ctx] () { - io_ctx.stop (); + nano::signal_handler_impl = [io_ctx_w = std::weak_ptr{ io_ctx }] () { + logger.warn (nano::log::type::daemon, "Interrupt signal received, stopping..."); + + if (auto io_ctx_l = io_ctx_w.lock ()) + { + io_ctx_l->stop (); + } sig_int_or_term = 1; }; diff --git a/nano/nano_wallet/entry.cpp b/nano/nano_wallet/entry.cpp index 69d8262aa..5c3a0a29a 100644 --- a/nano/nano_wallet/entry.cpp +++ b/nano/nano_wallet/entry.cpp @@ -122,7 +122,8 @@ int run_wallet (QApplication & application, int argc, char * const * argv, std:: config.node.websocket_config.tls_config = tls_config; } - boost::asio::io_context io_ctx; + std::shared_ptr io_ctx = std::make_shared (); + nano::thread_runner runner (io_ctx, config.node.io_threads); std::shared_ptr node; diff --git a/nano/node/ipc/ipc_server.cpp b/nano/node/ipc/ipc_server.cpp index 781f4051a..3d13d6676 100644 --- a/nano/node/ipc/ipc_server.cpp +++ b/nano/node/ipc/ipc_server.cpp @@ -463,12 +463,13 @@ class socket_transport : public nano::ipc::transport { public: socket_transport (nano::ipc::ipc_server & server_a, ENDPOINT_TYPE endpoint_a, nano::ipc::ipc_config_transport & config_transport_a, int concurrency_a) : - server (server_a), config_transport (config_transport_a) + server (server_a), + config_transport (config_transport_a) { // Using a per-transport event dispatcher? if (concurrency_a > 0) { - io_ctx = std::make_unique (); + io_ctx = std::make_shared (); } boost::asio::socket_base::reuse_address option (true); @@ -482,7 +483,7 @@ public: // A separate io_context for domain sockets may facilitate better performance on some systems. if (concurrency_a > 0) { - runner = std::make_unique (*io_ctx, static_cast (concurrency_a)); + runner = std::make_unique (io_ctx, static_cast (concurrency_a)); } } @@ -544,7 +545,7 @@ private: nano::ipc::ipc_server & server; nano::ipc::ipc_config_transport & config_transport; std::unique_ptr runner; - std::unique_ptr io_ctx; + std::shared_ptr io_ctx; std::unique_ptr acceptor; }; diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 338bab5fc..a7babb794 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -120,15 +120,16 @@ nano::keypair nano::load_or_create_node_id (std::filesystem::path const & applic } } -nano::node::node (boost::asio::io_context & io_ctx_a, uint16_t peering_port_a, std::filesystem::path const & application_path_a, nano::work_pool & work_a, nano::node_flags flags_a, unsigned seq) : +nano::node::node (std::shared_ptr io_ctx_a, uint16_t peering_port_a, std::filesystem::path const & application_path_a, nano::work_pool & work_a, nano::node_flags flags_a, unsigned seq) : node (io_ctx_a, application_path_a, nano::node_config (peering_port_a), work_a, flags_a, seq) { } -nano::node::node (boost::asio::io_context & io_ctx_a, std::filesystem::path const & application_path_a, nano::node_config const & config_a, nano::work_pool & work_a, nano::node_flags flags_a, unsigned seq) : +nano::node::node (std::shared_ptr io_ctx_a, std::filesystem::path const & application_path_a, nano::node_config const & config_a, nano::work_pool & work_a, nano::node_flags flags_a, unsigned seq) : + io_ctx_shared{ io_ctx_a }, + io_ctx{ *io_ctx_shared }, node_id{ load_or_create_node_id (application_path_a) }, write_database_queue (!flags_a.force_use_write_database_queue && (config_a.rocksdb_config.enable)), - io_ctx (io_ctx_a), node_initialized_latch (1), config (config_a), network_params{ config.network_params }, @@ -1380,7 +1381,7 @@ nano::node_wrapper::node_wrapper (std::filesystem::path const & path_a, std::fil auto & node_config = daemon_config.node; node_config.peering_port = 24000; - node = std::make_shared (*io_context, path_a, node_config, work, node_flags_a); + node = std::make_shared (io_context, path_a, node_config, work, node_flags_a); } nano::node_wrapper::~node_wrapper () diff --git a/nano/node/node.hpp b/nano/node/node.hpp index 0b4259316..fe7e901a0 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -63,11 +63,11 @@ namespace scheduler backlog_population::config backlog_population_config (node_config const &); outbound_bandwidth_limiter::config outbound_bandwidth_limiter_config (node_config const &); -class node final : public std::enable_shared_from_this +class node final : public std::enable_shared_from_this { public: - node (boost::asio::io_context &, uint16_t, std::filesystem::path const &, nano::work_pool &, nano::node_flags = nano::node_flags (), unsigned seq = 0); - node (boost::asio::io_context &, std::filesystem::path const &, nano::node_config const &, nano::work_pool &, nano::node_flags = nano::node_flags (), unsigned seq = 0); + node (std::shared_ptr, uint16_t peering_port, std::filesystem::path const & application_path, nano::work_pool &, nano::node_flags = nano::node_flags (), unsigned seq = 0); + node (std::shared_ptr, std::filesystem::path const & application_path, nano::node_config const &, nano::work_pool &, nano::node_flags = nano::node_flags (), unsigned seq = 0); ~node (); public: @@ -133,6 +133,7 @@ public: public: const nano::keypair node_id; nano::write_database_queue write_database_queue; + std::shared_ptr io_ctx_shared; boost::asio::io_context & io_ctx; boost::latch node_initialized_latch; nano::node_config config; diff --git a/nano/rpc/rpc.cpp b/nano/rpc/rpc.cpp index 687c9b316..196feee1a 100644 --- a/nano/rpc/rpc.cpp +++ b/nano/rpc/rpc.cpp @@ -12,10 +12,11 @@ #include #endif -nano::rpc::rpc (boost::asio::io_context & io_ctx_a, nano::rpc_config config_a, nano::rpc_handler_interface & rpc_handler_interface_a) : +nano::rpc::rpc (std::shared_ptr io_ctx_a, nano::rpc_config config_a, nano::rpc_handler_interface & rpc_handler_interface_a) : config (std::move (config_a)), - acceptor (io_ctx_a), - io_ctx (io_ctx_a), + io_ctx_shared (io_ctx_a), + io_ctx (*io_ctx_shared), + acceptor (io_ctx), rpc_handler_interface (rpc_handler_interface_a) { rpc_handler_interface.rpc_instance (*this); @@ -78,7 +79,7 @@ void nano::rpc::stop () acceptor.close (); } -std::unique_ptr nano::get_rpc (boost::asio::io_context & io_ctx_a, nano::rpc_config const & config_a, nano::rpc_handler_interface & rpc_handler_interface_a) +std::unique_ptr nano::get_rpc (std::shared_ptr io_ctx_a, nano::rpc_config const & config_a, nano::rpc_handler_interface & rpc_handler_interface_a) { std::unique_ptr impl; diff --git a/nano/rpc/rpc.hpp b/nano/rpc/rpc.hpp index 99f66b260..353d26313 100644 --- a/nano/rpc/rpc.hpp +++ b/nano/rpc/rpc.hpp @@ -20,25 +20,29 @@ class rpc_handler_interface; class rpc { public: - rpc (boost::asio::io_context & io_ctx_a, nano::rpc_config config_a, nano::rpc_handler_interface & rpc_handler_interface_a); + rpc (std::shared_ptr, nano::rpc_config config_a, nano::rpc_handler_interface & rpc_handler_interface_a); virtual ~rpc (); + void start (); - virtual void accept (); void stop (); - std::uint16_t listening_port () + virtual void accept (); + + std::uint16_t listening_port () const { return acceptor.local_endpoint ().port (); } +public: nano::logger logger{ "rpc" }; nano::rpc_config config; - boost::asio::ip::tcp::acceptor acceptor; + std::shared_ptr io_ctx_shared; boost::asio::io_context & io_ctx; + boost::asio::ip::tcp::acceptor acceptor; nano::rpc_handler_interface & rpc_handler_interface; bool stopped{ false }; }; /** Returns the correct RPC implementation based on TLS configuration */ -std::unique_ptr get_rpc (boost::asio::io_context & io_ctx_a, nano::rpc_config const & config_a, nano::rpc_handler_interface & rpc_handler_interface_a); +std::unique_ptr get_rpc (std::shared_ptr, nano::rpc_config const & config_a, nano::rpc_handler_interface & rpc_handler_interface_a); } diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 8eef6f5d3..7d4e0705f 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -1741,7 +1741,7 @@ TEST (rpc, version) auto const rpc_ctx = add_rpc (system, node1); boost::property_tree::ptree request1; request1.put ("action", "version"); - test_response response1 (request1, rpc_ctx.rpc->listening_port (), system.io_ctx); + test_response response1 (request1, rpc_ctx.rpc->listening_port (), *system.io_ctx); ASSERT_TIMELY (5s, response1.status != 0); ASSERT_EQ (200, response1.status); ASSERT_EQ ("1", response1.json.get ("rpc_version")); @@ -2506,7 +2506,7 @@ TEST (rpc, bootstrap) request.put ("action", "bootstrap"); request.put ("address", "::ffff:127.0.0.1"); request.put ("port", node1->network.endpoint ().port ()); - test_response response (request, rpc_ctx.rpc->listening_port (), system0.io_ctx); + test_response response (request, rpc_ctx.rpc->listening_port (), *system0.io_ctx); while (response.status == 0) { system0.poll (); @@ -6046,7 +6046,7 @@ TEST (rpc, simultaneous_calls) const auto ipc_tcp_port = ipc_server.listening_tcp_port (); ASSERT_TRUE (ipc_tcp_port.has_value ()); rpc_config.rpc_process.num_ipc_connections = 8; - nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config, ipc_tcp_port.value ()); + nano::ipc_rpc_processor ipc_rpc_processor (*system.io_ctx, rpc_config, ipc_tcp_port.value ()); nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); rpc.start (); boost::property_tree::ptree request; @@ -6057,7 +6057,7 @@ TEST (rpc, simultaneous_calls) std::array, num> test_responses; for (int i = 0; i < num; ++i) { - test_responses[i] = std::make_unique (request, system.io_ctx); + test_responses[i] = std::make_unique (request, *system.io_ctx); } std::promise promise; @@ -6087,7 +6087,7 @@ TEST (rpc, simultaneous_calls) rpc.stop (); system.stop (); ipc_server.stop (); - system.io_ctx.stop (); + system.io_ctx->stop (); runner.join (); } diff --git a/nano/rpc_test/rpc_context.cpp b/nano/rpc_test/rpc_context.cpp index fc5a601d5..31cadfdc4 100644 --- a/nano/rpc_test/rpc_context.cpp +++ b/nano/rpc_test/rpc_context.cpp @@ -22,7 +22,7 @@ nano::test::rpc_context::rpc_context (std::shared_ptr & rpc_a, std::u void nano::test::wait_response_impl (nano::test::system & system, rpc_context const & rpc_ctx, boost::property_tree::ptree & request, std::chrono::duration const & time, boost::property_tree::ptree & response_json) { - test_response response (request, rpc_ctx.rpc->listening_port (), system.io_ctx); + test_response response (request, rpc_ctx.rpc->listening_port (), *system.io_ctx); ASSERT_TIMELY (time, response.status != 0); ASSERT_EQ (200, response.status); response_json = response.json; @@ -49,7 +49,7 @@ nano::test::rpc_context nano::test::add_rpc (nano::test::system & system, std::s nano::rpc_config rpc_config (node_a->network_params.network, system.get_available_port (), true); const auto ipc_tcp_port = ipc_server->listening_tcp_port (); debug_assert (ipc_tcp_port.has_value ()); - auto ipc_rpc_processor (std::make_unique (system.io_ctx, rpc_config, ipc_tcp_port.value ())); + auto ipc_rpc_processor (std::make_unique (*system.io_ctx, rpc_config, ipc_tcp_port.value ())); auto rpc (std::make_shared (system.io_ctx, rpc_config, *ipc_rpc_processor)); rpc->start (); diff --git a/nano/slow_test/bootstrap.cpp b/nano/slow_test/bootstrap.cpp index 6492ad6bd..95662612a 100644 --- a/nano/slow_test/bootstrap.cpp +++ b/nano/slow_test/bootstrap.cpp @@ -34,7 +34,7 @@ public: node_rpc_config{}, rpc_config{ node.network_params.network, port, true }, ipc{ node, node_rpc_config }, - ipc_rpc_processor{ system.io_ctx, rpc_config }, + ipc_rpc_processor{ *system.io_ctx, rpc_config }, rpc{ system.io_ctx, rpc_config, ipc_rpc_processor } { } diff --git a/nano/test_common/system.cpp b/nano/test_common/system.cpp index f4522c62b..379d0ec7b 100644 --- a/nano/test_common/system.cpp +++ b/nano/test_common/system.cpp @@ -25,6 +25,49 @@ std::string nano::error_system_messages::message (int ev) const return "Invalid error code"; } +/* + * system + */ + +nano::test::system::system () : + io_ctx{ std::make_shared () } +{ + auto scale_str = std::getenv ("DEADLINE_SCALE_FACTOR"); + if (scale_str) + { + deadline_scaling_factor = std::stod (scale_str); + } +} + +nano::test::system::system (uint16_t count_a, nano::transport::transport_type type_a, nano::node_flags flags_a) : + system () +{ + nodes.reserve (count_a); + for (uint16_t i (0); i < count_a; ++i) + { + add_node (default_config (), flags_a, type_a); + } +} + +nano::test::system::~system () +{ + // Only stop system in destructor to avoid confusing and random bugs when debugging assertions that hit deadline expired condition + stop (); + +#ifndef _WIN32 + // Windows cannot remove the log and data files while they are still owned by this process. + // They will be removed later + + // Clean up tmp directories created by the tests. Since it's sometimes useful to + // see log files after test failures, an environment variable is supported to + // retain the files. + if (std::getenv ("TEST_KEEP_TMPDIRS") == nullptr) + { + nano::remove_temporary_directories (); + } +#endif +} + nano::node & nano::test::system::node (std::size_t index) const { debug_assert (index < nodes.size ()); @@ -142,44 +185,6 @@ std::shared_ptr nano::test::system::make_disconnected_node (std::opt return node; } -nano::test::system::system () -{ - auto scale_str = std::getenv ("DEADLINE_SCALE_FACTOR"); - if (scale_str) - { - deadline_scaling_factor = std::stod (scale_str); - } -} - -nano::test::system::system (uint16_t count_a, nano::transport::transport_type type_a, nano::node_flags flags_a) : - system () -{ - nodes.reserve (count_a); - for (uint16_t i (0); i < count_a; ++i) - { - add_node (default_config (), flags_a, type_a); - } -} - -nano::test::system::~system () -{ - // Only stop system in destructor to avoid confusing and random bugs when debugging assertions that hit deadline expired condition - stop (); - -#ifndef _WIN32 - // Windows cannot remove the log and data files while they are still owned by this process. - // They will be removed later - - // Clean up tmp directories created by the tests. Since it's sometimes useful to - // see log files after test failures, an environment variable is supported to - // retain the files. - if (std::getenv ("TEST_KEEP_TMPDIRS") == nullptr) - { - nano::remove_temporary_directories (); - } -#endif -} - void nano::test::system::ledger_initialization_set (std::vector const & reps, nano::amount const & reserve) { nano::block_hash previous = nano::dev::genesis->hash (); @@ -285,7 +290,7 @@ void nano::test::system::deadline_set (std::chrono::duration std::error_code nano::test::system::poll (std::chrono::nanoseconds const & wait_time) { #if NANO_ASIO_HANDLER_TRACKING == 0 - io_ctx.run_one_for (wait_time); + io_ctx->run_one_for (wait_time); #else nano::timer<> timer; timer.start (); @@ -331,7 +336,7 @@ void nano::test::system::delay_ms (std::chrono::milliseconds const & delay) auto endtime = now + delay; while (now <= endtime) { - io_ctx.run_one_for (endtime - now); + io_ctx->run_one_for (endtime - now); now = std::chrono::steady_clock::now (); } } diff --git a/nano/test_common/system.hpp b/nano/test_common/system.hpp index 4230742e9..00808006a 100644 --- a/nano/test_common/system.hpp +++ b/nano/test_common/system.hpp @@ -74,7 +74,7 @@ namespace test uint16_t get_available_port (); public: - boost::asio::io_context io_ctx; + std::shared_ptr io_ctx; std::vector> nodes; nano::stats stats; nano::logger logger{ "tests" }; From 0e441a91281bff7a4ae05c7cb798728a1aacd852 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Wed, 20 Mar 2024 11:10:19 +0000 Subject: [PATCH 063/128] Remove inclusion of election.hpp from other headers Moves election_behavior enum and vote_with_weight_info classes in to their own files. Before: [118/118] Linking CXX executable rpc_test ninja 1208.77s user 85.03s system 875% cpu 2:27.78 total After: [39/39] Linking CXX executable rpc_test ninja 338.18s user 25.74s system 714% cpu 50.953 total --- nano/core_test/confirmation_solicitor.cpp | 1 + nano/core_test/election_scheduler.cpp | 1 + nano/core_test/request_aggregator.cpp | 1 + nano/core_test/vote_processor.cpp | 1 + nano/core_test/wallet.cpp | 1 + nano/core_test/wallets.cpp | 1 + nano/node/CMakeLists.txt | 2 ++ nano/node/active_transactions.hpp | 3 ++- nano/node/election.hpp | 30 ++--------------------- nano/node/election_behavior.hpp | 22 +++++++++++++++++ nano/node/node.hpp | 1 - nano/node/scheduler/manual.cpp | 1 + nano/node/scheduler/priority.cpp | 1 + nano/node/vote_cache.cpp | 3 ++- nano/node/vote_with_weight_info.hpp | 18 ++++++++++++++ nano/node/websocket.hpp | 2 +- nano/qt/qt.cpp | 1 + nano/rpc_test/rpc.cpp | 1 + nano/test_common/testutil.cpp | 1 + 19 files changed, 60 insertions(+), 32 deletions(-) create mode 100644 nano/node/election_behavior.hpp create mode 100644 nano/node/vote_with_weight_info.hpp diff --git a/nano/core_test/confirmation_solicitor.cpp b/nano/core_test/confirmation_solicitor.cpp index 7afa210a2..a35ff9891 100644 --- a/nano/core_test/confirmation_solicitor.cpp +++ b/nano/core_test/confirmation_solicitor.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/core_test/election_scheduler.cpp b/nano/core_test/election_scheduler.cpp index c453bfe00..92afc9b2d 100644 --- a/nano/core_test/election_scheduler.cpp +++ b/nano/core_test/election_scheduler.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/nano/core_test/request_aggregator.cpp b/nano/core_test/request_aggregator.cpp index 7b1913f41..3851fa82e 100644 --- a/nano/core_test/request_aggregator.cpp +++ b/nano/core_test/request_aggregator.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/nano/core_test/vote_processor.cpp b/nano/core_test/vote_processor.cpp index f755eb8ac..6e271568e 100644 --- a/nano/core_test/vote_processor.cpp +++ b/nano/core_test/vote_processor.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/nano/core_test/wallet.cpp b/nano/core_test/wallet.cpp index de2cfb42c..358ee2d2a 100644 --- a/nano/core_test/wallet.cpp +++ b/nano/core_test/wallet.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/core_test/wallets.cpp b/nano/core_test/wallets.cpp index af4b953b1..48c0e79dc 100644 --- a/nano/core_test/wallets.cpp +++ b/nano/core_test/wallets.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/nano/node/CMakeLists.txt b/nano/node/CMakeLists.txt index 9602dc0a2..439ee94fd 100644 --- a/nano/node/CMakeLists.txt +++ b/nano/node/CMakeLists.txt @@ -75,6 +75,7 @@ add_library( distributed_work_factory.cpp election.hpp election.cpp + election_behavior.hpp election_insertion_result.hpp epoch_upgrader.hpp epoch_upgrader.cpp @@ -164,6 +165,7 @@ add_library( vote_cache.cpp vote_processor.hpp vote_processor.cpp + vote_with_weight_info.hpp voting.hpp voting.cpp wallet.hpp diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index 7323bf51d..e1dececf0 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -1,8 +1,9 @@ #pragma once #include -#include +#include #include +#include #include #include diff --git a/nano/node/election.hpp b/nano/node/election.hpp index 59cf2ea9e..5ea0c43ce 100644 --- a/nano/node/election.hpp +++ b/nano/node/election.hpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include @@ -24,34 +26,6 @@ public: nano::block_hash hash; }; -class vote_with_weight_info final -{ -public: - nano::account representative; - std::chrono::steady_clock::time_point time; - uint64_t timestamp; - nano::block_hash hash; - nano::uint128_t weight; -}; - -enum class election_behavior -{ - normal, - /** - * Hinted elections: - * - shorter timespan - * - limited space inside AEC - */ - hinted, - /** - * Optimistic elections: - * - shorter timespan - * - limited space inside AEC - * - more frequent confirmation requests - */ - optimistic, -}; - nano::stat::detail to_stat_detail (nano::election_behavior); // map of vote weight per block, ordered greater first diff --git a/nano/node/election_behavior.hpp b/nano/node/election_behavior.hpp new file mode 100644 index 000000000..c78733a30 --- /dev/null +++ b/nano/node/election_behavior.hpp @@ -0,0 +1,22 @@ +#pragma once + +namespace nano +{ +enum class election_behavior +{ + normal, + /** + * Hinted elections: + * - shorter timespan + * - limited space inside AEC + */ + hinted, + /** + * Optimistic elections: + * - shorter timespan + * - limited space inside AEC + * - more frequent confirmation requests + */ + optimistic, +}; +} diff --git a/nano/node/node.hpp b/nano/node/node.hpp index fe7e901a0..5c0e705a3 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include diff --git a/nano/node/scheduler/manual.cpp b/nano/node/scheduler/manual.cpp index 7fc908bd3..f84774bc4 100644 --- a/nano/node/scheduler/manual.cpp +++ b/nano/node/scheduler/manual.cpp @@ -1,3 +1,4 @@ +#include #include #include diff --git a/nano/node/scheduler/priority.cpp b/nano/node/scheduler/priority.cpp index f8e03ff39..b37276e06 100644 --- a/nano/node/scheduler/priority.cpp +++ b/nano/node/scheduler/priority.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/nano/node/vote_cache.cpp b/nano/node/vote_cache.cpp index 4d40807d2..7779b0aff 100644 --- a/nano/node/vote_cache.cpp +++ b/nano/node/vote_cache.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -280,4 +281,4 @@ nano::error nano::vote_cache_config::deserialize (nano::tomlconfig & toml) age_cutoff = std::chrono::seconds{ age_cutoff_l }; return toml.get_error (); -} \ No newline at end of file +} diff --git a/nano/node/vote_with_weight_info.hpp b/nano/node/vote_with_weight_info.hpp new file mode 100644 index 000000000..02daa9bb8 --- /dev/null +++ b/nano/node/vote_with_weight_info.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include + +namespace nano +{ +class vote_with_weight_info final +{ +public: + nano::account representative; + std::chrono::steady_clock::time_point time; + uint64_t timestamp; + nano::block_hash hash; + nano::uint128_t weight; +}; +} diff --git a/nano/node/websocket.hpp b/nano/node/websocket.hpp index 621219840..db470f743 100644 --- a/nano/node/websocket.hpp +++ b/nano/node/websocket.hpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/nano/qt/qt.cpp b/nano/qt/qt.cpp index 6c578f06b..744cf095a 100644 --- a/nano/qt/qt.cpp +++ b/nano/qt/qt.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 7d4e0705f..5d5e4190c 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/test_common/testutil.cpp b/nano/test_common/testutil.cpp index ce80cfeae..114a7813b 100644 --- a/nano/test_common/testutil.cpp +++ b/nano/test_common/testutil.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include From c5b24e8000cb92da74d93f6fcea57397827b3fb8 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Wed, 20 Mar 2024 11:26:35 +0000 Subject: [PATCH 064/128] Reduce headers needed by election.hpp and moves election_status in to its own file Before: [39/39] Linking CXX executable rpc_test ninja 338.18s user 25.74s system 714% cpu 50.953 total After: [39/39] Linking CXX executable rpc_test ninja 339.33s user 27.66s system 778% cpu 47.155 total --- nano/node/CMakeLists.txt | 1 + nano/node/active_transactions.hpp | 1 + nano/node/election.hpp | 5 ++-- nano/node/election_status.hpp | 39 +++++++++++++++++++++++++++++++ nano/node/node_observers.hpp | 1 + nano/secure/common.hpp | 25 -------------------- 6 files changed, 45 insertions(+), 27 deletions(-) create mode 100644 nano/node/election_status.hpp diff --git a/nano/node/CMakeLists.txt b/nano/node/CMakeLists.txt index 439ee94fd..aee2e628f 100644 --- a/nano/node/CMakeLists.txt +++ b/nano/node/CMakeLists.txt @@ -77,6 +77,7 @@ add_library( election.cpp election_behavior.hpp election_insertion_result.hpp + election_status.hpp epoch_upgrader.hpp epoch_upgrader.cpp ipc/action_handler.hpp diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index e1dececf0..8d966dbba 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/node/election.hpp b/nano/node/election.hpp index 5ea0c43ce..22dc8ab5c 100644 --- a/nano/node/election.hpp +++ b/nano/node/election.hpp @@ -2,10 +2,10 @@ #include #include +#include #include +#include #include -#include -#include #include #include @@ -13,6 +13,7 @@ namespace nano { +class block; class channel; class confirmation_solicitor; class inactive_cache_information; diff --git a/nano/node/election_status.hpp b/nano/node/election_status.hpp new file mode 100644 index 000000000..014a14ad7 --- /dev/null +++ b/nano/node/election_status.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include + +#include +#include + +namespace nano +{ +class block; +} + +namespace nano +{ +/* Defines the possible states for an election to stop in */ +enum class election_status_type : uint8_t +{ + ongoing = 0, + active_confirmed_quorum = 1, + active_confirmation_height = 2, + inactive_confirmation_height = 3, + stopped = 5 +}; + +/* Holds a summary of an election */ +class election_status final +{ +public: + std::shared_ptr winner; + nano::amount tally{ 0 }; + nano::amount final_tally{ 0 }; + std::chrono::milliseconds election_end{ std::chrono::duration_cast (std::chrono::system_clock::now ().time_since_epoch ()) }; + std::chrono::milliseconds election_duration{ std::chrono::duration_values::zero () }; + unsigned confirmation_request_count{ 0 }; + unsigned block_count{ 0 }; + unsigned voter_count{ 0 }; + election_status_type type{ nano::election_status_type::inactive_confirmation_height }; +}; +} diff --git a/nano/node/node_observers.hpp b/nano/node/node_observers.hpp index 8825655c5..083be5c37 100644 --- a/nano/node/node_observers.hpp +++ b/nano/node/node_observers.hpp @@ -7,6 +7,7 @@ namespace nano { +class election_status; class telemetry; class node_observers final { diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index d585e1b22..9ce8c55e1 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -345,30 +345,5 @@ enum class confirmation_height_mode bounded }; -/* Defines the possible states for an election to stop in */ -enum class election_status_type : uint8_t -{ - ongoing = 0, - active_confirmed_quorum = 1, - active_confirmation_height = 2, - inactive_confirmation_height = 3, - stopped = 5 -}; - -/* Holds a summary of an election */ -class election_status final -{ -public: - std::shared_ptr winner; - nano::amount tally{ 0 }; - nano::amount final_tally{ 0 }; - std::chrono::milliseconds election_end{ std::chrono::duration_cast (std::chrono::system_clock::now ().time_since_epoch ()) }; - std::chrono::milliseconds election_duration{ std::chrono::duration_values::zero () }; - unsigned confirmation_request_count{ 0 }; - unsigned block_count{ 0 }; - unsigned voter_count{ 0 }; - election_status_type type{ nano::election_status_type::inactive_confirmation_height }; -}; - nano::wallet_id random_wallet_id (); } From f430d8fb150fa6015a276eec7974c09806959b47 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Wed, 20 Mar 2024 12:14:49 +0000 Subject: [PATCH 065/128] Move local_vote_history in to its own file. --- nano/core_test/node.cpp | 1 + nano/core_test/request_aggregator.cpp | 1 + nano/core_test/voting.cpp | 1 + nano/lib/numbers.hpp | 8 ++ nano/node/CMakeLists.txt | 2 + nano/node/active_transactions.hpp | 3 +- nano/node/blockprocessor.cpp | 1 + nano/node/election.cpp | 1 + nano/node/local_vote_history.cpp | 115 ++++++++++++++++++++++++++ nano/node/local_vote_history.hpp | 74 +++++++++++++++++ nano/node/node.cpp | 6 +- nano/node/node.hpp | 3 +- nano/node/request_aggregator.cpp | 1 + nano/node/voting.cpp | 113 +------------------------ nano/node/voting.hpp | 54 +----------- nano/secure/common.hpp | 8 -- nano/secure/vote.hpp | 7 +- 17 files changed, 222 insertions(+), 177 deletions(-) create mode 100644 nano/node/local_vote_history.cpp create mode 100644 nano/node/local_vote_history.hpp diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index 03c05211b..f139d752d 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 3851fa82e..3bb865f9f 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 d6d644292..c1122bc8a 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 3021524c4..7396de3a0 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 aee2e628f..f3283f222 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 8d966dbba..0ef5dcbd4 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 a41136e4c..3349d0162 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 000000000..8569c225f --- /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 000000000..2cf603ec4 --- /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 a7babb794..6a3dbd8ac 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 5c0e705a3..5f62ba54e 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 32ad38d61..62d37146d 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 13a66ea9e..5101ca51d 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 24c5813dd..0ed940e35 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 9ce8c55e1..aab26e8be 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 9ae7b0689..7c29d5384 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 +} From 14860eaa09c3dd9f4f68e81a2f1019386806e1b7 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Wed, 20 Mar 2024 13:32:41 +0000 Subject: [PATCH 066/128] Move vote_generator and vote_spacing in to their own files. Recompile time for voting.hpp before and vote headers after Before: [117/117] Linking CXX executable rpc_test ninja 1262.92s user 87.90s system 869% cpu 2:35.28 total After: [16/16] Linking CXX executable nano_node ninja 98.05s user 11.24s system 377% cpu 28.917 total --- nano/core_test/node.cpp | 1 + nano/core_test/voting.cpp | 3 +- nano/lib/processing_queue.hpp | 2 +- nano/node/CMakeLists.txt | 6 +- nano/node/active_transactions.hpp | 9 ++- nano/node/backlog_population.cpp | 2 + nano/node/election.cpp | 1 + nano/node/node.cpp | 13 +++-- nano/node/node.hpp | 6 +- nano/node/node_observers.hpp | 8 +++ nano/node/request_aggregator.cpp | 2 +- nano/node/scheduler/priority.hpp | 4 ++ nano/node/{voting.cpp => vote_generator.cpp} | 58 ++++---------------- nano/node/{voting.hpp => vote_generator.hpp} | 45 +++------------ nano/node/vote_spacing.cpp | 39 +++++++++++++ nano/node/vote_spacing.hpp | 45 +++++++++++++++ nano/test_common/ledger.hpp | 1 + 17 files changed, 149 insertions(+), 96 deletions(-) rename nano/node/{voting.cpp => vote_generator.cpp} (84%) rename nano/node/{voting.hpp => vote_generator.hpp} (75%) create mode 100644 nano/node/vote_spacing.cpp create mode 100644 nano/node/vote_spacing.hpp diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index f139d752d..48fd15a04 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/core_test/voting.cpp b/nano/core_test/voting.cpp index c1122bc8a..3c08618ac 100644 --- a/nano/core_test/voting.cpp +++ b/nano/core_test/voting.cpp @@ -1,7 +1,8 @@ #include #include #include -#include +#include +#include #include #include #include diff --git a/nano/lib/processing_queue.hpp b/nano/lib/processing_queue.hpp index e9f317373..4570c9521 100644 --- a/nano/lib/processing_queue.hpp +++ b/nano/lib/processing_queue.hpp @@ -108,7 +108,7 @@ public: } public: // Container info - std::unique_ptr collect_container_info (std::string const & name) + std::unique_ptr collect_container_info (std::string const & name) const { nano::lock_guard guard{ mutex }; diff --git a/nano/node/CMakeLists.txt b/nano/node/CMakeLists.txt index f3283f222..3e3dd1dd3 100644 --- a/nano/node/CMakeLists.txt +++ b/nano/node/CMakeLists.txt @@ -166,11 +166,13 @@ add_library( unchecked_map.hpp vote_cache.hpp vote_cache.cpp + vote_generator.hpp + vote_generator.cpp vote_processor.hpp vote_processor.cpp + vote_spacing.hpp + vote_spacing.cpp vote_with_weight_info.hpp - voting.hpp - voting.cpp wallet.hpp wallet.cpp websocket.hpp diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index 0ef5dcbd4..da475cfd6 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -17,6 +16,7 @@ #include #include #include +#include #include namespace mi = boost::multi_index; @@ -32,7 +32,14 @@ class election; class vote; class confirmation_height_processor; class stats; +} +namespace nano::store +{ +class read_transaction; +} +namespace nano +{ class recently_confirmed_cache final { public: diff --git a/nano/node/backlog_population.cpp b/nano/node/backlog_population.cpp index 6b2a45156..f459b5069 100644 --- a/nano/node/backlog_population.cpp +++ b/nano/node/backlog_population.cpp @@ -2,7 +2,9 @@ #include #include #include +#include #include +#include nano::backlog_population::backlog_population (const config & config_a, nano::store::component & store_a, nano::stats & stats_a) : config_m{ config_a }, diff --git a/nano/node/election.cpp b/nano/node/election.cpp index 3349d0162..865593ab4 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 6a3dbd8ac..57e52b3d0 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -4,8 +4,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -177,8 +178,10 @@ nano::node::node (std::shared_ptr io_ctx_a, std::filesy 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 }, - generator{ config, *this, ledger, wallets, vote_processor, history, network, stats, logger, /* non-final */ false }, - final_generator{ config, *this, ledger, wallets, vote_processor, history, network, stats, logger, /* final */ true }, + generator_impl{ std::make_unique (config, *this, ledger, wallets, vote_processor, history, network, stats, logger, /* non-final */ false) }, + generator{ *generator_impl }, + final_generator_impl{ std::make_unique (config, *this, ledger, wallets, vote_processor, history, network, stats, logger, /* final */ true) }, + final_generator{ *final_generator_impl }, active{ *this, confirmation_height_processor, block_processor }, scheduler_impl{ std::make_unique (*this) }, scheduler{ *scheduler_impl }, @@ -547,8 +550,8 @@ std::unique_ptr nano::collect_container_info (no composite->add_component (collect_container_info (node.aggregator, "request_aggregator")); composite->add_component (node.scheduler.collect_container_info ("election_scheduler")); composite->add_component (node.vote_cache.collect_container_info ("vote_cache")); - composite->add_component (collect_container_info (node.generator, "vote_generator")); - composite->add_component (collect_container_info (node.final_generator, "vote_generator_final")); + composite->add_component (node.generator.collect_container_info ("vote_generator")); + composite->add_component (node.final_generator.collect_container_info ("vote_generator_final")); 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")); diff --git a/nano/node/node.hpp b/nano/node/node.hpp index 5f62ba54e..6d807463c 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -172,8 +172,10 @@ public: nano::vote_uniquer vote_uniquer; nano::confirmation_height_processor confirmation_height_processor; nano::vote_cache vote_cache; - nano::vote_generator generator; - nano::vote_generator final_generator; + std::unique_ptr generator_impl; + nano::vote_generator & generator; + std::unique_ptr final_generator_impl; + nano::vote_generator & final_generator; nano::active_transactions active; private: // Placed here to maintain initialization order diff --git a/nano/node/node_observers.hpp b/nano/node/node_observers.hpp index 083be5c37..d1b074f2a 100644 --- a/nano/node/node_observers.hpp +++ b/nano/node/node_observers.hpp @@ -9,6 +9,14 @@ namespace nano { class election_status; class telemetry; +} +namespace nano::transport +{ +class channel; +} + +namespace nano +{ class node_observers final { public: diff --git a/nano/node/request_aggregator.cpp b/nano/node/request_aggregator.cpp index 62d37146d..1c399bc9c 100644 --- a/nano/node/request_aggregator.cpp +++ b/nano/node/request_aggregator.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/nano/node/scheduler/priority.hpp b/nano/node/scheduler/priority.hpp index e232b3d97..d8a7b2daf 100644 --- a/nano/node/scheduler/priority.hpp +++ b/nano/node/scheduler/priority.hpp @@ -17,6 +17,10 @@ class block; class container_info_component; class node; } +namespace nano::store +{ +class transaction; +} namespace nano::scheduler { diff --git a/nano/node/voting.cpp b/nano/node/vote_generator.cpp similarity index 84% rename from nano/node/voting.cpp rename to nano/node/vote_generator.cpp index 5101ca51d..addc72192 100644 --- a/nano/node/voting.cpp +++ b/nano/node/vote_generator.cpp @@ -5,52 +5,15 @@ #include #include #include +#include #include -#include +#include #include #include #include #include -void nano::vote_spacing::trim () -{ - recent.get ().erase (recent.get ().begin (), recent.get ().upper_bound (std::chrono::steady_clock::now () - delay)); -} - -bool nano::vote_spacing::votable (nano::root const & root_a, nano::block_hash const & hash_a) const -{ - bool result = true; - for (auto range = recent.get ().equal_range (root_a); result && range.first != range.second; ++range.first) - { - auto & item = *range.first; - result = hash_a == item.hash || item.time < std::chrono::steady_clock::now () - delay; - } - return result; -} - -void nano::vote_spacing::flag (nano::root const & root_a, nano::block_hash const & hash_a) -{ - trim (); - auto now = std::chrono::steady_clock::now (); - auto existing = recent.get ().find (root_a); - if (existing != recent.end ()) - { - recent.get ().modify (existing, [now] (entry & entry) { - entry.time = now; - }); - } - else - { - recent.insert ({ root_a, now, hash_a }); - } -} - -std::size_t nano::vote_spacing::size () const -{ - return recent.size (); -} - 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), @@ -58,7 +21,8 @@ nano::vote_generator::vote_generator (nano::node_config const & config_a, nano:: wallets (wallets_a), vote_processor (vote_processor_a), history (history_a), - spacing{ config_a.network_params.voting.delay }, + spacing_impl{ std::make_unique (config_a.network_params.voting.delay) }, + spacing{ *spacing_impl }, network (network_a), stats (stats_a), logger (logger_a), @@ -318,20 +282,20 @@ void nano::vote_generator::run () } } -std::unique_ptr nano::collect_container_info (nano::vote_generator & vote_generator, std::string const & name) +std::unique_ptr nano::vote_generator::collect_container_info (std::string const & name) const { std::size_t candidates_count = 0; std::size_t requests_count = 0; { - nano::lock_guard guard{ vote_generator.mutex }; - candidates_count = vote_generator.candidates.size (); - requests_count = vote_generator.requests.size (); + nano::lock_guard guard{ mutex }; + candidates_count = candidates.size (); + requests_count = requests.size (); } - auto sizeof_candidate_element = sizeof (decltype (vote_generator.candidates)::value_type); - auto sizeof_request_element = sizeof (decltype (vote_generator.requests)::value_type); + auto sizeof_candidate_element = sizeof (decltype (candidates)::value_type); + auto sizeof_request_element = sizeof (decltype (requests)::value_type); auto composite = std::make_unique (name); composite->add_component (std::make_unique (container_info{ "candidates", candidates_count, sizeof_candidate_element })); composite->add_component (std::make_unique (container_info{ "requests", requests_count, sizeof_request_element })); - composite->add_component (vote_generator.vote_generation_queue.collect_container_info ("vote_generation_queue")); + composite->add_component (vote_generation_queue.collect_container_info ("vote_generation_queue")); return composite; } diff --git a/nano/node/voting.hpp b/nano/node/vote_generator.hpp similarity index 75% rename from nano/node/voting.hpp rename to nano/node/vote_generator.hpp index 0ed940e35..4fc3d65e1 100644 --- a/nano/node/voting.hpp +++ b/nano/node/vote_generator.hpp @@ -29,42 +29,16 @@ class node; class node_config; class stats; class vote_processor; +class vote_spacing; class wallets; -namespace transport +} +namespace nano::transport { - class channel; +class channel; } -class vote_spacing final +namespace nano { - class entry - { - public: - nano::root root; - std::chrono::steady_clock::time_point time; - nano::block_hash hash; - }; - - boost::multi_index_container, - mi::member>, - mi::ordered_non_unique, - mi::member>>> - recent; - std::chrono::milliseconds const delay; - void trim (); - -public: - vote_spacing (std::chrono::milliseconds const & delay) : - delay{ delay } - { - } - bool votable (nano::root const & root_a, nano::block_hash const & hash_a) const; - void flag (nano::root const & root_a, nano::block_hash const & hash_a); - std::size_t size () const; -}; - class vote_generator final { private: @@ -85,6 +59,8 @@ public: void start (); void stop (); + std::unique_ptr collect_container_info (std::string const & name) const; + private: void run (); void broadcast (nano::unique_lock &); @@ -109,7 +85,8 @@ private: // Dependencies nano::wallets & wallets; nano::vote_processor & vote_processor; nano::local_vote_history & history; - nano::vote_spacing spacing; + std::unique_ptr spacing_impl; + nano::vote_spacing & spacing; nano::network & network; nano::stats & stats; nano::logger & logger; @@ -126,9 +103,5 @@ private: std::deque candidates; std::atomic stopped{ false }; std::thread thread; - - friend std::unique_ptr collect_container_info (vote_generator & vote_generator, std::string const & name); }; - -std::unique_ptr collect_container_info (vote_generator & generator, std::string const & name); } diff --git a/nano/node/vote_spacing.cpp b/nano/node/vote_spacing.cpp new file mode 100644 index 000000000..201a9d7b5 --- /dev/null +++ b/nano/node/vote_spacing.cpp @@ -0,0 +1,39 @@ +#include + +void nano::vote_spacing::trim () +{ + recent.get ().erase (recent.get ().begin (), recent.get ().upper_bound (std::chrono::steady_clock::now () - delay)); +} + +bool nano::vote_spacing::votable (nano::root const & root_a, nano::block_hash const & hash_a) const +{ + bool result = true; + for (auto range = recent.get ().equal_range (root_a); result && range.first != range.second; ++range.first) + { + auto & item = *range.first; + result = hash_a == item.hash || item.time < std::chrono::steady_clock::now () - delay; + } + return result; +} + +void nano::vote_spacing::flag (nano::root const & root_a, nano::block_hash const & hash_a) +{ + trim (); + auto now = std::chrono::steady_clock::now (); + auto existing = recent.get ().find (root_a); + if (existing != recent.end ()) + { + recent.get ().modify (existing, [now] (entry & entry) { + entry.time = now; + }); + } + else + { + recent.insert ({ root_a, now, hash_a }); + } +} + +std::size_t nano::vote_spacing::size () const +{ + return recent.size (); +} diff --git a/nano/node/vote_spacing.hpp b/nano/node/vote_spacing.hpp new file mode 100644 index 000000000..f46cd6352 --- /dev/null +++ b/nano/node/vote_spacing.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include + +#include +#include +#include +#include + +#include + +namespace mi = boost::multi_index; + +namespace nano +{ +class vote_spacing final +{ + class entry + { + public: + nano::root root; + std::chrono::steady_clock::time_point time; + nano::block_hash hash; + }; + + boost::multi_index_container, + mi::member>, + mi::ordered_non_unique, + mi::member>>> + recent; + std::chrono::milliseconds const delay; + void trim (); + +public: + vote_spacing (std::chrono::milliseconds const & delay) : + delay{ delay } + { + } + bool votable (nano::root const & root_a, nano::block_hash const & hash_a) const; + void flag (nano::root const & root_a, nano::block_hash const & hash_a); + std::size_t size () const; +}; +} diff --git a/nano/test_common/ledger.hpp b/nano/test_common/ledger.hpp index 6c81f46bf..e968f4d17 100644 --- a/nano/test_common/ledger.hpp +++ b/nano/test_common/ledger.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include namespace nano From 194003405148d7fa8e9c8ce553c47a82620be47a Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Wed, 20 Mar 2024 13:56:45 +0000 Subject: [PATCH 067/128] Remove inclusion of active_transactions.hpp from other headers Changes that edit active_transactions.hpp changed: Before: [116/116] Linking CXX executable rpc_test ninja 1194.40s user 83.30s system 860% cpu 2:28.51 total After: [49/49] Linking CXX executable rpc_test ninja 465.39s user 35.25s system 762% cpu 1:05.67 total --- nano/core_test/active_transactions.cpp | 1 + nano/core_test/bootstrap.cpp | 1 + nano/core_test/confirmation_height.cpp | 1 + nano/core_test/confirmation_solicitor.cpp | 1 + nano/core_test/conflicts.cpp | 1 + nano/core_test/election.cpp | 1 + nano/core_test/election_scheduler.cpp | 1 + nano/core_test/ledger.cpp | 1 + nano/core_test/node.cpp | 1 + nano/core_test/optimistic_scheduler.cpp | 1 + nano/core_test/rep_crawler.cpp | 1 + nano/core_test/request_aggregator.cpp | 1 + nano/core_test/vote_processor.cpp | 1 + nano/core_test/wallet.cpp | 1 + nano/core_test/wallets.cpp | 1 + nano/core_test/websocket.cpp | 1 + nano/nano_node/entry.cpp | 1 + nano/node/blockprocessor.cpp | 1 + nano/node/election.cpp | 1 + nano/node/json_handler.cpp | 1 + nano/node/node.cpp | 9 ++++++--- nano/node/node.hpp | 9 +++++---- nano/node/node_observers.hpp | 2 +- nano/node/repcrawler.cpp | 1 + nano/node/scheduler/hinted.cpp | 2 ++ nano/node/scheduler/manual.cpp | 1 + nano/node/scheduler/manual.hpp | 2 +- nano/node/scheduler/optimistic.cpp | 2 ++ nano/node/scheduler/priority.cpp | 1 + nano/node/scheduler/priority.hpp | 2 +- nano/node/websocket.cpp | 1 + nano/qt/qt.cpp | 1 + nano/rpc_test/rpc.cpp | 1 + nano/slow_test/node.cpp | 1 + nano/slow_test/vote_cache.cpp | 1 + nano/test_common/system.cpp | 1 + nano/test_common/testutil.cpp | 1 + 37 files changed, 48 insertions(+), 10 deletions(-) diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index e40e81701..ed9fef2da 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/nano/core_test/bootstrap.cpp b/nano/core_test/bootstrap.cpp index 34617b1d0..b78b65d5d 100644 --- a/nano/core_test/bootstrap.cpp +++ b/nano/core_test/bootstrap.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/nano/core_test/confirmation_height.cpp b/nano/core_test/confirmation_height.cpp index 352d04d4b..b35eadc71 100644 --- a/nano/core_test/confirmation_height.cpp +++ b/nano/core_test/confirmation_height.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/nano/core_test/confirmation_solicitor.cpp b/nano/core_test/confirmation_solicitor.cpp index a35ff9891..20e2ee5af 100644 --- a/nano/core_test/confirmation_solicitor.cpp +++ b/nano/core_test/confirmation_solicitor.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/nano/core_test/conflicts.cpp b/nano/core_test/conflicts.cpp index 943bb7569..323154046 100644 --- a/nano/core_test/conflicts.cpp +++ b/nano/core_test/conflicts.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/nano/core_test/election.cpp b/nano/core_test/election.cpp index 766b41c06..c46731aa7 100644 --- a/nano/core_test/election.cpp +++ b/nano/core_test/election.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/nano/core_test/election_scheduler.cpp b/nano/core_test/election_scheduler.cpp index 92afc9b2d..48ccac768 100644 --- a/nano/core_test/election_scheduler.cpp +++ b/nano/core_test/election_scheduler.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 773057e89..3ee507cb2 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index 48fd15a04..77029005c 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/core_test/optimistic_scheduler.cpp b/nano/core_test/optimistic_scheduler.cpp index 789eb66b3..1fc1deac8 100644 --- a/nano/core_test/optimistic_scheduler.cpp +++ b/nano/core_test/optimistic_scheduler.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/nano/core_test/rep_crawler.cpp b/nano/core_test/rep_crawler.cpp index 6e78d3a67..0dc098ada 100644 --- a/nano/core_test/rep_crawler.cpp +++ b/nano/core_test/rep_crawler.cpp @@ -1,6 +1,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 3bb865f9f..9860075c4 100644 --- a/nano/core_test/request_aggregator.cpp +++ b/nano/core_test/request_aggregator.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/nano/core_test/vote_processor.cpp b/nano/core_test/vote_processor.cpp index 6e271568e..2af6b1e9f 100644 --- a/nano/core_test/vote_processor.cpp +++ b/nano/core_test/vote_processor.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/nano/core_test/wallet.cpp b/nano/core_test/wallet.cpp index 358ee2d2a..ef40fe8c7 100644 --- a/nano/core_test/wallet.cpp +++ b/nano/core_test/wallet.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/core_test/wallets.cpp b/nano/core_test/wallets.cpp index 48c0e79dc..fd5c54318 100644 --- a/nano/core_test/wallets.cpp +++ b/nano/core_test/wallets.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/nano/core_test/websocket.cpp b/nano/core_test/websocket.cpp index 0d147dba4..8d90ef3eb 100644 --- a/nano/core_test/websocket.cpp +++ b/nano/core_test/websocket.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/nano/nano_node/entry.cpp b/nano/nano_node/entry.cpp index fa4534e6e..0d2898ab4 100644 --- a/nano/nano_node/entry.cpp +++ b/nano/nano_node/entry.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index a63206ca7..564b6dd38 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/node/election.cpp b/nano/node/election.cpp index 865593ab4..3b2b5be47 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 1d98e73cb..1ba7f100e 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 57e52b3d0..95916058d 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -2,8 +2,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -167,22 +169,23 @@ nano::node::node (std::shared_ptr io_ctx_a, std::filesy tcp_listener{ std::make_shared (network.port, *this, config.tcp_incoming_connections_max) }, application_path (application_path_a), port_mapping (*this), + block_processor (*this, write_database_queue), + confirmation_height_processor (ledger, write_database_queue, config.conf_height_processor_batch_min_time, logger, node_initialized_latch, flags.confirmation_height_processor_mode), + active_impl{ std::make_unique (*this, confirmation_height_processor, block_processor) }, + active{ *active_impl }, rep_crawler (config.rep_crawler, *this), 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), 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 }, generator_impl{ std::make_unique (config, *this, ledger, wallets, vote_processor, history, network, stats, logger, /* non-final */ false) }, generator{ *generator_impl }, final_generator_impl{ std::make_unique (config, *this, ledger, wallets, vote_processor, history, network, stats, logger, /* final */ true) }, final_generator{ *final_generator_impl }, - active{ *this, confirmation_height_processor, block_processor }, scheduler_impl{ std::make_unique (*this) }, scheduler{ *scheduler_impl }, aggregator (config, stats, generator, final_generator, history, ledger, wallets, active), diff --git a/nano/node/node.hpp b/nano/node/node.hpp index 6d807463c..7bace7a73 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -47,6 +46,7 @@ namespace nano { +class active_transactions; namespace rocksdb { } // Declare a namespace rocksdb inside nano so all references to the rocksdb library need to be globally scoped e.g. ::rocksdb::Slice @@ -160,23 +160,24 @@ public: std::filesystem::path application_path; nano::node_observers observers; nano::port_mapping port_mapping; + nano::block_processor block_processor; + nano::confirmation_height_processor confirmation_height_processor; + std::unique_ptr active_impl; + nano::active_transactions & active; 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; 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; nano::vote_cache vote_cache; std::unique_ptr generator_impl; nano::vote_generator & generator; std::unique_ptr final_generator_impl; nano::vote_generator & final_generator; - nano::active_transactions active; private: // Placed here to maintain initialization order std::unique_ptr scheduler_impl; diff --git a/nano/node/node_observers.hpp b/nano/node/node_observers.hpp index d1b074f2a..9a97987c1 100644 --- a/nano/node/node_observers.hpp +++ b/nano/node/node_observers.hpp @@ -2,8 +2,8 @@ #include #include -#include #include +#include namespace nano { diff --git a/nano/node/repcrawler.cpp b/nano/node/repcrawler.cpp index adf2fd2fe..42f6d210d 100644 --- a/nano/node/repcrawler.cpp +++ b/nano/node/repcrawler.cpp @@ -1,3 +1,4 @@ +#include #include #include #include diff --git a/nano/node/scheduler/hinted.cpp b/nano/node/scheduler/hinted.cpp index 33e008fa5..a6f976864 100644 --- a/nano/node/scheduler/hinted.cpp +++ b/nano/node/scheduler/hinted.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include #include diff --git a/nano/node/scheduler/manual.cpp b/nano/node/scheduler/manual.cpp index f84774bc4..3bcc83c16 100644 --- a/nano/node/scheduler/manual.cpp +++ b/nano/node/scheduler/manual.cpp @@ -1,3 +1,4 @@ +#include #include #include #include diff --git a/nano/node/scheduler/manual.hpp b/nano/node/scheduler/manual.hpp index f29f42d2b..947c2fe4d 100644 --- a/nano/node/scheduler/manual.hpp +++ b/nano/node/scheduler/manual.hpp @@ -1,7 +1,7 @@ #pragma once #include #include -#include +#include #include diff --git a/nano/node/scheduler/optimistic.cpp b/nano/node/scheduler/optimistic.cpp index 2c1425160..a11e1540e 100644 --- a/nano/node/scheduler/optimistic.cpp +++ b/nano/node/scheduler/optimistic.cpp @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/nano/node/scheduler/priority.cpp b/nano/node/scheduler/priority.cpp index b37276e06..c6eb0d996 100644 --- a/nano/node/scheduler/priority.cpp +++ b/nano/node/scheduler/priority.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/nano/node/scheduler/priority.hpp b/nano/node/scheduler/priority.hpp index d8a7b2daf..693200217 100644 --- a/nano/node/scheduler/priority.hpp +++ b/nano/node/scheduler/priority.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include @@ -16,6 +15,7 @@ namespace nano class block; class container_info_component; class node; +class stats; } namespace nano::store { diff --git a/nano/node/websocket.cpp b/nano/node/websocket.cpp index 68f870a3c..1cebbf750 100644 --- a/nano/node/websocket.cpp +++ b/nano/node/websocket.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/qt/qt.cpp b/nano/qt/qt.cpp index 744cf095a..d33db65cc 100644 --- a/nano/qt/qt.cpp +++ b/nano/qt/qt.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 5d5e4190c..32a324815 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/slow_test/node.cpp b/nano/slow_test/node.cpp index 06b8728eb..73a9ebe30 100644 --- a/nano/slow_test/node.cpp +++ b/nano/slow_test/node.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/slow_test/vote_cache.cpp b/nano/slow_test/vote_cache.cpp index 1e0b4abae..73ecba40e 100644 --- a/nano/slow_test/vote_cache.cpp +++ b/nano/slow_test/vote_cache.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/nano/test_common/system.cpp b/nano/test_common/system.cpp index 379d0ec7b..914d472e5 100644 --- a/nano/test_common/system.cpp +++ b/nano/test_common/system.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/nano/test_common/testutil.cpp b/nano/test_common/testutil.cpp index 114a7813b..c7b126f2a 100644 --- a/nano/test_common/testutil.cpp +++ b/nano/test_common/testutil.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include From d2e6f97283f1df927e801717917c0441e79236d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20W=C3=B3jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 21 Mar 2024 16:30:21 +0100 Subject: [PATCH 068/128] Return per hash vote result (#4510) * Election use `nano::vote_code` * Return map of results from `active_transactions::vote (...)` * Fix test * Use `to_stat_detail (nano::vote_code)` helper * Ignore duplicate hashes when processing votes * Compilation fix * Fix compilation --- nano/core_test/active_transactions.cpp | 32 +++++++-------- nano/core_test/election.cpp | 12 +++--- nano/core_test/ledger.cpp | 10 ++--- nano/core_test/vote_processor.cpp | 6 +-- nano/lib/stats_enums.hpp | 14 ++++--- nano/node/active_transactions.cpp | 55 ++++++++++++++------------ nano/node/active_transactions.hpp | 2 +- nano/node/election.cpp | 12 +++--- nano/node/election.hpp | 19 ++------- nano/node/vote_cache.cpp | 4 +- nano/node/vote_processor.cpp | 37 +++++++---------- nano/secure/common.cpp | 14 +++++++ nano/secure/common.hpp | 13 +++++- 13 files changed, 121 insertions(+), 109 deletions(-) diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index ed9fef2da..d9cba0580 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -583,19 +583,19 @@ TEST (active_transactions, vote_replays) // First vote is not a replay and confirms the election, second vote should be a replay since the election has confirmed but not yet removed auto vote_send1 = nano::test::make_final_vote (nano::dev::genesis_key, { send1 }); - ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote_send1)); - ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_send1)); + ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote_send1).at (send1->hash ())); + ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_send1).at (send1->hash ())); // Wait until the election is removed, at which point the vote is still a replay since it's been recently confirmed ASSERT_TIMELY_EQ (5s, node.active.size (), 1); - ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_send1)); + ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_send1).at (send1->hash ())); // Open new account auto vote_open1 = nano::test::make_final_vote (nano::dev::genesis_key, { open1 }); - ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote_open1)); - ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_open1)); + ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote_open1).at (open1->hash ())); + ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_open1).at (open1->hash ())); ASSERT_TIMELY (5s, node.active.empty ()); - ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_open1)); + ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_open1).at (open1->hash ())); ASSERT_EQ (nano::Gxrb_ratio, node.ledger.weight (key.pub)); // send 1 raw to key to key @@ -616,27 +616,27 @@ TEST (active_transactions, vote_replays) // vote2_send2 is a non final vote with little weight, vote1_send2 is the vote that confirms the election auto vote1_send2 = nano::test::make_final_vote (nano::dev::genesis_key, { send2 }); auto vote2_send2 = nano::test::make_vote (key, { send2 }, 0, 0); - ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote2_send2)); // this vote cannot confirm the election + ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote2_send2).at (send2->hash ())); // this vote cannot confirm the election ASSERT_EQ (1, node.active.size ()); - ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote2_send2)); // this vote cannot confirm the election + ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote2_send2).at (send2->hash ())); // this vote cannot confirm the election ASSERT_EQ (1, node.active.size ()); - ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote1_send2)); // this vote confirms the election + ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote1_send2).at (send2->hash ())); // this vote confirms the election // this should still return replay, either because the election is still in the AEC or because it is recently confirmed - ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote1_send2)); + ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote1_send2).at (send2->hash ())); ASSERT_TIMELY (5s, node.active.empty ()); - ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote1_send2)); - ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote2_send2)); + ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote1_send2).at (send2->hash ())); + ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote2_send2).at (send2->hash ())); // Removing blocks as recently confirmed makes every vote indeterminate { nano::lock_guard guard (node.active.mutex); node.active.recently_confirmed.clear (); } - ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote_send1)); - ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote_open1)); - ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote1_send2)); - ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote2_send2)); + ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote_send1).at (send1->hash ())); + ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote_open1).at (open1->hash ())); + ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote1_send2).at (send2->hash ())); + ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote2_send2).at (send2->hash ())); } } diff --git a/nano/core_test/election.cpp b/nano/core_test/election.cpp index c46731aa7..ac4c47a63 100644 --- a/nano/core_test/election.cpp +++ b/nano/core_test/election.cpp @@ -72,7 +72,7 @@ TEST (election, quorum_minimum_flip_success) ASSERT_TIMELY_EQ (5s, election->blocks ().size (), 2); auto vote = nano::test::make_final_vote (nano::dev::genesis_key, { send2->hash () }); - ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote)); + ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote).at (send2->hash ())); ASSERT_TIMELY (5s, election->confirmed ()); auto const winner = election->winner (); @@ -121,7 +121,7 @@ TEST (election, quorum_minimum_flip_fail) // genesis generates a final vote for send2 but it should not be enough to reach quorum due to the online_weight_minimum being so high auto vote = nano::test::make_final_vote (nano::dev::genesis_key, { send2->hash () }); - ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote)); + ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote).at (send2->hash ())); // give the election some time before asserting it is not confirmed so that in case // it would be wrongfully confirmed, have that immediately fail instead of race @@ -157,7 +157,7 @@ TEST (election, quorum_minimum_confirm_success) ASSERT_NE (nullptr, election); ASSERT_EQ (1, election->blocks ().size ()); auto vote = nano::test::make_final_vote (nano::dev::genesis_key, { send1->hash () }); - ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote)); + ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote).at (send1->hash ())); ASSERT_NE (nullptr, node1.block (send1->hash ())); ASSERT_TIMELY (5s, election->confirmed ()); } @@ -188,7 +188,7 @@ TEST (election, quorum_minimum_confirm_fail) ASSERT_EQ (1, election->blocks ().size ()); auto vote = nano::test::make_final_vote (nano::dev::genesis_key, { send1->hash () }); - ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote)); + ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote).at (send1->hash ())); // give the election a chance to confirm WAIT (1s); @@ -251,7 +251,7 @@ TEST (election, quorum_minimum_update_weight_before_quorum_checks) ASSERT_EQ (1, election->blocks ().size ()); auto vote1 = nano::test::make_final_vote (nano::dev::genesis_key, { send1->hash () }); - ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1)); + ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1).at (send1->hash ())); auto channel = node1.network.find_node_id (node2.get_node_id ()); ASSERT_NE (channel, nullptr); @@ -265,7 +265,7 @@ TEST (election, quorum_minimum_update_weight_before_quorum_checks) // Modify online_m for online_reps to more than is available, this checks that voting below updates it to current online reps. node1.online_reps.online_m = node_config.online_weight_minimum.number () + 20; } - ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2)); + ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2).at (send1->hash ())); ASSERT_TIMELY (5s, election->confirmed ()); ASSERT_NE (nullptr, node1.block (send1->hash ())); } diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 3ee507cb2..647589b62 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -932,9 +932,9 @@ TEST (votes, add_one) auto election1 = node1.active.election (send1->qualified_root ()); ASSERT_EQ (1, election1->votes ().size ()); auto vote1 = nano::test::make_vote (nano::dev::genesis_key, { send1 }, nano::vote::timestamp_min * 1, 0); - ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1)); + ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1).at (send1->hash ())); auto vote2 = nano::test::make_vote (nano::dev::genesis_key, { send1 }, nano::vote::timestamp_min * 2, 0); - ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2)); + ASSERT_EQ (nano::vote_code::ignored, node1.active.vote (vote2).at (send1->hash ())); // Ignored due to vote cooldown ASSERT_EQ (2, election1->votes ().size ()); auto votes1 (election1->votes ()); auto existing1 (votes1.find (nano::dev::genesis_key.pub)); @@ -973,7 +973,7 @@ TEST (votes, add_existing) ASSERT_TIMELY (5s, node1.active.election (send1->qualified_root ())); auto election1 = node1.active.election (send1->qualified_root ()); auto vote1 = nano::test::make_vote (nano::dev::genesis_key, { send1 }, nano::vote::timestamp_min * 1, 0); - ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1)); + ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1).at (send1->hash ())); // Block is already processed from vote ASSERT_TRUE (node1.active.publish (send1)); ASSERT_EQ (nano::vote::timestamp_min * 1, election1->last_votes[nano::dev::genesis_key.pub].timestamp); @@ -995,13 +995,13 @@ TEST (votes, add_existing) auto vote_info1 = election1->get_last_vote (nano::dev::genesis_key.pub); vote_info1.time = std::chrono::steady_clock::now () - std::chrono::seconds (20); election1->set_last_vote (nano::dev::genesis_key.pub, vote_info1); - ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2)); + ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2).at (send2->hash ())); ASSERT_EQ (nano::vote::timestamp_min * 2, election1->last_votes[nano::dev::genesis_key.pub].timestamp); // Also resend the old vote, and see if we respect the timestamp auto vote_info2 = election1->get_last_vote (nano::dev::genesis_key.pub); vote_info2.time = std::chrono::steady_clock::now () - std::chrono::seconds (20); election1->set_last_vote (nano::dev::genesis_key.pub, vote_info2); - ASSERT_EQ (nano::vote_code::replay, node1.active.vote (vote1)); + ASSERT_EQ (nano::vote_code::replay, node1.active.vote (vote1).at (send1->hash ())); ASSERT_EQ (nano::vote::timestamp_min * 2, election1->votes ()[nano::dev::genesis_key.pub].timestamp); auto votes (election1->votes ()); ASSERT_EQ (2, votes.size ()); diff --git a/nano/core_test/vote_processor.cpp b/nano/core_test/vote_processor.cpp index 2af6b1e9f..8ed2dd221 100644 --- a/nano/core_test/vote_processor.cpp +++ b/nano/core_test/vote_processor.cpp @@ -208,7 +208,7 @@ TEST (vote_processor, no_broadcast_local) ASSERT_FALSE (node.wallets.reps ().have_half_rep ()); // Genesis balance remaining after `send' is less than the half_rep threshold // Process a vote with a key that is in the local wallet. auto vote = std::make_shared (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::milliseconds_since_epoch (), nano::vote::duration_max, std::vector{ send->hash () }); - ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote)); + ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote).at (send->hash ())); // Make sure the vote was processed. auto election (node.active.election (send->qualified_root ())); ASSERT_NE (nullptr, election); @@ -256,7 +256,7 @@ TEST (vote_processor, local_broadcast_without_a_representative) node.start_election (send); // Process a vote without a representative auto vote = std::make_shared (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::milliseconds_since_epoch (), nano::vote::duration_max, std::vector{ send->hash () }); - ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote)); + ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote).at (send->hash ())); // Make sure the vote was processed. std::shared_ptr election; ASSERT_TIMELY (5s, election = node.active.election (send->qualified_root ())); @@ -309,7 +309,7 @@ TEST (vote_processor, no_broadcast_local_with_a_principal_representative) ASSERT_TRUE (node.wallets.reps ().have_half_rep ()); // Genesis balance after `send' is over both half_rep and PR threshold. // Process a vote with a key that is in the local wallet. auto vote = std::make_shared (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::milliseconds_since_epoch (), nano::vote::duration_max, std::vector{ send->hash () }); - ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote)); + ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote).at (send->hash ())); // Make sure the vote was processed. auto election (node.active.election (send->qualified_root ())); ASSERT_NE (nullptr, election); diff --git a/nano/lib/stats_enums.hpp b/nano/lib/stats_enums.hpp index b1baaa963..01bf4b62d 100644 --- a/nano/lib/stats_enums.hpp +++ b/nano/lib/stats_enums.hpp @@ -84,6 +84,7 @@ enum class detail : uint8_t none, success, unknown, + cache, // processing queue queue, @@ -167,12 +168,15 @@ enum class detail : uint8_t frontier_confirmation_failed, error_socket_close, - // vote specific - vote_valid, - vote_replay, - vote_indeterminate, - vote_invalid, + // vote result + vote, + valid, + replay, + indeterminate, + + // vote processor vote_overflow, + vote_ignored, // election specific vote_new, diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 350b4c28b..9603721af 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -438,31 +438,33 @@ nano::election_insertion_result nano::active_transactions::insert (std::shared_p } // Validate a vote and apply it to the current election if one exists -nano::vote_code nano::active_transactions::vote (std::shared_ptr const & vote_a) +std::unordered_map nano::active_transactions::vote (std::shared_ptr const & vote) { - nano::vote_code result{ nano::vote_code::indeterminate }; - // If all hashes were recently confirmed then it is a replay - unsigned recently_confirmed_counter (0); - - std::vector, nano::block_hash>> process; + std::unordered_map results; + std::unordered_map> process; std::vector inactive; // Hashes that should be added to inactive vote cache - { nano::unique_lock lock{ mutex }; - for (auto const & hash : vote_a->hashes) + for (auto const & hash : vote->hashes) { - auto existing (blocks.find (hash)); - if (existing != blocks.end ()) + // Ignore duplicate hashes (should not happen with a well-behaved voting node) + if (results.find (hash) != results.end ()) { - process.emplace_back (existing->second, hash); + continue; + } + + if (auto existing = blocks.find (hash); existing != blocks.end ()) + { + process[hash] = existing->second; } else if (!recently_confirmed.exists (hash)) { inactive.emplace_back (hash); + results[hash] = nano::vote_code::indeterminate; } else { - ++recently_confirmed_counter; + results[hash] = nano::vote_code::replay; } } } @@ -470,37 +472,38 @@ nano::vote_code nano::active_transactions::vote (std::shared_ptr con // Process inactive votes outside of the critical section for (auto & hash : inactive) { - add_vote_cache (hash, vote_a); + add_vote_cache (hash, vote); } if (!process.empty ()) { - bool replay = false; bool processed = false; - for (auto const & [election, block_hash] : process) + for (auto const & [block_hash, election] : process) { - auto const vote_result = election->vote (vote_a->account, vote_a->timestamp (), block_hash); - processed |= (vote_result == nano::election::vote_result::processed); - replay |= (vote_result == nano::election::vote_result::replay); + auto const vote_result = election->vote (vote->account, vote->timestamp (), block_hash); + results[block_hash] = vote_result; + + processed |= (vote_result == nano::vote_code::vote); } // Republish vote if it is new and the node does not host a principal representative (or close to) if (processed) { auto const reps (node.wallets.reps ()); - if (!reps.have_half_rep () && !reps.exists (vote_a->account)) + if (!reps.have_half_rep () && !reps.exists (vote->account)) { - node.network.flood_vote (vote_a, 0.5f); + node.network.flood_vote (vote, 0.5f); } } - result = replay ? nano::vote_code::replay : nano::vote_code::vote; } - else if (recently_confirmed_counter == vote_a->hashes.size ()) - { - result = nano::vote_code::replay; - } - return result; + + // All hashes should have their result set + debug_assert (std::all_of (vote->hashes.begin (), vote->hashes.end (), [&results] (auto const & hash) { + return results.find (hash) != results.end (); + })); + + return results; } bool nano::active_transactions::active (nano::qualified_root const & root_a) const diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index da475cfd6..a0eef10b1 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -152,7 +152,7 @@ public: */ nano::election_insertion_result insert (std::shared_ptr const &, nano::election_behavior = nano::election_behavior::normal); // Distinguishes replay votes, cannot be determined if the block is not in any election - nano::vote_code vote (std::shared_ptr const &); + std::unordered_map vote (std::shared_ptr const &); // Is the root of this block in the roots container bool active (nano::block const &) const; bool active (nano::qualified_root const &) const; diff --git a/nano/node/election.cpp b/nano/node/election.cpp index 3b2b5be47..f67806378 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -434,12 +434,12 @@ std::shared_ptr nano::election::find (nano::block_hash const & hash return result; } -auto nano::election::vote (nano::account const & rep, uint64_t timestamp_a, nano::block_hash const & block_hash_a, vote_source vote_source_a) -> vote_result +nano::vote_code nano::election::vote (nano::account const & rep, uint64_t timestamp_a, nano::block_hash const & block_hash_a, nano::vote_source vote_source_a) { auto weight = node.ledger.weight (rep); if (!node.network_params.network.is_dev_network () && weight <= node.minimum_principal_weight ()) { - return vote_result::ignored; + return vote_code::indeterminate; } nano::unique_lock lock{ mutex }; @@ -450,11 +450,11 @@ auto nano::election::vote (nano::account const & rep, uint64_t timestamp_a, nano auto last_vote_l (last_vote_it->second); if (last_vote_l.timestamp > timestamp_a) { - return vote_result::replay; + return vote_code::replay; } if (last_vote_l.timestamp == timestamp_a && !(last_vote_l.hash < block_hash_a)) { - return vote_result::replay; + return vote_code::replay; } auto max_vote = timestamp_a == std::numeric_limits::max () && last_vote_l.timestamp < timestamp_a; @@ -468,7 +468,7 @@ auto nano::election::vote (nano::account const & rep, uint64_t timestamp_a, nano if (!max_vote && !past_cooldown) { - return vote_result::ignored; + return vote_code::ignored; } } @@ -494,7 +494,7 @@ auto nano::election::vote (nano::account const & rep, uint64_t timestamp_a, nano confirm_if_quorum (lock); } - return vote_result::processed; + return vote_code::vote; } bool nano::election::publish (std::shared_ptr const & block_a) diff --git a/nano/node/election.hpp b/nano/node/election.hpp index 22dc8ab5c..6f8b61ca6 100644 --- a/nano/node/election.hpp +++ b/nano/node/election.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -42,24 +43,10 @@ struct election_extended_status final void operator() (nano::object_stream &) const; }; -class election final : public std::enable_shared_from_this +class election final : public std::enable_shared_from_this { nano::id_t const id{ nano::next_id () }; // Track individual objects when tracing -public: - enum class vote_source - { - live, - cache, - }; - - enum class vote_result - { - ignored, - processed, - replay, - }; - private: // Minimum time between broadcasts of the current winner of an election, as a backup to requesting confirmations std::chrono::milliseconds base_latency () const; @@ -117,7 +104,7 @@ public: // Interface * Process vote. Internally uses cooldown to throttle non-final votes * If the election reaches consensus, it will be confirmed */ - vote_result vote (nano::account const & representative, uint64_t timestamp, nano::block_hash const & block_hash, vote_source = vote_source::live); + nano::vote_code vote (nano::account const & representative, uint64_t timestamp, nano::block_hash const & block_hash, nano::vote_source = nano::vote_source::live); bool publish (std::shared_ptr const & block_a); // Confirm this block if quorum is met void confirm_if_quorum (nano::unique_lock &); diff --git a/nano/node/vote_cache.cpp b/nano/node/vote_cache.cpp index 7779b0aff..4edab3149 100644 --- a/nano/node/vote_cache.cpp +++ b/nano/node/vote_cache.cpp @@ -69,8 +69,8 @@ std::size_t nano::vote_cache::entry::fill (std::shared_ptr const std::size_t inserted = 0; for (const auto & entry : voters_m) { - auto result = election->vote (entry.representative, entry.timestamp, hash_m, nano::election::vote_source::cache); - if (result == nano::election::vote_result::processed) + auto result = election->vote (entry.representative, entry.timestamp, hash_m, nano::vote_source::cache); + if (result == nano::vote_code::vote) { inserted++; } diff --git a/nano/node/vote_processor.cpp b/nano/node/vote_processor.cpp index 337b0bddf..7b70aa0e5 100644 --- a/nano/node/vote_processor.cpp +++ b/nano/node/vote_processor.cpp @@ -163,32 +163,25 @@ void nano::vote_processor::verify_votes (decltype (votes) const & votes_a) nano::vote_code nano::vote_processor::vote_blocking (std::shared_ptr const & vote_a, std::shared_ptr const & channel_a, bool validated) { - auto result (nano::vote_code::invalid); + auto result = nano::vote_code::invalid; if (validated || !vote_a->validate ()) { - result = active.vote (vote_a); + auto vote_results = active.vote (vote_a); + + // Aggregate results for individual hashes + bool replay = false; + bool processed = false; + for (auto const & [hash, hash_result] : vote_results) + { + replay |= (hash_result == nano::vote_code::replay); + processed |= (hash_result == nano::vote_code::vote); + } + result = replay ? nano::vote_code::replay : (processed ? nano::vote_code::vote : nano::vote_code::indeterminate); + observers.vote.notify (vote_a, channel_a, result); } - std::string status; - switch (result) - { - case nano::vote_code::invalid: - status = "Invalid"; - stats.inc (nano::stat::type::vote, nano::stat::detail::vote_invalid); - break; - case nano::vote_code::replay: - status = "Replay"; - stats.inc (nano::stat::type::vote, nano::stat::detail::vote_replay); - break; - case nano::vote_code::vote: - status = "Vote"; - stats.inc (nano::stat::type::vote, nano::stat::detail::vote_valid); - break; - case nano::vote_code::indeterminate: - status = "Indeterminate"; - stats.inc (nano::stat::type::vote, nano::stat::detail::vote_indeterminate); - break; - } + + stats.inc (nano::stat::type::vote, to_stat_detail (result)); logger.trace (nano::log::type::vote_processor, nano::log::detail::vote_processed, nano::log::arg{ "vote", vote_a }, diff --git a/nano/secure/common.cpp b/nano/secure/common.cpp index 385205332..40dfbed70 100644 --- a/nano/secure/common.cpp +++ b/nano/secure/common.cpp @@ -341,6 +341,20 @@ nano::block_hash const & nano::unchecked_key::key () const return previous; } +nano::stat::detail nano::to_stat_detail (nano::vote_code code) +{ + auto value = magic_enum::enum_cast (magic_enum::enum_name (code)); + debug_assert (value); + return value.value_or (nano::stat::detail{}); +} + +nano::stat::detail nano::to_stat_detail (nano::vote_source source) +{ + auto value = magic_enum::enum_cast (magic_enum::enum_name (source)); + debug_assert (value); + return value.value_or (nano::stat::detail{}); +} + std::string_view nano::to_string (nano::block_status code) { return magic_enum::enum_name (code); diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index aab26e8be..ff6c61663 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -191,9 +191,20 @@ enum class vote_code invalid, // Vote is not signed correctly replay, // Vote does not have the highest timestamp, it's a replay vote, // Vote has the highest timestamp - indeterminate // Unknown if replay or vote + indeterminate, // Unknown if replay or vote + ignored, // Vote is valid, but got ingored (e.g. due to cooldown) }; +nano::stat::detail to_stat_detail (vote_code); + +enum class vote_source +{ + live, + cache, +}; + +nano::stat::detail to_stat_detail (vote_source); + enum class block_status { progress, // Hasn't been seen before, signed correctly From 1ebcec3ce25e30a3afa07bc778f7caaef6e77f6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20W=C3=B3jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 21 Mar 2024 17:55:22 +0100 Subject: [PATCH 069/128] Config option for disabling hinted scheduler (#4512) --- nano/core_test/toml.cpp | 3 +++ nano/node/scheduler/hinted.cpp | 7 +++++++ nano/node/scheduler/hinted.hpp | 1 + nano/node/scheduler/optimistic.cpp | 4 ++-- 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/nano/core_test/toml.cpp b/nano/core_test/toml.cpp index 9e2a5cbe8..b29519ba6 100644 --- a/nano/core_test/toml.cpp +++ b/nano/core_test/toml.cpp @@ -245,6 +245,7 @@ TEST (toml, daemon_config_deserialize_defaults) ASSERT_EQ (conf.node.optimistic_scheduler.gap_threshold, defaults.node.optimistic_scheduler.gap_threshold); ASSERT_EQ (conf.node.optimistic_scheduler.max_size, defaults.node.optimistic_scheduler.max_size); + ASSERT_EQ (conf.node.hinted_scheduler.enabled, defaults.node.hinted_scheduler.enabled); ASSERT_EQ (conf.node.hinted_scheduler.hinting_threshold_percent, defaults.node.hinted_scheduler.hinting_threshold_percent); ASSERT_EQ (conf.node.hinted_scheduler.check_interval.count (), defaults.node.hinted_scheduler.check_interval.count ()); ASSERT_EQ (conf.node.hinted_scheduler.block_cooldown.count (), defaults.node.hinted_scheduler.block_cooldown.count ()); @@ -516,6 +517,7 @@ TEST (toml, daemon_config_deserialize_no_defaults) max_size = 999 [node.hinted_scheduler] + enabled = false hinting_threshold = 99 check_interval = 999 block_cooldown = 999 @@ -667,6 +669,7 @@ TEST (toml, daemon_config_deserialize_no_defaults) ASSERT_NE (conf.node.optimistic_scheduler.gap_threshold, defaults.node.optimistic_scheduler.gap_threshold); ASSERT_NE (conf.node.optimistic_scheduler.max_size, defaults.node.optimistic_scheduler.max_size); + ASSERT_NE (conf.node.hinted_scheduler.enabled, defaults.node.hinted_scheduler.enabled); ASSERT_NE (conf.node.hinted_scheduler.hinting_threshold_percent, defaults.node.hinted_scheduler.hinting_threshold_percent); ASSERT_NE (conf.node.hinted_scheduler.check_interval.count (), defaults.node.hinted_scheduler.check_interval.count ()); ASSERT_NE (conf.node.hinted_scheduler.block_cooldown.count (), defaults.node.hinted_scheduler.block_cooldown.count ()); diff --git a/nano/node/scheduler/hinted.cpp b/nano/node/scheduler/hinted.cpp index a6f976864..0d971aa1e 100644 --- a/nano/node/scheduler/hinted.cpp +++ b/nano/node/scheduler/hinted.cpp @@ -30,6 +30,11 @@ void nano::scheduler::hinted::start () { debug_assert (!thread.joinable ()); + if (!config.enabled) + { + return; + } + thread = std::thread{ [this] () { nano::thread_role::set (nano::thread_role::name::scheduler_hinted); run (); @@ -254,6 +259,7 @@ nano::scheduler::hinted_config::hinted_config (nano::network_constants const & n nano::error nano::scheduler::hinted_config::serialize (nano::tomlconfig & toml) const { + toml.put ("enable", enabled, "Enable or disable hinted elections\ntype:bool"); toml.put ("hinting_threshold", hinting_threshold_percent, "Percentage of online weight needed to start a hinted election. \ntype:uint32,[0,100]"); toml.put ("check_interval", check_interval.count (), "Interval between scans of the vote cache for possible hinted elections. \ntype:milliseconds"); toml.put ("block_cooldown", block_cooldown.count (), "Cooldown period for blocks that failed to start an election. \ntype:milliseconds"); @@ -264,6 +270,7 @@ nano::error nano::scheduler::hinted_config::serialize (nano::tomlconfig & toml) nano::error nano::scheduler::hinted_config::deserialize (nano::tomlconfig & toml) { + toml.get ("enabled", enabled); toml.get ("hinting_threshold", hinting_threshold_percent); auto check_interval_l = check_interval.count (); diff --git a/nano/node/scheduler/hinted.hpp b/nano/node/scheduler/hinted.hpp index 4bb30cd9a..9e8fd425a 100644 --- a/nano/node/scheduler/hinted.hpp +++ b/nano/node/scheduler/hinted.hpp @@ -36,6 +36,7 @@ public: nano::error serialize (nano::tomlconfig & toml) const; public: + bool enabled{ true }; std::chrono::milliseconds check_interval{ 1000 }; std::chrono::milliseconds block_cooldown{ 10000 }; unsigned hinting_threshold_percent{ 10 }; diff --git a/nano/node/scheduler/optimistic.cpp b/nano/node/scheduler/optimistic.cpp index a11e1540e..cda286665 100644 --- a/nano/node/scheduler/optimistic.cpp +++ b/nano/node/scheduler/optimistic.cpp @@ -25,13 +25,13 @@ nano::scheduler::optimistic::~optimistic () void nano::scheduler::optimistic::start () { + debug_assert (!thread.joinable ()); + if (!config.enabled) { return; } - debug_assert (!thread.joinable ()); - thread = std::thread{ [this] () { nano::thread_role::set (nano::thread_role::name::scheduler_optimistic); run (); From 3eb50a327684f976f057f499c4c6a4af6a91eb2e Mon Sep 17 00:00:00 2001 From: clemahieu Date: Thu, 21 Mar 2024 17:22:12 +0000 Subject: [PATCH 070/128] Rename block::hash(blake2b_state&) overload to block::generate_hash(blake2b_state&) (#4514) Fix annoyance where a debugger may not perform overload resolution correctly. With lldb asking the debugger to calculate a block hash will fail, likely because it doesn't follow overload resolution through inheritance. --- nano/lib/blocks.cpp | 12 ++++++------ nano/lib/blocks.hpp | 27 ++++++++++++++++----------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/nano/lib/blocks.cpp b/nano/lib/blocks.cpp index 9d38cd404..653ecd292 100644 --- a/nano/lib/blocks.cpp +++ b/nano/lib/blocks.cpp @@ -101,7 +101,7 @@ nano::block_hash nano::block::generate_hash () const blake2b_state hash_l; auto status (blake2b_init (&hash_l, sizeof (result.bytes))); debug_assert (status == 0); - hash (hash_l); + generate_hash (hash_l); status = blake2b_final (&hash_l, result.bytes.data (), sizeof (result.bytes)); debug_assert (status == 0); return result; @@ -359,7 +359,7 @@ void nano::send_block::visit (nano::mutable_block_visitor & visitor_a) visitor_a.send_block (*this); } -void nano::send_block::hash (blake2b_state & hash_a) const +void nano::send_block::generate_hash (blake2b_state & hash_a) const { hashables.hash (hash_a); } @@ -754,7 +754,7 @@ nano::open_block::open_block (bool & error_a, boost::property_tree::ptree const } } -void nano::open_block::hash (blake2b_state & hash_a) const +void nano::open_block::generate_hash (blake2b_state & hash_a) const { hashables.hash (hash_a); } @@ -1025,7 +1025,7 @@ nano::change_block::change_block (bool & error_a, boost::property_tree::ptree co } } -void nano::change_block::hash (blake2b_state & hash_a) const +void nano::change_block::generate_hash (blake2b_state & hash_a) const { hashables.hash (hash_a); } @@ -1322,7 +1322,7 @@ nano::state_block::state_block (bool & error_a, boost::property_tree::ptree cons } } -void nano::state_block::hash (blake2b_state & hash_a) const +void nano::state_block::generate_hash (blake2b_state & hash_a) const { nano::uint256_union preamble (static_cast (nano::block_type::state)); blake2b_update (&hash_a, preamble.bytes.data (), preamble.bytes.size ()); @@ -1788,7 +1788,7 @@ nano::receive_block::receive_block (bool & error_a, boost::property_tree::ptree } } -void nano::receive_block::hash (blake2b_state & hash_a) const +void nano::receive_block::generate_hash (blake2b_state & hash_a) const { hashables.hash (hash_a); } diff --git a/nano/lib/blocks.hpp b/nano/lib/blocks.hpp index 99abb9743..1ddb8302d 100644 --- a/nano/lib/blocks.hpp +++ b/nano/lib/blocks.hpp @@ -30,7 +30,6 @@ public: void sideband_set (nano::block_sideband const &); bool has_sideband () const; std::string to_json () const; - virtual void hash (blake2b_state &) const = 0; virtual uint64_t block_work () const = 0; virtual void block_work_set (uint64_t) = 0; // Previous block or account number for open blocks @@ -84,6 +83,7 @@ public: // Direct access to the block fields or nullopt if the block type does n virtual std::optional source_field () const; protected: + virtual void generate_hash (blake2b_state &) const = 0; mutable nano::block_hash cached_hash{ 0 }; /** * Contextual details about a block, some fields may or may not be set depending on block type. @@ -121,8 +121,6 @@ public: send_block (bool &, nano::stream &); send_block (bool &, boost::property_tree::ptree const &); virtual ~send_block () = default; - using nano::block::hash; - void hash (blake2b_state &) const override; uint64_t block_work () const override; void block_work_set (uint64_t) override; nano::root const & root () const override; @@ -151,6 +149,9 @@ public: // Send block fields public: // Logging void operator() (nano::object_stream &) const override; + +protected: + void generate_hash (blake2b_state &) const override; }; class receive_hashables @@ -174,8 +175,6 @@ public: receive_block (bool &, nano::stream &); receive_block (bool &, boost::property_tree::ptree const &); virtual ~receive_block () = default; - using nano::block::hash; - void hash (blake2b_state &) const override; uint64_t block_work () const override; void block_work_set (uint64_t) override; nano::root const & root () const override; @@ -203,6 +202,9 @@ public: // Receive block fields public: // Logging void operator() (nano::object_stream &) const override; + +protected: + void generate_hash (blake2b_state &) const override; }; class open_hashables @@ -228,8 +230,6 @@ public: open_block (bool &, nano::stream &); open_block (bool &, boost::property_tree::ptree const &); virtual ~open_block () = default; - using nano::block::hash; - void hash (blake2b_state &) const override; uint64_t block_work () const override; void block_work_set (uint64_t) override; nano::root const & root () const override; @@ -259,6 +259,9 @@ public: // Open block fields public: // Logging void operator() (nano::object_stream &) const override; + +protected: + void generate_hash (blake2b_state &) const override; }; class change_hashables @@ -282,8 +285,6 @@ public: change_block (bool &, nano::stream &); change_block (bool &, boost::property_tree::ptree const &); virtual ~change_block () = default; - using nano::block::hash; - void hash (blake2b_state &) const override; uint64_t block_work () const override; void block_work_set (uint64_t) override; nano::root const & root () const override; @@ -311,6 +312,9 @@ public: // Change block fields public: // Logging void operator() (nano::object_stream &) const override; + +protected: + void generate_hash (blake2b_state &) const override; }; class state_hashables @@ -347,8 +351,6 @@ public: state_block (bool &, nano::stream &); state_block (bool &, boost::property_tree::ptree const &); virtual ~state_block () = default; - using nano::block::hash; - void hash (blake2b_state &) const override; uint64_t block_work () const override; void block_work_set (uint64_t) override; nano::root const & root () const override; @@ -379,6 +381,9 @@ public: // State block fields public: // Logging void operator() (nano::object_stream &) const override; + +protected: + void generate_hash (blake2b_state &) const override; }; class block_visitor From f7432195b92aa77e4ed54a800b17ccd8081ad3c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 21 Mar 2024 21:15:44 +0100 Subject: [PATCH 071/128] Start/stop guards --- nano/test_common/testutil.hpp | 64 ++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/nano/test_common/testutil.hpp b/nano/test_common/testutil.hpp index 60821e7dd..a2706dc3f 100644 --- a/nano/test_common/testutil.hpp +++ b/nano/test_common/testutil.hpp @@ -115,6 +115,46 @@ ASSERT_FALSE (condition); \ } +namespace nano::test +{ +template +class start_stop_guard +{ +public: + explicit start_stop_guard (Ts &... refs_a) : + refs{ std::forward (refs_a)... } + { + std::apply ([] (Ts &... refs) { (refs.start (), ...); }, refs); + } + + ~start_stop_guard () + { + std::apply ([] (Ts &... refs) { (refs.stop (), ...); }, refs); + } + +private: + std::tuple refs; +}; + +template +class stop_guard +{ +public: + explicit stop_guard (Ts &... refs_a) : + refs{ std::forward (refs_a)... } + { + } + + ~stop_guard () + { + std::apply ([] (Ts &... refs) { (refs.stop (), ...); }, refs); + } + +private: + std::tuple refs; +}; +} + /* Convenience globals for gtest projects */ namespace nano { @@ -233,28 +273,6 @@ namespace test std::atomic required_count; }; - /** - * A helper that calls `start` from constructor and `stop` from destructor - */ - template - class start_stop_guard - { - public: - explicit start_stop_guard (T & ref_a) : - ref{ ref_a } - { - ref.start (); - } - - ~start_stop_guard () - { - ref.stop (); - } - - private: - T & ref; - }; - void wait_peer_connections (nano::test::system &); /** @@ -408,4 +426,4 @@ namespace test */ void print_all_blocks (nano::node & node); } -} +} \ No newline at end of file From 970b048052c2cc0470bace686e881aa6b9ccf514 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Tue, 19 Mar 2024 11:00:11 +0100 Subject: [PATCH 072/128] Reorganize `tcp_listener` --- nano/core_test/network.cpp | 15 ++------- nano/node/node.cpp | 2 +- nano/node/transport/tcp_server.cpp | 41 ++++++++++++++---------- nano/node/transport/tcp_server.hpp | 50 ++++++++++++++++++------------ 4 files changed, 60 insertions(+), 48 deletions(-) diff --git a/nano/core_test/network.cpp b/nano/core_test/network.cpp index 82c014c31..086491d0a 100644 --- a/nano/core_test/network.cpp +++ b/nano/core_test/network.cpp @@ -698,10 +698,7 @@ TEST (tcp_listener, DISABLED_tcp_listener_timeout_empty) system.deadline_set (std::chrono::seconds (6)); while (!disconnected) { - { - nano::lock_guard guard (node0->tcp_listener->mutex); - disconnected = node0->tcp_listener->connections.empty (); - } + disconnected = node0->tcp_listener->connection_count () == 0; ASSERT_NO_ERROR (system.poll ()); } } @@ -723,18 +720,12 @@ TEST (tcp_listener, tcp_listener_timeout_node_id_handshake) }); }); ASSERT_TIMELY (5s, node0->stats.count (nano::stat::type::tcp_server, nano::stat::detail::node_id_handshake) != 0); - { - nano::lock_guard guard (node0->tcp_listener->mutex); - ASSERT_EQ (node0->tcp_listener->connections.size (), 1); - } + ASSERT_EQ (node0->tcp_listener->connection_count (), 1); bool disconnected (false); system.deadline_set (std::chrono::seconds (20)); while (!disconnected) { - { - nano::lock_guard guard (node0->tcp_listener->mutex); - disconnected = node0->tcp_listener->connections.empty (); - } + disconnected = node0->tcp_listener->connection_count () == 0; ASSERT_NO_ERROR (system.poll ()); } } diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 95916058d..da4542476 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -535,7 +535,7 @@ std::unique_ptr nano::collect_container_info (no composite->add_component (collect_container_info (node.ledger, "ledger")); composite->add_component (collect_container_info (node.active, "active")); composite->add_component (collect_container_info (node.bootstrap_initiator, "bootstrap_initiator")); - composite->add_component (collect_container_info (*node.tcp_listener, "tcp_listener")); + composite->add_component (node.tcp_listener->collect_container_info ("tcp_listener")); composite->add_component (collect_container_info (node.network, "network")); composite->add_component (node.telemetry.collect_container_info ("telemetry")); composite->add_component (collect_container_info (node.workers, "workers")); diff --git a/nano/node/transport/tcp_server.cpp b/nano/node/transport/tcp_server.cpp index a10d85dd5..b6930d528 100644 --- a/nano/node/transport/tcp_server.cpp +++ b/nano/node/transport/tcp_server.cpp @@ -42,10 +42,15 @@ nano::transport::tcp_listener::tcp_listener (uint16_t port_a, nano::node & node_ { } +nano::transport::tcp_listener::~tcp_listener () +{ + debug_assert (stopped); +} + void nano::transport::tcp_listener::start (std::function const &, boost::system::error_code const &)> callback_a) { nano::lock_guard lock{ mutex }; - on = true; + acceptor.open (local.protocol ()); acceptor.set_option (boost::asio::ip::tcp::acceptor::reuse_address (true)); boost::system::error_code ec; @@ -68,12 +73,14 @@ void nano::transport::tcp_listener::stop () decltype (connections) connections_l; { nano::lock_guard lock{ mutex }; - on = false; + stopped = true; connections_l.swap (connections); } + nano::lock_guard lock{ mutex }; - boost::asio::dispatch (strand, boost::asio::bind_executor (strand, [this_l = shared_from_this ()] () { + boost::asio::dispatch (strand, [this_l = shared_from_this ()] () { this_l->acceptor.close (); + for (auto & address_connection_pair : this_l->connections_per_address) { if (auto connection_l = address_connection_pair.second.lock ()) @@ -82,15 +89,25 @@ void nano::transport::tcp_listener::stop () } } this_l->connections_per_address.clear (); - })); + }); } std::size_t nano::transport::tcp_listener::connection_count () { nano::lock_guard lock{ mutex }; + cleanup (); return connections.size (); } +void nano::transport::tcp_listener::cleanup () +{ + debug_assert (!mutex.try_lock ()); + + erase_if (connections, [] (auto const & connection) { + return connection.second.expired (); + }); +} + bool nano::transport::tcp_listener::limit_reached_for_incoming_subnetwork_connections (std::shared_ptr const & new_connection) { debug_assert (strand.running_in_this_thread ()); @@ -253,10 +270,10 @@ void nano::transport::tcp_listener::accept_action (boost::system::error_code con } } -boost::asio::ip::tcp::endpoint nano::transport::tcp_listener::endpoint () +boost::asio::ip::tcp::endpoint nano::transport::tcp_listener::endpoint () const { nano::lock_guard lock{ mutex }; - if (on) + if (!stopped) { return boost::asio::ip::tcp::endpoint (boost::asio::ip::address_v6::loopback (), acceptor.local_endpoint ().port ()); } @@ -266,11 +283,10 @@ boost::asio::ip::tcp::endpoint nano::transport::tcp_listener::endpoint () } } -std::unique_ptr nano::transport::collect_container_info (nano::transport::tcp_listener & bootstrap_listener, std::string const & name) +std::unique_ptr nano::transport::tcp_listener::collect_container_info (std::string const & name) { - auto sizeof_element = sizeof (decltype (bootstrap_listener.connections)::value_type); auto composite = std::make_unique (name); - composite->add_component (std::make_unique (container_info{ "connections", bootstrap_listener.connection_count (), sizeof_element })); + composite->add_component (std::make_unique (container_info{ "connections", connection_count (), sizeof (decltype (connections)::value_type) })); return composite; } @@ -321,9 +337,6 @@ nano::transport::tcp_server::~tcp_server () } stop (); - - nano::lock_guard lock{ node->tcp_listener->mutex }; - node->tcp_listener->connections.erase (this); } void nano::transport::tcp_server::start () @@ -840,10 +853,6 @@ void nano::transport::tcp_server::timeout () { node->logger.debug (nano::log::type::tcp_server, "Closing TCP server due to timeout ({})", fmt::streamed (remote_endpoint)); - { - nano::lock_guard lock{ node->tcp_listener->mutex }; - node->tcp_listener->connections.erase (this); - } socket->close (); } } diff --git a/nano/node/transport/tcp_server.hpp b/nano/node/transport/tcp_server.hpp index e8a220bc1..8743772c8 100644 --- a/nano/node/transport/tcp_server.hpp +++ b/nano/node/transport/tcp_server.hpp @@ -19,38 +19,50 @@ class tcp_server; /** * Server side portion of bootstrap sessions. Listens for new socket connections and spawns tcp_server objects when connected. */ -class tcp_listener final : public std::enable_shared_from_this +class tcp_listener final : public std::enable_shared_from_this { public: - tcp_listener (uint16_t, nano::node &, std::size_t); - void start (std::function const &, boost::system::error_code const &)> callback_a); - void stop (); - void accept_action (boost::system::error_code const &, std::shared_ptr const &); - std::size_t connection_count (); + tcp_listener (uint16_t port, nano::node &, std::size_t max_inbound_connections); + ~tcp_listener (); - nano::mutex mutex; - std::unordered_map> connections; - nano::tcp_endpoint endpoint (); + void start (std::function const &, boost::system::error_code const &)> callback); + void stop (); + + void accept_action (boost::system::error_code const &, std::shared_ptr const &); + + std::size_t connection_count (); + nano::tcp_endpoint endpoint () const; + + std::unique_ptr collect_container_info (std::string const & name); + +private: // Dependencies nano::node & node; - bool on{ false }; - std::atomic bootstrap_count{ 0 }; - std::atomic realtime_count{ 0 }; private: - boost::asio::strand strand; - nano::transport::address_socket_mmap connections_per_address; - boost::asio::ip::tcp::acceptor acceptor; - boost::asio::ip::tcp::endpoint local; - std::size_t max_inbound_connections; void on_connection (std::function const &, boost::system::error_code const &)> callback_a); void evict_dead_connections (); void on_connection_requeue_delayed (std::function const & new_connection, boost::system::error_code const &)>); /** Checks whether the maximum number of connections per IP was reached. If so, it returns true. */ bool limit_reached_for_incoming_ip_connections (std::shared_ptr const & new_connection); bool limit_reached_for_incoming_subnetwork_connections (std::shared_ptr const & new_connection); -}; + void cleanup (); -std::unique_ptr collect_container_info (tcp_listener & bootstrap_listener, std::string const & name); +public: + std::atomic bootstrap_count{ 0 }; + std::atomic realtime_count{ 0 }; + +private: + std::unordered_map> connections; + std::multimap> connections_per_address; + + boost::asio::strand strand; + boost::asio::ip::tcp::acceptor acceptor; + boost::asio::ip::tcp::endpoint local; + std::size_t const max_inbound_connections; + + std::atomic stopped; + mutable nano::mutex mutex; +}; class tcp_server final : public std::enable_shared_from_this { From a47da679a9de942ae77cfdac61c12a1c83cf313d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 21 Mar 2024 21:20:10 +0100 Subject: [PATCH 073/128] Use stop guards --- nano/core_test/socket.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/nano/core_test/socket.cpp b/nano/core_test/socket.cpp index 11a0862b1..711d20415 100644 --- a/nano/core_test/socket.cpp +++ b/nano/core_test/socket.cpp @@ -29,6 +29,7 @@ TEST (socket, max_connections) // start a server socket that allows max 2 live connections auto listener = std::make_shared (server_port, *node, 2); + nano::test::stop_guard stop_guard{ *listener }; listener->start ([&server_sockets] (std::shared_ptr const & new_connection, boost::system::error_code const & ec_a) { server_sockets.push_back (new_connection); return true; @@ -123,6 +124,7 @@ TEST (socket, max_connections_per_ip) std::vector> server_sockets; auto listener = std::make_shared (server_port, *node, max_global_connections); + nano::test::stop_guard stop_guard{ *listener }; listener->start ([&server_sockets] (std::shared_ptr const & new_connection, boost::system::error_code const & ec_a) { server_sockets.push_back (new_connection); return true; @@ -243,6 +245,7 @@ TEST (socket, max_connections_per_subnetwork) std::vector> server_sockets; auto listener = std::make_shared (server_port, *node, max_global_connections); + nano::test::stop_guard stop_guard{ *listener }; listener->start ([&server_sockets] (std::shared_ptr const & new_connection, boost::system::error_code const & ec_a) { server_sockets.push_back (new_connection); return true; @@ -303,6 +306,7 @@ TEST (socket, disabled_max_peers_per_ip) std::vector> server_sockets; auto server_socket = std::make_shared (server_port, *node, max_global_connections); + nano::test::stop_guard stop_guard{ *server_socket }; server_socket->start ([&server_sockets] (std::shared_ptr const & new_connection, boost::system::error_code const & ec_a) { server_sockets.push_back (new_connection); return true; @@ -363,6 +367,7 @@ TEST (socket, disconnection_of_silent_connections) // start a server listening socket auto listener = std::make_shared (server_port, *node, 1); + nano::test::stop_guard stop_guard{ *listener }; listener->start ([&server_data_socket] (std::shared_ptr const & new_connection, boost::system::error_code const & ec_a) { server_data_socket = new_connection; return true; @@ -414,6 +419,7 @@ TEST (socket, drop_policy) auto server_port (system.get_available_port ()); auto listener = std::make_shared (server_port, *node, 1); + nano::test::stop_guard stop_guard{ *listener }; listener->start ([&connections] (std::shared_ptr const & new_connection, boost::system::error_code const & ec_a) { connections.push_back (new_connection); return true; @@ -502,6 +508,7 @@ TEST (socket, concurrent_writes) std::vector> connections; auto listener = std::make_shared (server_port, *node, max_connections); + nano::test::stop_guard stop_guard{ *listener }; listener->start ([&connections, &reader] (std::shared_ptr const & new_connection, boost::system::error_code const & ec_a) { if (ec_a) { From 6768e7dc6e9a30172309d4d46d555a14c5d217ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 21 Mar 2024 21:34:57 +0100 Subject: [PATCH 074/128] Move `tcp_listener` to a dedicated file and reduce header coupling --- nano/core_test/network.cpp | 1 + nano/core_test/node.cpp | 1 + nano/core_test/socket.cpp | 1 + nano/node/CMakeLists.txt | 2 + nano/node/node.cpp | 1 + nano/node/node.hpp | 13 +- nano/node/transport/tcp_listener.cpp | 287 +++++++++++++++++++++++++++ nano/node/transport/tcp_listener.hpp | 60 ++++++ nano/node/transport/tcp_server.cpp | 283 +------------------------- nano/node/transport/tcp_server.hpp | 48 ----- nano/test_common/system.cpp | 1 + 11 files changed, 365 insertions(+), 333 deletions(-) create mode 100644 nano/node/transport/tcp_listener.cpp create mode 100644 nano/node/transport/tcp_listener.hpp diff --git a/nano/core_test/network.cpp b/nano/core_test/network.cpp index 086491d0a..d5a97da73 100644 --- a/nano/core_test/network.cpp +++ b/nano/core_test/network.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index 77029005c..91924b5bf 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/core_test/socket.cpp b/nano/core_test/socket.cpp index 711d20415..bbbf9d88a 100644 --- a/nano/core_test/socket.cpp +++ b/nano/core_test/socket.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include diff --git a/nano/node/CMakeLists.txt b/nano/node/CMakeLists.txt index 3e3dd1dd3..0b8ad15e7 100644 --- a/nano/node/CMakeLists.txt +++ b/nano/node/CMakeLists.txt @@ -158,6 +158,8 @@ add_library( transport/socket.cpp transport/tcp.hpp transport/tcp.cpp + transport/tcp_listener.hpp + transport/tcp_listener.cpp transport/tcp_server.hpp transport/tcp_server.cpp transport/transport.hpp diff --git a/nano/node/node.cpp b/nano/node/node.cpp index da4542476..589112e7b 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/node/node.hpp b/nano/node/node.hpp index 7bace7a73..c59a09ba1 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -47,9 +47,6 @@ namespace nano { class active_transactions; -namespace rocksdb -{ -} // Declare a namespace rocksdb inside nano so all references to the rocksdb library need to be globally scoped e.g. ::rocksdb::Slice class node; class work_pool; @@ -57,7 +54,17 @@ namespace scheduler { class component; } +namespace transport +{ + class tcp_listener; +} +namespace rocksdb +{ +} // Declare a namespace rocksdb inside nano so all references to the rocksdb library need to be globally scoped e.g. ::rocksdb::Slice +} +namespace nano +{ // Configs backlog_population::config backlog_population_config (node_config const &); outbound_bandwidth_limiter::config outbound_bandwidth_limiter_config (node_config const &); diff --git a/nano/node/transport/tcp_listener.cpp b/nano/node/transport/tcp_listener.cpp new file mode 100644 index 000000000..07f3fc8d6 --- /dev/null +++ b/nano/node/transport/tcp_listener.cpp @@ -0,0 +1,287 @@ +#include +#include +#include +#include +#include + +#include + +namespace +{ +bool is_temporary_error (boost::system::error_code const & ec_a) +{ + switch (ec_a.value ()) + { +#if EAGAIN != EWOULDBLOCK + case EAGAIN: +#endif + + case EWOULDBLOCK: + case EINTR: + return true; + default: + return false; + } +} +} + +/* + * tcp_listener + */ + +nano::transport::tcp_listener::tcp_listener (uint16_t port_a, nano::node & node_a, std::size_t max_inbound_connections) : + node (node_a), + strand{ node_a.io_ctx.get_executor () }, + acceptor{ node_a.io_ctx }, + local{ boost::asio::ip::tcp::endpoint{ boost::asio::ip::address_v6::any (), port_a } }, + max_inbound_connections{ max_inbound_connections } +{ +} + +nano::transport::tcp_listener::~tcp_listener () +{ + debug_assert (stopped); +} + +void nano::transport::tcp_listener::start (std::function const &, boost::system::error_code const &)> callback_a) +{ + nano::lock_guard lock{ mutex }; + + acceptor.open (local.protocol ()); + acceptor.set_option (boost::asio::ip::tcp::acceptor::reuse_address (true)); + boost::system::error_code ec; + acceptor.bind (local, ec); + if (!ec) + { + acceptor.listen (boost::asio::socket_base::max_listen_connections, ec); + } + if (ec) + { + node.logger.critical (nano::log::type::tcp_listener, "Error while binding for incoming TCP: {} (port: {})", ec.message (), acceptor.local_endpoint ().port ()); + throw std::runtime_error (ec.message ()); + } + + on_connection (callback_a); +} + +void nano::transport::tcp_listener::stop () +{ + decltype (connections) connections_l; + { + nano::lock_guard lock{ mutex }; + stopped = true; + connections_l.swap (connections); + } + + nano::lock_guard lock{ mutex }; + boost::asio::dispatch (strand, [this_l = shared_from_this ()] () { + this_l->acceptor.close (); + + for (auto & address_connection_pair : this_l->connections_per_address) + { + if (auto connection_l = address_connection_pair.second.lock ()) + { + connection_l->close (); + } + } + this_l->connections_per_address.clear (); + }); +} + +std::size_t nano::transport::tcp_listener::connection_count () +{ + nano::lock_guard lock{ mutex }; + cleanup (); + return connections.size (); +} + +void nano::transport::tcp_listener::cleanup () +{ + debug_assert (!mutex.try_lock ()); + + erase_if (connections, [] (auto const & connection) { + return connection.second.expired (); + }); +} + +bool nano::transport::tcp_listener::limit_reached_for_incoming_subnetwork_connections (std::shared_ptr const & new_connection) +{ + debug_assert (strand.running_in_this_thread ()); + if (node.flags.disable_max_peers_per_subnetwork || nano::transport::is_ipv4_or_v4_mapped_address (new_connection->remote.address ())) + { + // If the limit is disabled, then it is unreachable. + // If the address is IPv4 we don't check for a network limit, since its address space isn't big as IPv6 /64. + return false; + } + auto const counted_connections = socket_functions::count_subnetwork_connections ( + connections_per_address, + new_connection->remote.address ().to_v6 (), + node.network_params.network.ipv6_subnetwork_prefix_for_limiting); + return counted_connections >= node.network_params.network.max_peers_per_subnetwork; +} + +bool nano::transport::tcp_listener::limit_reached_for_incoming_ip_connections (std::shared_ptr const & new_connection) +{ + debug_assert (strand.running_in_this_thread ()); + if (node.flags.disable_max_peers_per_ip) + { + // If the limit is disabled, then it is unreachable. + return false; + } + auto const address_connections_range = connections_per_address.equal_range (new_connection->remote.address ()); + auto const counted_connections = static_cast (std::abs (std::distance (address_connections_range.first, address_connections_range.second))); + return counted_connections >= node.network_params.network.max_peers_per_ip; +} + +void nano::transport::tcp_listener::on_connection (std::function const &, boost::system::error_code const &)> callback_a) +{ + boost::asio::post (strand, boost::asio::bind_executor (strand, [this_l = shared_from_this (), callback = std::move (callback_a)] () mutable { + if (!this_l->acceptor.is_open ()) + { + this_l->node.logger.error (nano::log::type::tcp_listener, "Acceptor is not open"); + return; + } + + // Prepare new connection + auto new_connection = std::make_shared (this_l->node, socket::endpoint_type_t::server); + this_l->acceptor.async_accept (new_connection->tcp_socket, new_connection->remote, + boost::asio::bind_executor (this_l->strand, + [this_l, new_connection, cbk = std::move (callback)] (boost::system::error_code const & ec_a) mutable { + this_l->evict_dead_connections (); + + if (this_l->connections_per_address.size () >= this_l->max_inbound_connections) + { + this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_accept_failure, nano::stat::dir::in); + this_l->node.logger.debug (nano::log::type::tcp_listener, "Max connections reached ({}), unable to open new connection", this_l->connections_per_address.size ()); + + this_l->on_connection_requeue_delayed (std::move (cbk)); + return; + } + + if (this_l->limit_reached_for_incoming_ip_connections (new_connection)) + { + this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_max_per_ip, nano::stat::dir::in); + this_l->node.logger.debug (nano::log::type::tcp_listener, "Max connections per IP reached ({}), unable to open new connection", new_connection->remote_endpoint ().address ().to_string ()); + + this_l->on_connection_requeue_delayed (std::move (cbk)); + return; + } + + if (this_l->limit_reached_for_incoming_subnetwork_connections (new_connection)) + { + auto const remote_ip_address = new_connection->remote_endpoint ().address (); + debug_assert (remote_ip_address.is_v6 ()); + auto const remote_subnet = socket_functions::get_ipv6_subnet_address (remote_ip_address.to_v6 (), this_l->node.network_params.network.max_peers_per_subnetwork); + + this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_max_per_subnetwork, nano::stat::dir::in); + this_l->node.logger.debug (nano::log::type::tcp_listener, "Max connections per subnetwork reached (subnetwork: {}, ip: {}), unable to open new connection", + remote_subnet.canonical ().to_string (), + remote_ip_address.to_string ()); + + this_l->on_connection_requeue_delayed (std::move (cbk)); + return; + } + + if (!ec_a) + { + { + // Best effort attempt to get endpoint addresses + boost::system::error_code ec; + new_connection->local = new_connection->tcp_socket.local_endpoint (ec); + } + + // Make sure the new connection doesn't idle. Note that in most cases, the callback is going to start + // an IO operation immediately, which will start a timer. + new_connection->start (); + new_connection->set_timeout (this_l->node.network_params.network.idle_timeout); + this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_accept_success, nano::stat::dir::in); + this_l->connections_per_address.emplace (new_connection->remote.address (), new_connection); + this_l->node.observers.socket_accepted.notify (*new_connection); + if (cbk (new_connection, ec_a)) + { + this_l->on_connection (std::move (cbk)); + return; + } + this_l->node.logger.warn (nano::log::type::tcp_listener, "Stopping to accept new connections"); + return; + } + + // accept error + this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_accept_failure, nano::stat::dir::in); + this_l->node.logger.error (nano::log::type::tcp_listener, "Unable to accept connection: {} ({})", ec_a.message (), new_connection->remote_endpoint ().address ().to_string ()); + + if (is_temporary_error (ec_a)) + { + // if it is a temporary error, just retry it + this_l->on_connection_requeue_delayed (std::move (cbk)); + return; + } + + // if it is not a temporary error, check how the listener wants to handle this error + if (cbk (new_connection, ec_a)) + { + this_l->on_connection_requeue_delayed (std::move (cbk)); + return; + } + + // No requeue if we reach here, no incoming socket connections will be handled + this_l->node.logger.warn (nano::log::type::tcp_listener, "Stopping to accept new connections"); + })); + })); +} + +// If we are unable to accept a socket, for any reason, we wait just a little (1ms) before rescheduling the next connection accept. +// The intention is to throttle back the connection requests and break up any busy loops that could possibly form and +// give the rest of the system a chance to recover. +void nano::transport::tcp_listener::on_connection_requeue_delayed (std::function const &, boost::system::error_code const &)> callback_a) +{ + node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::milliseconds (1), [this_l = shared_from_this (), callback = std::move (callback_a)] () mutable { + this_l->on_connection (std::move (callback)); + }); +} + +// This must be called from a strand +void nano::transport::tcp_listener::evict_dead_connections () +{ + debug_assert (strand.running_in_this_thread ()); + + erase_if (connections_per_address, [] (auto const & entry) { + return entry.second.expired (); + }); +} + +void nano::transport::tcp_listener::accept_action (boost::system::error_code const & ec, std::shared_ptr const & socket_a) +{ + if (!node.network.excluded_peers.check (socket_a->remote_endpoint ())) + { + auto server = std::make_shared (socket_a, node.shared (), true); + nano::lock_guard lock{ mutex }; + connections[server.get ()] = server; + server->start (); + } + else + { + node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_excluded); + node.logger.debug (nano::log::type::tcp_server, "Rejected connection from excluded peer: {}", nano::util::to_str (socket_a->remote_endpoint ())); + } +} + +boost::asio::ip::tcp::endpoint nano::transport::tcp_listener::endpoint () const +{ + nano::lock_guard lock{ mutex }; + if (!stopped) + { + return boost::asio::ip::tcp::endpoint (boost::asio::ip::address_v6::loopback (), acceptor.local_endpoint ().port ()); + } + else + { + return boost::asio::ip::tcp::endpoint (boost::asio::ip::address_v6::loopback (), 0); + } +} + +std::unique_ptr nano::transport::tcp_listener::collect_container_info (std::string const & name) +{ + auto composite = std::make_unique (name); + composite->add_component (std::make_unique (container_info{ "connections", connection_count (), sizeof (decltype (connections)::value_type) })); + return composite; +} \ No newline at end of file diff --git a/nano/node/transport/tcp_listener.hpp b/nano/node/transport/tcp_listener.hpp new file mode 100644 index 000000000..ae049d97e --- /dev/null +++ b/nano/node/transport/tcp_listener.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include +#include + +#include + +namespace nano::transport +{ +class socket; +class tcp_server; + +/** + * Server side portion of bootstrap sessions. Listens for new socket connections and spawns tcp_server objects when connected. + */ +class tcp_listener final : public std::enable_shared_from_this +{ +public: + tcp_listener (uint16_t port, nano::node &, std::size_t max_inbound_connections); + ~tcp_listener (); + + void start (std::function const &, boost::system::error_code const &)> callback); + void stop (); + + void accept_action (boost::system::error_code const &, std::shared_ptr const &); + + std::size_t connection_count (); + nano::tcp_endpoint endpoint () const; + + std::unique_ptr collect_container_info (std::string const & name); + +private: // Dependencies + nano::node & node; + +private: + void on_connection (std::function const &, boost::system::error_code const &)> callback_a); + void evict_dead_connections (); + void on_connection_requeue_delayed (std::function const & new_connection, boost::system::error_code const &)>); + /** Checks whether the maximum number of connections per IP was reached. If so, it returns true. */ + bool limit_reached_for_incoming_ip_connections (std::shared_ptr const & new_connection); + bool limit_reached_for_incoming_subnetwork_connections (std::shared_ptr const & new_connection); + void cleanup (); + +public: + std::atomic bootstrap_count{ 0 }; + std::atomic realtime_count{ 0 }; + +private: + std::unordered_map> connections; + std::multimap> connections_per_address; + + boost::asio::strand strand; + boost::asio::ip::tcp::acceptor acceptor; + boost::asio::ip::tcp::endpoint local; + std::size_t const max_inbound_connections; + + std::atomic stopped; + mutable nano::mutex mutex; +}; +} \ No newline at end of file diff --git a/nano/node/transport/tcp_server.cpp b/nano/node/transport/tcp_server.cpp index b6930d528..e4dc93a4d 100644 --- a/nano/node/transport/tcp_server.cpp +++ b/nano/node/transport/tcp_server.cpp @@ -4,292 +4,11 @@ #include #include #include +#include #include -#include - #include -namespace -{ -bool is_temporary_error (boost::system::error_code const & ec_a) -{ - switch (ec_a.value ()) - { -#if EAGAIN != EWOULDBLOCK - case EAGAIN: -#endif - - case EWOULDBLOCK: - case EINTR: - return true; - default: - return false; - } -} -} - -/* - * tcp_listener - */ - -nano::transport::tcp_listener::tcp_listener (uint16_t port_a, nano::node & node_a, std::size_t max_inbound_connections) : - node (node_a), - strand{ node_a.io_ctx.get_executor () }, - acceptor{ node_a.io_ctx }, - local{ boost::asio::ip::tcp::endpoint{ boost::asio::ip::address_v6::any (), port_a } }, - max_inbound_connections{ max_inbound_connections } -{ -} - -nano::transport::tcp_listener::~tcp_listener () -{ - debug_assert (stopped); -} - -void nano::transport::tcp_listener::start (std::function const &, boost::system::error_code const &)> callback_a) -{ - nano::lock_guard lock{ mutex }; - - acceptor.open (local.protocol ()); - acceptor.set_option (boost::asio::ip::tcp::acceptor::reuse_address (true)); - boost::system::error_code ec; - acceptor.bind (local, ec); - if (!ec) - { - acceptor.listen (boost::asio::socket_base::max_listen_connections, ec); - } - if (ec) - { - node.logger.critical (nano::log::type::tcp_listener, "Error while binding for incoming TCP: {} (port: {})", ec.message (), acceptor.local_endpoint ().port ()); - throw std::runtime_error (ec.message ()); - } - - on_connection (callback_a); -} - -void nano::transport::tcp_listener::stop () -{ - decltype (connections) connections_l; - { - nano::lock_guard lock{ mutex }; - stopped = true; - connections_l.swap (connections); - } - - nano::lock_guard lock{ mutex }; - boost::asio::dispatch (strand, [this_l = shared_from_this ()] () { - this_l->acceptor.close (); - - for (auto & address_connection_pair : this_l->connections_per_address) - { - if (auto connection_l = address_connection_pair.second.lock ()) - { - connection_l->close (); - } - } - this_l->connections_per_address.clear (); - }); -} - -std::size_t nano::transport::tcp_listener::connection_count () -{ - nano::lock_guard lock{ mutex }; - cleanup (); - return connections.size (); -} - -void nano::transport::tcp_listener::cleanup () -{ - debug_assert (!mutex.try_lock ()); - - erase_if (connections, [] (auto const & connection) { - return connection.second.expired (); - }); -} - -bool nano::transport::tcp_listener::limit_reached_for_incoming_subnetwork_connections (std::shared_ptr const & new_connection) -{ - debug_assert (strand.running_in_this_thread ()); - if (node.flags.disable_max_peers_per_subnetwork || nano::transport::is_ipv4_or_v4_mapped_address (new_connection->remote.address ())) - { - // If the limit is disabled, then it is unreachable. - // If the address is IPv4 we don't check for a network limit, since its address space isn't big as IPv6 /64. - return false; - } - auto const counted_connections = socket_functions::count_subnetwork_connections ( - connections_per_address, - new_connection->remote.address ().to_v6 (), - node.network_params.network.ipv6_subnetwork_prefix_for_limiting); - return counted_connections >= node.network_params.network.max_peers_per_subnetwork; -} - -bool nano::transport::tcp_listener::limit_reached_for_incoming_ip_connections (std::shared_ptr const & new_connection) -{ - debug_assert (strand.running_in_this_thread ()); - if (node.flags.disable_max_peers_per_ip) - { - // If the limit is disabled, then it is unreachable. - return false; - } - auto const address_connections_range = connections_per_address.equal_range (new_connection->remote.address ()); - auto const counted_connections = static_cast (std::abs (std::distance (address_connections_range.first, address_connections_range.second))); - return counted_connections >= node.network_params.network.max_peers_per_ip; -} - -void nano::transport::tcp_listener::on_connection (std::function const &, boost::system::error_code const &)> callback_a) -{ - boost::asio::post (strand, boost::asio::bind_executor (strand, [this_l = shared_from_this (), callback = std::move (callback_a)] () mutable { - if (!this_l->acceptor.is_open ()) - { - this_l->node.logger.error (nano::log::type::tcp_listener, "Acceptor is not open"); - return; - } - - // Prepare new connection - auto new_connection = std::make_shared (this_l->node, socket::endpoint_type_t::server); - this_l->acceptor.async_accept (new_connection->tcp_socket, new_connection->remote, - boost::asio::bind_executor (this_l->strand, - [this_l, new_connection, cbk = std::move (callback)] (boost::system::error_code const & ec_a) mutable { - this_l->evict_dead_connections (); - - if (this_l->connections_per_address.size () >= this_l->max_inbound_connections) - { - this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_accept_failure, nano::stat::dir::in); - this_l->node.logger.debug (nano::log::type::tcp_listener, "Max connections reached ({}), unable to open new connection", this_l->connections_per_address.size ()); - - this_l->on_connection_requeue_delayed (std::move (cbk)); - return; - } - - if (this_l->limit_reached_for_incoming_ip_connections (new_connection)) - { - this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_max_per_ip, nano::stat::dir::in); - this_l->node.logger.debug (nano::log::type::tcp_listener, "Max connections per IP reached ({}), unable to open new connection", new_connection->remote_endpoint ().address ().to_string ()); - - this_l->on_connection_requeue_delayed (std::move (cbk)); - return; - } - - if (this_l->limit_reached_for_incoming_subnetwork_connections (new_connection)) - { - auto const remote_ip_address = new_connection->remote_endpoint ().address (); - debug_assert (remote_ip_address.is_v6 ()); - auto const remote_subnet = socket_functions::get_ipv6_subnet_address (remote_ip_address.to_v6 (), this_l->node.network_params.network.max_peers_per_subnetwork); - - this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_max_per_subnetwork, nano::stat::dir::in); - this_l->node.logger.debug (nano::log::type::tcp_listener, "Max connections per subnetwork reached (subnetwork: {}, ip: {}), unable to open new connection", - remote_subnet.canonical ().to_string (), - remote_ip_address.to_string ()); - - this_l->on_connection_requeue_delayed (std::move (cbk)); - return; - } - - if (!ec_a) - { - { - // Best effort attempt to get endpoint addresses - boost::system::error_code ec; - new_connection->local = new_connection->tcp_socket.local_endpoint (ec); - } - - // Make sure the new connection doesn't idle. Note that in most cases, the callback is going to start - // an IO operation immediately, which will start a timer. - new_connection->start (); - new_connection->set_timeout (this_l->node.network_params.network.idle_timeout); - this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_accept_success, nano::stat::dir::in); - this_l->connections_per_address.emplace (new_connection->remote.address (), new_connection); - this_l->node.observers.socket_accepted.notify (*new_connection); - if (cbk (new_connection, ec_a)) - { - this_l->on_connection (std::move (cbk)); - return; - } - this_l->node.logger.warn (nano::log::type::tcp_listener, "Stopping to accept new connections"); - return; - } - - // accept error - this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_accept_failure, nano::stat::dir::in); - this_l->node.logger.error (nano::log::type::tcp_listener, "Unable to accept connection: {} ({})", ec_a.message (), new_connection->remote_endpoint ().address ().to_string ()); - - if (is_temporary_error (ec_a)) - { - // if it is a temporary error, just retry it - this_l->on_connection_requeue_delayed (std::move (cbk)); - return; - } - - // if it is not a temporary error, check how the listener wants to handle this error - if (cbk (new_connection, ec_a)) - { - this_l->on_connection_requeue_delayed (std::move (cbk)); - return; - } - - // No requeue if we reach here, no incoming socket connections will be handled - this_l->node.logger.warn (nano::log::type::tcp_listener, "Stopping to accept new connections"); - })); - })); -} - -// If we are unable to accept a socket, for any reason, we wait just a little (1ms) before rescheduling the next connection accept. -// The intention is to throttle back the connection requests and break up any busy loops that could possibly form and -// give the rest of the system a chance to recover. -void nano::transport::tcp_listener::on_connection_requeue_delayed (std::function const &, boost::system::error_code const &)> callback_a) -{ - node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::milliseconds (1), [this_l = shared_from_this (), callback = std::move (callback_a)] () mutable { - this_l->on_connection (std::move (callback)); - }); -} - -// This must be called from a strand -void nano::transport::tcp_listener::evict_dead_connections () -{ - debug_assert (strand.running_in_this_thread ()); - - erase_if (connections_per_address, [] (auto const & entry) { - return entry.second.expired (); - }); -} - -void nano::transport::tcp_listener::accept_action (boost::system::error_code const & ec, std::shared_ptr const & socket_a) -{ - if (!node.network.excluded_peers.check (socket_a->remote_endpoint ())) - { - auto server = std::make_shared (socket_a, node.shared (), true); - nano::lock_guard lock{ mutex }; - connections[server.get ()] = server; - server->start (); - } - else - { - node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_excluded); - node.logger.debug (nano::log::type::tcp_server, "Rejected connection from excluded peer: {}", nano::util::to_str (socket_a->remote_endpoint ())); - } -} - -boost::asio::ip::tcp::endpoint nano::transport::tcp_listener::endpoint () const -{ - nano::lock_guard lock{ mutex }; - if (!stopped) - { - return boost::asio::ip::tcp::endpoint (boost::asio::ip::address_v6::loopback (), acceptor.local_endpoint ().port ()); - } - else - { - return boost::asio::ip::tcp::endpoint (boost::asio::ip::address_v6::loopback (), 0); - } -} - -std::unique_ptr nano::transport::tcp_listener::collect_container_info (std::string const & name) -{ - auto composite = std::make_unique (name); - composite->add_component (std::make_unique (container_info{ "connections", connection_count (), sizeof (decltype (connections)::value_type) })); - return composite; -} - /* * tcp_server */ diff --git a/nano/node/transport/tcp_server.hpp b/nano/node/transport/tcp_server.hpp index 8743772c8..fdd28f187 100644 --- a/nano/node/transport/tcp_server.hpp +++ b/nano/node/transport/tcp_server.hpp @@ -16,54 +16,6 @@ namespace nano::transport class message_deserializer; class tcp_server; -/** - * Server side portion of bootstrap sessions. Listens for new socket connections and spawns tcp_server objects when connected. - */ -class tcp_listener final : public std::enable_shared_from_this -{ -public: - tcp_listener (uint16_t port, nano::node &, std::size_t max_inbound_connections); - ~tcp_listener (); - - void start (std::function const &, boost::system::error_code const &)> callback); - void stop (); - - void accept_action (boost::system::error_code const &, std::shared_ptr const &); - - std::size_t connection_count (); - nano::tcp_endpoint endpoint () const; - - std::unique_ptr collect_container_info (std::string const & name); - -private: // Dependencies - nano::node & node; - -private: - void on_connection (std::function const &, boost::system::error_code const &)> callback_a); - void evict_dead_connections (); - void on_connection_requeue_delayed (std::function const & new_connection, boost::system::error_code const &)>); - /** Checks whether the maximum number of connections per IP was reached. If so, it returns true. */ - bool limit_reached_for_incoming_ip_connections (std::shared_ptr const & new_connection); - bool limit_reached_for_incoming_subnetwork_connections (std::shared_ptr const & new_connection); - void cleanup (); - -public: - std::atomic bootstrap_count{ 0 }; - std::atomic realtime_count{ 0 }; - -private: - std::unordered_map> connections; - std::multimap> connections_per_address; - - boost::asio::strand strand; - boost::asio::ip::tcp::acceptor acceptor; - boost::asio::ip::tcp::endpoint local; - std::size_t const max_inbound_connections; - - std::atomic stopped; - mutable nano::mutex mutex; -}; - class tcp_server final : public std::enable_shared_from_this { public: diff --git a/nano/test_common/system.cpp b/nano/test_common/system.cpp index 914d472e5..4c9b2579a 100644 --- a/nano/test_common/system.cpp +++ b/nano/test_common/system.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include From 9c72bebfb1c9b052abe5f6798c9d813862997914 Mon Sep 17 00:00:00 2001 From: gr0vity-dev <85646666+gr0vity-dev@users.noreply.github.com> Date: Fri, 22 Mar 2024 10:28:00 +0100 Subject: [PATCH 075/128] Introduce nano-nightly dockerhub registry for live network builds (#4513) * simplify DOCKER_PASSWORD conditional check * move env variable declaration to job level * feat: introduce nano-nightly dockerhub registry for live network - all non release builds for the live network are pushed to nano-nightly - only release builds will be pushed to nanocurrency/nano dockerhub registry * avoid setting `docker_image_name` twice --------- Co-authored-by: gr0vity-dev --- .github/workflows/build_deploy.yml | 16 +++++++--------- ci/actions/linux/docker-impl/docker-common.sh | 6 +++++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build_deploy.yml b/.github/workflows/build_deploy.yml index b9a25f868..49c9a884e 100644 --- a/.github/workflows/build_deploy.yml +++ b/.github/workflows/build_deploy.yml @@ -155,6 +155,12 @@ jobs: strategy: matrix: network: ["TEST", "BETA", "LIVE"] + env: + CI_TAG: ${{ needs.prepare_build.outputs.ci_tag }} + DOCKER_REGISTRY: ${{ vars.DOCKER_REGISTRY }} + DOCKER_USER: ${{ vars.DOCKER_USER }} + DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} + IS_RELEASE_BUILD: ${{ github.event.inputs.is_release_build || 'false' }} steps: - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 #v3.1.0 with: @@ -165,23 +171,15 @@ jobs: run: ci/actions/linux/docker-build.sh env: NETWORK: ${{ matrix.network }} - CI_TAG: ${{ needs.prepare_build.outputs.ci_tag }} - DOCKER_REGISTRY: ${{ vars.DOCKER_REGISTRY }} - - name: Check if secrets.DOCKER_PASSWORD exists - run: echo "DOCKER_PASSWORD_EXISTS=${{ secrets.DOCKER_PASSWORD != '' }}" >> $GITHUB_ENV - name: Deploy Docker Hub - if: env.DOCKER_PASSWORD_EXISTS == 'true' + if: env.DOCKER_PASSWORD != '' run: ci/actions/linux/docker-deploy.sh env: - CI_TAG: ${{ needs.prepare_build.outputs.ci_tag }} NETWORK: ${{ matrix.network }} - DOCKER_REGISTRY: ${{ vars.DOCKER_REGISTRY }} - DOCKER_USER: ${{ vars.DOCKER_USER }} DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} - name: Deploy Docker (ghcr.io) run: ci/actions/linux/ghcr-deploy.sh env: - CI_TAG: ${{ needs.prepare_build.outputs.ci_tag }} NETWORK: ${{ matrix.network }} DOCKER_REGISTRY: ghcr.io DOCKER_USER: ${{ github.repository_owner }} diff --git a/ci/actions/linux/docker-impl/docker-common.sh b/ci/actions/linux/docker-impl/docker-common.sh index bcda39fbb..912558b12 100755 --- a/ci/actions/linux/docker-impl/docker-common.sh +++ b/ci/actions/linux/docker-impl/docker-common.sh @@ -18,7 +18,11 @@ fi if [[ "$NETWORK" = "LIVE" ]]; then echo "Live" - network_tag_suffix='' + if [[ "$IS_RELEASE_BUILD" = "true" ]]; then + network_tag_suffix='' + else + network_tag_suffix='-nightly' + fi network="live" elif [[ "$NETWORK" = "BETA" ]]; then echo "Beta" From 5980be65cb58d7d9747c62c3523b18c329f2b145 Mon Sep 17 00:00:00 2001 From: Gustav Schauwecker Date: Mon, 11 Mar 2024 15:31:33 +0100 Subject: [PATCH 076/128] Fill rep_weights table --- nano/core_test/block_store.cpp | 43 ++++++++++++++++++++++++++++++++++ nano/secure/account_info.cpp | 33 ++++++++++++++++++++++++++ nano/secure/account_info.hpp | 20 ++++++++++++++++ nano/store/component.hpp | 2 +- nano/store/db_val.hpp | 4 ++++ nano/store/db_val_impl.hpp | 16 +++++++++++++ nano/store/lmdb/lmdb.cpp | 40 ++++++++++++++++++++++++++++++- nano/store/lmdb/lmdb.hpp | 1 + nano/store/rocksdb/rocksdb.cpp | 36 ++++++++++++++++++++++++++++ nano/store/rocksdb/rocksdb.hpp | 1 + 10 files changed, 194 insertions(+), 2 deletions(-) diff --git a/nano/core_test/block_store.cpp b/nano/core_test/block_store.cpp index ecd575ea4..2f611b01f 100644 --- a/nano/core_test/block_store.cpp +++ b/nano/core_test/block_store.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -1445,6 +1446,48 @@ TEST (rocksdb_block_store, upgrade_v21_v22) } } +// Tests that the new rep_weight table gets filled with all +// existing representatives +TEST (mdb_block_store, upgrade_v22_to_v23) +{ + nano::logger logger; + auto const path = nano::unique_path (); + nano::account rep_a{ 123 }; + nano::account rep_b{ 456 }; + // Setting the database to its 22nd version state + { + auto store{ nano::make_store (logger, path, nano::dev::constants) }; + auto txn{ store->tx_begin_write () }; + + // Add three accounts referencing two representatives + nano::account_info info1{}; + info1.representative = rep_a; + info1.balance = 1000; + store->account.put (txn, 1, info1); + + nano::account_info info2{}; + info2.representative = rep_a; + info2.balance = 500; + store->account.put (txn, 2, info2); + + nano::account_info info3{}; + info3.representative = rep_b; + info3.balance = 42; + store->account.put (txn, 3, info3); + + store->version.put (txn, 22); + } + + // Testing the upgrade code worked + auto store{ nano::make_store (logger, path, nano::dev::constants) }; + auto txn (store->tx_begin_read ()); + ASSERT_EQ (store->version.get (txn), store->version_current); + + // The rep_weight table should contain all reps now + ASSERT_EQ (1500, store->rep_weight.get (txn, rep_a)); + ASSERT_EQ (42, store->rep_weight.get (txn, rep_b)); +} + TEST (mdb_block_store, upgrade_backup) { if (nano::rocksdb_config::using_rocksdb_in_tests ()) diff --git a/nano/secure/account_info.cpp b/nano/secure/account_info.cpp index b478f95e7..1b18ca602 100644 --- a/nano/secure/account_info.cpp +++ b/nano/secure/account_info.cpp @@ -58,3 +58,36 @@ nano::epoch nano::account_info::epoch () const { return epoch_m; } + +size_t nano::account_info_v22::db_size () const +{ + debug_assert (reinterpret_cast (this) == reinterpret_cast (&head)); + debug_assert (reinterpret_cast (&head) + sizeof (head) == reinterpret_cast (&representative)); + debug_assert (reinterpret_cast (&representative) + sizeof (representative) == reinterpret_cast (&open_block)); + debug_assert (reinterpret_cast (&open_block) + sizeof (open_block) == reinterpret_cast (&balance)); + debug_assert (reinterpret_cast (&balance) + sizeof (balance) == reinterpret_cast (&modified)); + debug_assert (reinterpret_cast (&modified) + sizeof (modified) == reinterpret_cast (&block_count)); + debug_assert (reinterpret_cast (&block_count) + sizeof (block_count) == reinterpret_cast (&epoch_m)); + return sizeof (head) + sizeof (representative) + sizeof (open_block) + sizeof (balance) + sizeof (modified) + sizeof (block_count) + sizeof (epoch_m); +} + +bool nano::account_info_v22::deserialize (nano::stream & stream_a) +{ + auto error (false); + try + { + nano::read (stream_a, head.bytes); + nano::read (stream_a, representative.bytes); + nano::read (stream_a, open_block.bytes); + nano::read (stream_a, balance.bytes); + nano::read (stream_a, modified); + nano::read (stream_a, block_count); + nano::read (stream_a, epoch_m); + } + catch (std::runtime_error const &) + { + error = true; + } + + return error; +} \ No newline at end of file diff --git a/nano/secure/account_info.hpp b/nano/secure/account_info.hpp index 422515fa8..bd7a7b0cc 100644 --- a/nano/secure/account_info.hpp +++ b/nano/secure/account_info.hpp @@ -29,4 +29,24 @@ public: uint64_t block_count{ 0 }; nano::epoch epoch_m{ nano::epoch::epoch_0 }; }; + +/** + * Account info as of DB version 22. + * This class protects the DB upgrades from future changes of the account_info class. + */ +class account_info_v22 final +{ +public: + account_info_v22 () = default; + size_t db_size () const; + bool deserialize (nano::stream &); + nano::block_hash head{ 0 }; + nano::account representative{}; + nano::block_hash open_block{ 0 }; + nano::amount balance{ 0 }; + /** Seconds since posix epoch */ + nano::seconds_t modified{ 0 }; + uint64_t block_count{ 0 }; + nano::epoch epoch_m{ nano::epoch::epoch_0 }; +}; } // namespace nano diff --git a/nano/store/component.hpp b/nano/store/component.hpp index eaa8faf95..ac5103e92 100644 --- a/nano/store/component.hpp +++ b/nano/store/component.hpp @@ -72,7 +72,7 @@ namespace store store::pending & pending; store::rep_weight & rep_weight; static int constexpr version_minimum{ 21 }; - static int constexpr version_current{ 22 }; + static int constexpr version_current{ 23 }; public: store::online_weight & online_weight; diff --git a/nano/store/db_val.hpp b/nano/store/db_val.hpp index fee58d9e0..7cc415a5f 100644 --- a/nano/store/db_val.hpp +++ b/nano/store/db_val.hpp @@ -12,6 +12,7 @@ namespace nano { class account_info; +class account_info_v22; class block; class pending_info; class pending_key; @@ -63,6 +64,8 @@ public: db_val (nano::account_info const & val_a); + db_val (nano::account_info_v22 const & val_a); + db_val (nano::pending_info const & val_a); db_val (nano::pending_key const & val_a); @@ -103,6 +106,7 @@ public: } explicit operator nano::account_info () const; + explicit operator nano::account_info_v22 () const; explicit operator block_info () const { diff --git a/nano/store/db_val_impl.hpp b/nano/store/db_val_impl.hpp index c8ee18624..f2eaf4884 100644 --- a/nano/store/db_val_impl.hpp +++ b/nano/store/db_val_impl.hpp @@ -10,6 +10,13 @@ nano::store::db_val::db_val (nano::account_info const & val_a) : db_val (val_a.db_size (), const_cast (&val_a)) { } + +template +nano::store::db_val::db_val (nano::account_info_v22 const & val_a) : + db_val (val_a.db_size (), const_cast (&val_a)) +{ +} + template nano::store::db_val::db_val (std::shared_ptr const & val_a) : buffer (std::make_shared> ()) @@ -44,6 +51,15 @@ nano::store::db_val::operator nano::account_info () const return result; } +template +nano::store::db_val::operator nano::account_info_v22 () const +{ + nano::account_info_v22 result; + debug_assert (size () == result.db_size ()); + std::copy (reinterpret_cast (data ()), reinterpret_cast (data ()) + result.db_size (), reinterpret_cast (&result)); + return result; +} + template nano::store::db_val::operator std::shared_ptr () const { diff --git a/nano/store/lmdb/lmdb.cpp b/nano/store/lmdb/lmdb.cpp index b5397b50f..179c14b40 100644 --- a/nano/store/lmdb/lmdb.cpp +++ b/nano/store/lmdb/lmdb.cpp @@ -1,6 +1,8 @@ +#include #include #include #include +#include #include #include #include @@ -206,7 +208,7 @@ void nano::store::lmdb::component::open_databases (bool & error_a, store::transa pending_store.pending_handle = pending_store.pending_v0_handle; error_a |= mdb_dbi_open (env.tx (transaction_a), "final_votes", flags, &final_vote_store.final_votes_handle) != 0; error_a |= mdb_dbi_open (env.tx (transaction_a), "blocks", MDB_CREATE, &block_store.blocks_handle) != 0; - mdb_dbi_open (env.tx (transaction_a), "rep_weights", flags, &rep_weight_store.rep_weights_handle); + error_a |= mdb_dbi_open (env.tx (transaction_a), "rep_weights", flags, &rep_weight_store.rep_weights_handle) != 0; } bool nano::store::lmdb::component::do_upgrades (store::write_transaction & transaction_a, nano::ledger_constants & constants, bool & needs_vacuuming) @@ -224,6 +226,9 @@ bool nano::store::lmdb::component::do_upgrades (store::write_transaction & trans upgrade_v21_to_v22 (transaction_a); [[fallthrough]]; case 22: + upgrade_v22_to_v23 (transaction_a); + [[fallthrough]]; + case 23: break; default: logger.critical (nano::log::type::lmdb, "The version of the ledger ({}) is too high for this node", version_l); @@ -245,6 +250,39 @@ void nano::store::lmdb::component::upgrade_v21_to_v22 (store::write_transaction logger.info (nano::log::type::lmdb, "Upgrading database from v21 to v22 completed"); } +// Fill rep_weights table with all existing representatives and their vote weight +void nano::store::lmdb::component::upgrade_v22_to_v23 (store::write_transaction const & transaction_a) +{ + logger.info (nano::log::type::lmdb, "Upgrading database from v22 to v23..."); + auto i{ make_iterator (transaction_a, tables::accounts) }; + auto end{ store::iterator (nullptr) }; + uint64_t processed_accounts = 0; + for (; i != end; ++i) + { + if (!i->second.balance.is_zero ()) + { + nano::uint128_t total{ 0 }; + nano::store::lmdb::db_val value; + auto status = get (transaction_a, tables::rep_weights, i->second.representative, value); + if (success (status)) + { + total = nano::amount{ value }.number (); + } + total += i->second.balance.number (); + status = put (transaction_a, tables::rep_weights, i->second.representative, nano::amount{ total }); + release_assert_success (status); + } + processed_accounts++; + if (processed_accounts % 250000 == 0) + { + logger.info (nano::log::type::lmdb, "processed {} accounts", processed_accounts); + } + } + logger.info (nano::log::type::lmdb, "processed {} accounts", processed_accounts); + version.put (transaction_a, 23); + logger.info (nano::log::type::lmdb, "Upgrading database from v22 to v23 completed"); +} + /** Takes a filepath, appends '_backup_' to the end (but before any extension) and saves that file in the same directory */ void nano::store::lmdb::component::create_backup_file (nano::store::lmdb::env & env_a, std::filesystem::path const & filepath_a, nano::logger & logger) { diff --git a/nano/store/lmdb/lmdb.hpp b/nano/store/lmdb/lmdb.hpp index 90bbc8ca6..c76381886 100644 --- a/nano/store/lmdb/lmdb.hpp +++ b/nano/store/lmdb/lmdb.hpp @@ -116,6 +116,7 @@ public: private: bool do_upgrades (store::write_transaction &, nano::ledger_constants & constants, bool &); void upgrade_v21_to_v22 (store::write_transaction const &); + void upgrade_v22_to_v23 (store::write_transaction const &); void open_databases (bool &, store::transaction const &, unsigned); diff --git a/nano/store/rocksdb/rocksdb.cpp b/nano/store/rocksdb/rocksdb.cpp index 515a03772..f13cdb76b 100644 --- a/nano/store/rocksdb/rocksdb.cpp +++ b/nano/store/rocksdb/rocksdb.cpp @@ -245,6 +245,9 @@ bool nano::store::rocksdb::component::do_upgrades (store::write_transaction cons upgrade_v21_to_v22 (transaction_a); [[fallthrough]]; case 22: + upgrade_v22_to_v23 (transaction_a); + [[fallthrough]]; + case 23: break; default: logger.critical (nano::log::type::rocksdb, "The version of the ledger ({}) is too high for this node", version_l); @@ -280,6 +283,39 @@ void nano::store::rocksdb::component::upgrade_v21_to_v22 (store::write_transacti logger.info (nano::log::type::rocksdb, "Upgrading database from v21 to v22 completed"); } +// Fill rep_weights table with all existing representatives and their vote weight +void nano::store::rocksdb::component::upgrade_v22_to_v23 (store::write_transaction const & transaction_a) +{ + logger.info (nano::log::type::rocksdb, "Upgrading database from v22 to v23..."); + auto i{ make_iterator (transaction_a, tables::accounts) }; + auto end{ store::iterator (nullptr) }; + uint64_t processed_accounts = 0; + for (; i != end; ++i) + { + if (!i->second.balance.is_zero ()) + { + nano::uint128_t total{ 0 }; + nano::store::rocksdb::db_val value; + auto status = get (transaction_a, tables::rep_weights, i->second.representative, value); + if (success (status)) + { + total = nano::amount{ value }.number (); + } + total += i->second.balance.number (); + status = put (transaction_a, tables::rep_weights, i->second.representative, nano::amount{ total }); + release_assert_success (status); + } + processed_accounts++; + if (processed_accounts % 250000 == 0) + { + logger.info (nano::log::type::lmdb, "processed {} accounts", processed_accounts); + } + } + logger.info (nano::log::type::lmdb, "processed {} accounts", processed_accounts); + version.put (transaction_a, 23); + logger.info (nano::log::type::rocksdb, "Upgrading database from v22 to v23 completed"); +} + void nano::store::rocksdb::component::generate_tombstone_map () { tombstone_map.emplace (std::piecewise_construct, std::forward_as_tuple (nano::tables::blocks), std::forward_as_tuple (0, 25000)); diff --git a/nano/store/rocksdb/rocksdb.hpp b/nano/store/rocksdb/rocksdb.hpp index f23145b02..f6c2d683b 100644 --- a/nano/store/rocksdb/rocksdb.hpp +++ b/nano/store/rocksdb/rocksdb.hpp @@ -154,6 +154,7 @@ private: bool do_upgrades (store::write_transaction const &); void upgrade_v21_to_v22 (store::write_transaction const &); + void upgrade_v22_to_v23 (store::write_transaction const &); void construct_column_family_mutexes (); ::rocksdb::Options get_db_options (); From 23947a3a68b859be1e9573278bf55c07b1366725 Mon Sep 17 00:00:00 2001 From: Gustav Schauwecker Date: Tue, 12 Mar 2024 09:00:00 +0100 Subject: [PATCH 077/128] Move reps_cache to nano/secure This is needed, because reps_cache will get access to the new rep_cache data store. --- nano/lib/CMakeLists.txt | 2 -- nano/secure/CMakeLists.txt | 2 ++ nano/secure/common.hpp | 2 +- nano/secure/ledger.cpp | 2 +- nano/secure/ledger_cache.hpp | 2 +- nano/{lib => secure}/rep_weights.cpp | 2 +- nano/{lib => secure}/rep_weights.hpp | 0 7 files changed, 6 insertions(+), 6 deletions(-) rename nano/{lib => secure}/rep_weights.cpp (98%) rename nano/{lib => secure}/rep_weights.hpp (100%) diff --git a/nano/lib/CMakeLists.txt b/nano/lib/CMakeLists.txt index c7029af76..fa9c81f61 100644 --- a/nano/lib/CMakeLists.txt +++ b/nano/lib/CMakeLists.txt @@ -72,8 +72,6 @@ add_library( rate_limiting.hpp rate_limiting.cpp relaxed_atomic.hpp - rep_weights.hpp - rep_weights.cpp rocksdbconfig.hpp rocksdbconfig.cpp rpc_handler_interface.hpp diff --git a/nano/secure/CMakeLists.txt b/nano/secure/CMakeLists.txt index f9ca76129..cdb5adae0 100644 --- a/nano/secure/CMakeLists.txt +++ b/nano/secure/CMakeLists.txt @@ -53,6 +53,8 @@ add_library( network_filter.cpp pending_info.hpp pending_info.cpp + rep_weights.hpp + rep_weights.cpp utility.hpp utility.cpp vote.hpp diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index e1d43fec8..119e05142 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -6,10 +6,10 @@ #include #include #include -#include #include #include #include +#include #include #include diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 55570fcf8..e9d029183 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -1,12 +1,12 @@ #include #include -#include #include #include #include #include #include #include +#include #include #include #include diff --git a/nano/secure/ledger_cache.hpp b/nano/secure/ledger_cache.hpp index 726a1e111..28cac3dcd 100644 --- a/nano/secure/ledger_cache.hpp +++ b/nano/secure/ledger_cache.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include diff --git a/nano/lib/rep_weights.cpp b/nano/secure/rep_weights.cpp similarity index 98% rename from nano/lib/rep_weights.cpp rename to nano/secure/rep_weights.cpp index 65a0604b4..dd69bbd65 100644 --- a/nano/lib/rep_weights.cpp +++ b/nano/secure/rep_weights.cpp @@ -1,4 +1,4 @@ -#include +#include #include void nano::rep_weights::representation_add (nano::account const & source_rep_a, nano::uint128_t const & amount_a) diff --git a/nano/lib/rep_weights.hpp b/nano/secure/rep_weights.hpp similarity index 100% rename from nano/lib/rep_weights.hpp rename to nano/secure/rep_weights.hpp From 3e5055e3c6b5496eebcc0115f8b5b1df87b0e7f1 Mon Sep 17 00:00:00 2001 From: Gustav Schauwecker Date: Mon, 11 Mar 2024 16:38:34 +0100 Subject: [PATCH 078/128] Update rep_weight table when rep_weights cache is changed --- nano/core_test/block_store.cpp | 10 ++++----- nano/core_test/ledger.cpp | 4 +++- nano/core_test/node.cpp | 2 +- nano/secure/ledger.cpp | 38 +++++++++++++++++++++------------- nano/secure/ledger_cache.cpp | 6 ++++++ nano/secure/ledger_cache.hpp | 2 ++ nano/secure/rep_weights.cpp | 34 ++++++++++++++++++++---------- nano/secure/rep_weights.hpp | 11 +++++++--- nano/store/component.cpp | 1 + 9 files changed, 73 insertions(+), 35 deletions(-) diff --git a/nano/core_test/block_store.cpp b/nano/core_test/block_store.cpp index 2f611b01f..385829766 100644 --- a/nano/core_test/block_store.cpp +++ b/nano/core_test/block_store.cpp @@ -380,7 +380,7 @@ TEST (block_store, genesis) nano::logger logger; auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants); ASSERT_TRUE (!store->init_error ()); - nano::ledger_cache ledger_cache; + nano::ledger_cache ledger_cache{ store->rep_weight }; auto transaction (store->tx_begin_write ()); store->initialize (transaction, ledger_cache, nano::dev::constants); nano::account_info info; @@ -902,7 +902,7 @@ TEST (block_store, cemented_count_cache) auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants); ASSERT_TRUE (!store->init_error ()); auto transaction (store->tx_begin_write ()); - nano::ledger_cache ledger_cache; + nano::ledger_cache ledger_cache{ store->rep_weight }; store->initialize (transaction, ledger_cache, nano::dev::constants); ASSERT_EQ (1, ledger_cache.cemented_count); } @@ -912,7 +912,7 @@ TEST (block_store, block_random) nano::logger logger; auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants); { - nano::ledger_cache ledger_cache; + nano::ledger_cache ledger_cache{ store->rep_weight }; auto transaction (store->tx_begin_write ()); store->initialize (transaction, ledger_cache, nano::dev::constants); } @@ -939,7 +939,7 @@ TEST (block_store, pruned_random) block->sideband_set ({}); auto hash1 (block->hash ()); { - nano::ledger_cache ledger_cache; + nano::ledger_cache ledger_cache{ store->rep_weight }; auto transaction (store->tx_begin_write ()); store->initialize (transaction, ledger_cache, nano::dev::constants); store->pruned.put (transaction, hash1); @@ -969,7 +969,7 @@ TEST (block_store, state_block) block1->sideband_set ({}); { - nano::ledger_cache ledger_cache; + nano::ledger_cache ledger_cache{ store->rep_weight }; auto transaction (store->tx_begin_write ()); store->initialize (transaction, ledger_cache, nano::dev::constants); ASSERT_EQ (nano::block_type::state, block1->type ()); diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 773057e89..e688ccff6 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -657,8 +658,9 @@ TEST (ledger, open_fork) TEST (ledger, representation_changes) { + auto store{ nano::test::make_store () }; nano::keypair key1; - nano::rep_weights rep_weights; + nano::rep_weights rep_weights{ store->rep_weight }; ASSERT_EQ (0, rep_weights.representation_get (key1.pub)); rep_weights.representation_put (key1.pub, 1); ASSERT_EQ (1, rep_weights.representation_get (key1.pub)); diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index cf81a51bd..56bd706a7 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -2906,7 +2906,7 @@ TEST (node, dont_write_lock_node) nano::logger logger; auto store = nano::make_store (logger, path, nano::dev::constants, false, true); { - nano::ledger_cache ledger_cache; + nano::ledger_cache ledger_cache{ store->rep_weight }; auto transaction (store->tx_begin_write ()); store->initialize (transaction, ledger_cache, nano::dev::constants); } diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index e9d029183..d22a7a124 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -17,6 +18,7 @@ #include #include #include +#include #include #include @@ -51,7 +53,7 @@ public: auto info = ledger.account_info (transaction, pending.value ().source); debug_assert (info); ledger.store.pending.del (transaction, key); - ledger.cache.rep_weights.representation_add (info->representative, pending.value ().amount.number ()); + ledger.cache.rep_weights.representation_add (transaction, info->representative, pending.value ().amount.number ()); nano::account_info new_info (block_a.hashables.previous, info->representative, info->open_block, ledger.balance (transaction, block_a.hashables.previous).value (), nano::seconds_since_epoch (), info->block_count - 1, nano::epoch::epoch_0); ledger.update_account (transaction, pending.value ().source, *info, new_info); ledger.store.block.del (transaction, hash); @@ -70,7 +72,7 @@ public: auto source_account = ledger.account (transaction, block_a.hashables.source); auto info = ledger.account_info (transaction, destination_account); debug_assert (info); - ledger.cache.rep_weights.representation_add (info->representative, 0 - amount); + ledger.cache.rep_weights.representation_add (transaction, info->representative, 0 - amount); nano::account_info new_info (block_a.hashables.previous, info->representative, info->open_block, ledger.balance (transaction, block_a.hashables.previous).value (), nano::seconds_since_epoch (), info->block_count - 1, nano::epoch::epoch_0); ledger.update_account (transaction, destination_account, *info, new_info); ledger.store.block.del (transaction, hash); @@ -86,7 +88,7 @@ public: auto amount = ledger.amount (transaction, hash).value (); auto destination_account = block_a.account (); auto source_account = ledger.account (transaction, block_a.hashables.source); - ledger.cache.rep_weights.representation_add (block_a.representative_field ().value (), 0 - amount); + ledger.cache.rep_weights.representation_add (transaction, block_a.representative_field ().value (), 0 - amount); nano::account_info new_info; ledger.update_account (transaction, destination_account, new_info, new_info); ledger.store.block.del (transaction, hash); @@ -105,7 +107,7 @@ public: auto block = ledger.store.block.get (transaction, rep_block); release_assert (block != nullptr); auto representative = block->representative_field ().value (); - ledger.cache.rep_weights.representation_add_dual (block_a.hashables.representative, 0 - balance, representative, balance); + ledger.cache.rep_weights.representation_add_dual (transaction, block_a.hashables.representative, 0 - balance, representative, balance); ledger.store.block.del (transaction, hash); nano::account_info new_info (block_a.hashables.previous, representative, info->open_block, info->balance, nano::seconds_since_epoch (), info->block_count - 1, nano::epoch::epoch_0); ledger.update_account (transaction, account, *info, new_info); @@ -131,12 +133,12 @@ public: auto block (ledger.store.block.get (transaction, rep_block_hash)); debug_assert (block != nullptr); representative = block->representative_field ().value (); - ledger.cache.rep_weights.representation_add_dual (representative, balance, block_a.hashables.representative, 0 - block_a.hashables.balance.number ()); + ledger.cache.rep_weights.representation_add_dual (transaction, representative, balance, block_a.hashables.representative, 0 - block_a.hashables.balance.number ()); } else { // Add in amount delta only - ledger.cache.rep_weights.representation_add (block_a.hashables.representative, 0 - block_a.hashables.balance.number ()); + ledger.cache.rep_weights.representation_add (transaction, block_a.hashables.representative, 0 - block_a.hashables.balance.number ()); } auto info = ledger.account_info (transaction, block_a.hashables.account); @@ -346,12 +348,12 @@ void ledger_processor::state_block_impl (nano::state_block & block_a) if (!info.head.is_zero ()) { // Move existing representation & add in amount delta - ledger.cache.rep_weights.representation_add_dual (info.representative, 0 - info.balance.number (), block_a.hashables.representative, block_a.hashables.balance.number ()); + ledger.cache.rep_weights.representation_add_dual (transaction, info.representative, 0 - info.balance.number (), block_a.hashables.representative, block_a.hashables.balance.number ()); } else { // Add in amount delta only - ledger.cache.rep_weights.representation_add (block_a.hashables.representative, block_a.hashables.balance.number ()); + ledger.cache.rep_weights.representation_add (transaction, block_a.hashables.representative, block_a.hashables.balance.number ()); } if (is_send) @@ -482,7 +484,7 @@ void ledger_processor::change_block (nano::change_block & block_a) block_a.sideband_set (nano::block_sideband (account, 0, info->balance, info->block_count + 1, nano::seconds_since_epoch (), block_details, nano::epoch::epoch_0 /* unused */)); ledger.store.block.put (transaction, hash, block_a); auto balance = previous->balance (); - ledger.cache.rep_weights.representation_add_dual (block_a.hashables.representative, balance.number (), info->representative, 0 - balance.number ()); + ledger.cache.rep_weights.representation_add_dual (transaction, block_a.hashables.representative, balance.number (), info->representative, 0 - balance.number ()); nano::account_info new_info (hash, block_a.hashables.representative, info->open_block, info->balance, nano::seconds_since_epoch (), info->block_count + 1, nano::epoch::epoch_0); ledger.update_account (transaction, account, *info, new_info); ledger.store.frontier.del (transaction, block_a.hashables.previous); @@ -529,7 +531,7 @@ void ledger_processor::send_block (nano::send_block & block_a) if (result == nano::block_status::progress) { auto amount (info->balance.number () - block_a.hashables.balance.number ()); - ledger.cache.rep_weights.representation_add (info->representative, 0 - amount); + ledger.cache.rep_weights.representation_add (transaction, info->representative, 0 - amount); block_a.sideband_set (nano::block_sideband (account, 0, block_a.hashables.balance /* unused */, info->block_count + 1, nano::seconds_since_epoch (), block_details, nano::epoch::epoch_0 /* unused */)); ledger.store.block.put (transaction, hash, block_a); nano::account_info new_info (hash, info->representative, info->open_block, block_a.hashables.balance, nano::seconds_since_epoch (), info->block_count + 1, nano::epoch::epoch_0); @@ -602,7 +604,7 @@ void ledger_processor::receive_block (nano::receive_block & block_a) ledger.store.block.put (transaction, hash, block_a); nano::account_info new_info (hash, info->representative, info->open_block, new_balance, nano::seconds_since_epoch (), info->block_count + 1, nano::epoch::epoch_0); ledger.update_account (transaction, account, *info, new_info); - ledger.cache.rep_weights.representation_add (info->representative, pending.value ().amount.number ()); + ledger.cache.rep_weights.representation_add (transaction, info->representative, pending.value ().amount.number ()); ledger.store.frontier.del (transaction, block_a.hashables.previous); ledger.store.frontier.put (transaction, hash, account); ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::receive); @@ -664,7 +666,7 @@ void ledger_processor::open_block (nano::open_block & block_a) ledger.store.block.put (transaction, hash, block_a); nano::account_info new_info (hash, block_a.representative_field ().value (), hash, pending.value ().amount.number (), nano::seconds_since_epoch (), 1, nano::epoch::epoch_0); ledger.update_account (transaction, block_a.hashables.account, info, new_info); - ledger.cache.rep_weights.representation_add (block_a.representative_field ().value (), pending.value ().amount.number ()); + ledger.cache.rep_weights.representation_add (transaction, block_a.representative_field ().value (), pending.value ().amount.number ()); ledger.store.frontier.put (transaction, hash, block_a.hashables.account); ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::open); } @@ -750,6 +752,7 @@ void representative_visitor::state_block (nano::state_block const & block_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) : constants{ constants }, store{ store_a }, + cache{ store_a.rep_weight }, stats{ stat_a }, check_bootstrap_weights{ true } { @@ -767,16 +770,23 @@ void nano::ledger::initialize (nano::generate_cache_flags const & generate_cache [this] (store::read_transaction const & /*unused*/, store::iterator i, store::iterator n) { uint64_t block_count_l{ 0 }; uint64_t account_count_l{ 0 }; - decltype (this->cache.rep_weights) rep_weights_l; for (; i != n; ++i) { nano::account_info const & info (i->second); block_count_l += info.block_count; ++account_count_l; - rep_weights_l.representation_add (info.representative, info.balance.number ()); } this->cache.block_count += block_count_l; this->cache.account_count += account_count_l; + }); + + store.rep_weight.for_each_par ( + [this] (store::read_transaction const & /*unused*/, store::iterator i, store::iterator n) { + nano::rep_weights rep_weights_l{ this->store.rep_weight }; + for (; i != n; ++i) + { + rep_weights_l.representation_put (i->first, i->second.number ()); + } this->cache.rep_weights.copy_from (rep_weights_l); }); } diff --git a/nano/secure/ledger_cache.cpp b/nano/secure/ledger_cache.cpp index e69de29bb..5076e1cc1 100644 --- a/nano/secure/ledger_cache.cpp +++ b/nano/secure/ledger_cache.cpp @@ -0,0 +1,6 @@ +#include + +nano::ledger_cache::ledger_cache (nano::store::rep_weight & rep_weight_store_a) : + rep_weights{ rep_weight_store_a } +{ +} diff --git a/nano/secure/ledger_cache.hpp b/nano/secure/ledger_cache.hpp index 28cac3dcd..34b86cb63 100644 --- a/nano/secure/ledger_cache.hpp +++ b/nano/secure/ledger_cache.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include @@ -10,6 +11,7 @@ namespace nano class ledger_cache { public: + explicit ledger_cache (nano::store::rep_weight & rep_weight_store_a); nano::rep_weights rep_weights; std::atomic cemented_count{ 0 }; std::atomic block_count{ 0 }; diff --git a/nano/secure/rep_weights.cpp b/nano/secure/rep_weights.cpp index dd69bbd65..c645bb1e0 100644 --- a/nano/secure/rep_weights.cpp +++ b/nano/secure/rep_weights.cpp @@ -1,30 +1,42 @@ #include #include +#include -void nano::rep_weights::representation_add (nano::account const & source_rep_a, nano::uint128_t const & amount_a) +nano::rep_weights::rep_weights (nano::store::rep_weight & rep_weight_store_a) : + rep_weight_store{ rep_weight_store_a } { - nano::lock_guard guard (mutex); - auto source_previous (get (source_rep_a)); - put (source_rep_a, source_previous + amount_a); } -void nano::rep_weights::representation_add_dual (nano::account const & source_rep_1, nano::uint128_t const & amount_1, nano::account const & source_rep_2, nano::uint128_t const & amount_2) +void nano::rep_weights::representation_add (store::write_transaction const & txn_a, nano::account const & source_rep_a, nano::uint128_t const & amount_a) +{ + auto weight{ rep_weight_store.get (txn_a, source_rep_a) }; + weight += amount_a; + nano::lock_guard guard (mutex); + rep_weight_store.put (txn_a, source_rep_a, weight); + put (source_rep_a, weight); +} + +void nano::rep_weights::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) { if (source_rep_1 != source_rep_2) { + auto rep_1_weight{ rep_weight_store.get (txn_a, source_rep_1) }; + auto rep_2_weight{ rep_weight_store.get (txn_a, source_rep_2) }; + rep_1_weight += amount_1; + rep_2_weight += amount_2; + rep_weight_store.put (txn_a, source_rep_1, rep_1_weight); + rep_weight_store.put (txn_a, source_rep_2, rep_2_weight); nano::lock_guard guard (mutex); - auto source_previous_1 (get (source_rep_1)); - put (source_rep_1, source_previous_1 + amount_1); - auto source_previous_2 (get (source_rep_2)); - put (source_rep_2, source_previous_2 + amount_2); + put (source_rep_1, rep_1_weight); + put (source_rep_2, rep_2_weight); } else { - representation_add (source_rep_1, amount_1 + amount_2); + representation_add (txn_a, source_rep_1, amount_1 + amount_2); } } -void nano::rep_weights::representation_put (nano::account const & account_a, nano::uint128_union const & representation_a) +void nano::rep_weights::representation_put (nano::account const & account_a, nano::uint128_t const & representation_a) { nano::lock_guard guard (mutex); put (account_a, representation_a); diff --git a/nano/secure/rep_weights.hpp b/nano/secure/rep_weights.hpp index e7716ee3b..7ee057146 100644 --- a/nano/secure/rep_weights.hpp +++ b/nano/secure/rep_weights.hpp @@ -12,21 +12,26 @@ namespace nano namespace store { class component; + class rep_weight; + class write_transaction; } class rep_weights { public: - void representation_add (nano::account const & source_rep_a, nano::uint128_t const & amount_a); - void representation_add_dual (nano::account const & source_rep_1, nano::uint128_t const & amount_1, nano::account const & source_rep_2, nano::uint128_t const & amount_2); + explicit rep_weights (nano::store::rep_weight & rep_weight_store_a); + 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; - void representation_put (nano::account const & account_a, nano::uint128_union const & representation_a); + /* Only use this method when loading rep weights from the database table */ + void representation_put (nano::account const & account_a, nano::uint128_t const & representation_a); std::unordered_map get_rep_amounts () const; void copy_from (rep_weights & other_a); private: mutable nano::mutex mutex; std::unordered_map rep_amounts; + nano::store::rep_weight & rep_weight_store; void put (nano::account const & account_a, nano::uint128_union const & representation_a); nano::uint128_t get (nano::account const & account_a) const; diff --git a/nano/store/component.cpp b/nano/store/component.cpp index 9e4b7e9db..bb6c78d51 100644 --- a/nano/store/component.cpp +++ b/nano/store/component.cpp @@ -39,6 +39,7 @@ void nano::store::component::initialize (store::write_transaction const & transa ledger_cache_a.final_votes_confirmation_canary = (constants.final_votes_canary_account == constants.genesis->account () && 1 >= constants.final_votes_canary_height); account.put (transaction_a, constants.genesis->account (), { hash_l, constants.genesis->account (), constants.genesis->hash (), std::numeric_limits::max (), nano::seconds_since_epoch (), 1, nano::epoch::epoch_0 }); ++ledger_cache_a.account_count; + rep_weight.put (transaction_a, constants.genesis->account (), std::numeric_limits::max ()); ledger_cache_a.rep_weights.representation_put (constants.genesis->account (), std::numeric_limits::max ()); frontier.put (transaction_a, hash_l, constants.genesis->account ()); } From 9f0157699bdecd01197785805e97307b757102cb Mon Sep 17 00:00:00 2001 From: Gustav Schauwecker Date: Tue, 12 Mar 2024 11:12:08 +0100 Subject: [PATCH 079/128] Extend ledger::migrate_lmdb_to_rocks_db() to copy rep_weights --- nano/secure/ledger.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index d22a7a124..7d031e2e7 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -1445,6 +1445,15 @@ bool nano::ledger::migrate_lmdb_to_rocksdb (std::filesystem::path const & data_p } }); + store.rep_weight.for_each_par ( + [&rocksdb_store] (store::read_transaction const & /*unused*/, auto i, auto n) { + for (; i != n; ++i) + { + auto rocksdb_transaction (rocksdb_store->tx_begin_write ({}, { nano::tables::rep_weights })); + rocksdb_store->rep_weight.put (rocksdb_transaction, i->first, i->second.number ()); + } + }); + store.frontier.for_each_par ( [&rocksdb_store] (store::read_transaction const & /*unused*/, auto i, auto n) { for (; i != n; ++i) From 69f147991c7b6c336a4df93336fe8dfb650a157b Mon Sep 17 00:00:00 2001 From: Gustav Schauwecker Date: Tue, 12 Mar 2024 13:07:51 +0100 Subject: [PATCH 080/128] Delete reps with weight 0 from rep_weight table and cache --- nano/core_test/ledger.cpp | 20 ++++++++++ nano/secure/rep_weights.cpp | 77 ++++++++++++++++++++++++++----------- nano/secure/rep_weights.hpp | 5 ++- 3 files changed, 78 insertions(+), 24 deletions(-) diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index e688ccff6..f6aecb4c8 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -668,6 +668,26 @@ TEST (ledger, representation_changes) ASSERT_EQ (2, rep_weights.representation_get (key1.pub)); } +TEST (ledger, delete_rep_weight_of_zero) +{ + auto store{ nano::test::make_store () }; + nano::rep_weights rep_weights{ store->rep_weight }; + auto txn{ store->tx_begin_write () }; + rep_weights.representation_add (txn, 1, 100); + rep_weights.representation_add_dual (txn, 2, 100, 3, 100); + ASSERT_EQ (3, rep_weights.size ()); + ASSERT_EQ (3, store->rep_weight.count (txn)); + + // set rep weights to 0 + rep_weights.representation_add (txn, 1, std::numeric_limits::max () - 99); + ASSERT_EQ (2, rep_weights.size ()); + ASSERT_EQ (2, store->rep_weight.count (txn)); + + rep_weights.representation_add_dual (txn, 2, std::numeric_limits::max () - 99, 3, std::numeric_limits::max () - 99); + ASSERT_EQ (0, rep_weights.size ()); + ASSERT_EQ (0, 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 c645bb1e0..29a10d9cf 100644 --- a/nano/secure/rep_weights.cpp +++ b/nano/secure/rep_weights.cpp @@ -7,39 +7,39 @@ nano::rep_weights::rep_weights (nano::store::rep_weight & rep_weight_store_a) : { } -void nano::rep_weights::representation_add (store::write_transaction const & txn_a, nano::account const & source_rep_a, nano::uint128_t const & amount_a) +void nano::rep_weights::representation_add (store::write_transaction const & txn_a, nano::account const & rep_a, nano::uint128_t const & amount_a) { - auto weight{ rep_weight_store.get (txn_a, source_rep_a) }; - weight += amount_a; + auto previous_weight{ rep_weight_store.get (txn_a, rep_a) }; + auto new_weight = previous_weight + amount_a; + put_store (txn_a, rep_a, previous_weight, new_weight); nano::lock_guard guard (mutex); - rep_weight_store.put (txn_a, source_rep_a, weight); - put (source_rep_a, weight); + put_cache (rep_a, new_weight); } -void nano::rep_weights::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) +void nano::rep_weights::representation_add_dual (store::write_transaction const & txn_a, nano::account const & rep_1, nano::uint128_t const & amount_1, nano::account const & rep_2, nano::uint128_t const & amount_2) { - if (source_rep_1 != source_rep_2) + if (rep_1 != rep_2) { - auto rep_1_weight{ rep_weight_store.get (txn_a, source_rep_1) }; - auto rep_2_weight{ rep_weight_store.get (txn_a, source_rep_2) }; - rep_1_weight += amount_1; - rep_2_weight += amount_2; - rep_weight_store.put (txn_a, source_rep_1, rep_1_weight); - rep_weight_store.put (txn_a, source_rep_2, rep_2_weight); + auto previous_weight_1{ rep_weight_store.get (txn_a, rep_1) }; + auto previous_weight_2{ rep_weight_store.get (txn_a, rep_2) }; + auto new_weight_1 = previous_weight_1 + amount_1; + auto new_weight_2 = previous_weight_2 + amount_2; + put_store (txn_a, rep_1, previous_weight_1, new_weight_1); + put_store (txn_a, rep_2, previous_weight_2, new_weight_2); nano::lock_guard guard (mutex); - put (source_rep_1, rep_1_weight); - put (source_rep_2, rep_2_weight); + put_cache (rep_1, new_weight_1); + put_cache (rep_2, new_weight_2); } else { - representation_add (txn_a, source_rep_1, amount_1 + amount_2); + representation_add (txn_a, rep_1, amount_1 + amount_2); } } void nano::rep_weights::representation_put (nano::account const & account_a, nano::uint128_t const & representation_a) { nano::lock_guard guard (mutex); - put (account_a, representation_a); + put_cache (account_a, representation_a); } nano::uint128_t nano::rep_weights::representation_get (nano::account const & account_a) const @@ -62,21 +62,46 @@ void nano::rep_weights::copy_from (nano::rep_weights & other_a) for (auto const & entry : other_a.rep_amounts) { auto prev_amount (get (entry.first)); - put (entry.first, prev_amount + entry.second); + put_cache (entry.first, prev_amount + entry.second); } } -void nano::rep_weights::put (nano::account const & account_a, nano::uint128_union const & representation_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); - auto amount = representation_a.number (); - if (it != rep_amounts.end ()) + if (representation_a.is_zero ()) { - it->second = amount; + if (it != rep_amounts.end ()) + { + rep_amounts.erase (it); + } } else { - rep_amounts.emplace (account_a, amount); + auto amount = representation_a.number (); + if (it != rep_amounts.end ()) + { + it->second = amount; + } + else + { + rep_amounts.emplace (account_a, amount); + } + } +} + +void nano::rep_weights::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) +{ + if (new_weight_a.is_zero ()) + { + if (!previous_weight_a.is_zero ()) + { + rep_weight_store.del (txn_a, rep_a); + } + } + else + { + rep_weight_store.put (txn_a, rep_a, new_weight_a); } } @@ -93,6 +118,12 @@ nano::uint128_t nano::rep_weights::get (nano::account const & account_a) const } } +std::size_t nano::rep_weights::size () const +{ + nano::lock_guard guard (mutex); + return rep_amounts.size (); +} + std::unique_ptr nano::collect_container_info (nano::rep_weights const & rep_weights, std::string const & name) { size_t rep_amounts_count; diff --git a/nano/secure/rep_weights.hpp b/nano/secure/rep_weights.hpp index 7ee057146..52fb0013f 100644 --- a/nano/secure/rep_weights.hpp +++ b/nano/secure/rep_weights.hpp @@ -26,13 +26,16 @@ public: /* Only use this method when loading rep weights from the database table */ void representation_put (nano::account const & account_a, nano::uint128_t const & representation_a); std::unordered_map get_rep_amounts () const; + /* Only use this method when loading rep weights from the database table */ void copy_from (rep_weights & other_a); + size_t size () const; private: mutable nano::mutex mutex; std::unordered_map rep_amounts; nano::store::rep_weight & rep_weight_store; - void put (nano::account const & account_a, nano::uint128_union const & representation_a); + 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; friend std::unique_ptr collect_container_info (rep_weights const &, std::string const &); From f99ba3e852e4791f5c13e07f91262de7f44591c1 Mon Sep 17 00:00:00 2001 From: Gustav Schauwecker Date: Tue, 12 Mar 2024 14:49:45 +0100 Subject: [PATCH 081/128] Add missing RocksDB table locks for rep_weights table --- nano/node/blockprocessor.cpp | 2 +- nano/node/node.cpp | 4 ++-- nano/test_common/testutil.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index 978f4f62f..8541484da 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -282,7 +282,7 @@ auto nano::block_processor::process_batch (nano::unique_lock & lock processed_batch_t processed; auto scoped_write_guard = write_database_queue.wait (nano::writer::process_batch); - auto transaction (node.store.tx_begin_write ({ tables::accounts, tables::blocks, tables::frontiers, tables::pending })); + auto transaction (node.store.tx_begin_write ({ tables::accounts, tables::blocks, tables::frontiers, tables::pending, tables::rep_weights })); nano::timer timer_l; lock_a.lock (); diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 054e3669f..aa1b11b77 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -354,7 +354,7 @@ nano::node::node (boost::asio::io_context & io_ctx_a, std::filesystem::path cons if (!is_initialized && !flags.read_only) { - auto const transaction (store.tx_begin_write ({ tables::accounts, tables::blocks, tables::confirmation_height, tables::frontiers })); + auto const transaction (store.tx_begin_write ({ tables::accounts, tables::blocks, tables::confirmation_height, tables::frontiers, tables::rep_weights })); // Store was empty meaning we just created it, add the genesis block store.initialize (transaction, ledger.cache, ledger.constants); } @@ -558,7 +558,7 @@ void nano::node::process_active (std::shared_ptr const & incoming) nano::block_status nano::node::process (std::shared_ptr block) { - auto const transaction = store.tx_begin_write ({ tables::accounts, tables::blocks, tables::frontiers, tables::pending }); + auto const transaction = store.tx_begin_write ({ tables::accounts, tables::blocks, tables::frontiers, tables::pending, tables::rep_weights }); return process (transaction, block); } diff --git a/nano/test_common/testutil.cpp b/nano/test_common/testutil.cpp index ce80cfeae..7cd682220 100644 --- a/nano/test_common/testutil.cpp +++ b/nano/test_common/testutil.cpp @@ -63,7 +63,7 @@ nano::account nano::test::random_account () bool nano::test::process (nano::node & node, std::vector> blocks) { - auto const transaction = node.store.tx_begin_write ({ tables::accounts, tables::blocks, tables::frontiers, tables::pending }); + auto const transaction = node.store.tx_begin_write ({ tables::accounts, tables::blocks, tables::frontiers, tables::pending, tables::rep_weights }); for (auto & block : blocks) { auto result = node.process (transaction, block); From ef64ecb799d131ce5bd2532a2f809799234805a7 Mon Sep 17 00:00:00 2001 From: Gustav Schauwecker Date: Fri, 22 Mar 2024 13:56:25 +0100 Subject: [PATCH 082/128] Address issues from code review --- nano/core_test/ledger.cpp | 6 ++++-- nano/secure/account_info.hpp | 3 +-- nano/secure/ledger.cpp | 2 +- nano/secure/rep_weights.cpp | 8 ++++---- nano/secure/rep_weights.hpp | 5 +---- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index f6aecb4c8..df55af7c1 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -1,3 +1,5 @@ +#include "nano/lib/numbers.hpp" + #include #include #include @@ -679,11 +681,11 @@ TEST (ledger, delete_rep_weight_of_zero) ASSERT_EQ (3, store->rep_weight.count (txn)); // set rep weights to 0 - rep_weights.representation_add (txn, 1, std::numeric_limits::max () - 99); + rep_weights.representation_add (txn, 1, nano::uint128_t{ 0 } - 100); ASSERT_EQ (2, rep_weights.size ()); ASSERT_EQ (2, store->rep_weight.count (txn)); - rep_weights.representation_add_dual (txn, 2, std::numeric_limits::max () - 99, 3, std::numeric_limits::max () - 99); + rep_weights.representation_add_dual (txn, 2, nano::uint128_t{ 0 } - 100, 3, nano::uint128_t{ 0 } - 100); ASSERT_EQ (0, rep_weights.size ()); ASSERT_EQ (0, store->rep_weight.count (txn)); } diff --git a/nano/secure/account_info.hpp b/nano/secure/account_info.hpp index bd7a7b0cc..ee850d8d2 100644 --- a/nano/secure/account_info.hpp +++ b/nano/secure/account_info.hpp @@ -31,8 +31,7 @@ public: }; /** - * Account info as of DB version 22. - * This class protects the DB upgrades from future changes of the account_info class. + * This is a snapshot of the account_info table at v22 which needs to be read for the v22 to v23 upgrade */ class account_info_v22 final { diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 7d031e2e7..bf644f822 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -1603,6 +1603,6 @@ std::unique_ptr nano::collect_container_info (le auto sizeof_element = sizeof (decltype (ledger.bootstrap_weights)::value_type); auto composite = std::make_unique (name); composite->add_component (std::make_unique (container_info{ "bootstrap_weights", count, sizeof_element })); - composite->add_component (collect_container_info (ledger.cache.rep_weights, "rep_weights")); + composite->add_component (ledger.cache.rep_weights.collect_container_info ("rep_weights")); return composite; } diff --git a/nano/secure/rep_weights.cpp b/nano/secure/rep_weights.cpp index 29a10d9cf..7be8915cf 100644 --- a/nano/secure/rep_weights.cpp +++ b/nano/secure/rep_weights.cpp @@ -124,15 +124,15 @@ std::size_t nano::rep_weights::size () const return rep_amounts.size (); } -std::unique_ptr nano::collect_container_info (nano::rep_weights const & rep_weights, std::string const & name) +std::unique_ptr nano::rep_weights::collect_container_info (std::string const & name) { size_t rep_amounts_count; { - nano::lock_guard guard (rep_weights.mutex); - rep_amounts_count = rep_weights.rep_amounts.size (); + nano::lock_guard guard (mutex); + rep_amounts_count = rep_amounts.size (); } - auto sizeof_element = sizeof (decltype (rep_weights.rep_amounts)::value_type); + auto sizeof_element = sizeof (decltype (rep_amounts)::value_type); auto composite = std::make_unique (name); composite->add_component (std::make_unique (container_info{ "rep_amounts", rep_amounts_count, sizeof_element })); return composite; diff --git a/nano/secure/rep_weights.hpp b/nano/secure/rep_weights.hpp index 52fb0013f..e29f4e0ba 100644 --- a/nano/secure/rep_weights.hpp +++ b/nano/secure/rep_weights.hpp @@ -29,6 +29,7 @@ public: /* Only use this method when loading rep weights from the database table */ void copy_from (rep_weights & other_a); size_t size () const; + std::unique_ptr collect_container_info (std::string const &); private: mutable nano::mutex mutex; @@ -37,9 +38,5 @@ private: 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; - - friend std::unique_ptr collect_container_info (rep_weights const &, std::string const &); }; - -std::unique_ptr collect_container_info (rep_weights const &, std::string const &); } From dc783a79716682ee52bbc7050387d2b490970bbc Mon Sep 17 00:00:00 2001 From: Gustav Schauwecker Date: Tue, 12 Mar 2024 13:32:49 +0100 Subject: [PATCH 083/128] 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 df55af7c1..22e41d9de 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 7be8915cf..632ace73a 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 e29f4e0ba..214e22b8e 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 @@ private: 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 084/128] 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 9e2a5cbe8..7ea1846f2 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 f755eb8ac..4da56adc2 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 aa1b11b77..8c0ca258b 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 16f3c897b..c240c583c 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 cf778df4e..b93611aca 100644 --- a/nano/node/nodeconfig.hpp +++ b/nano/node/nodeconfig.hpp @@ -64,6 +64,11 @@ public: 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 bf644f822..643e47bc0 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 6c325a029..6293ac1b6 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 5076e1cc1..16c46c0f5 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 34b86cb63..12ebc9af2 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 f4522c62b..334f77b4e 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 085/128] 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 1d98e73cb..0624c1782 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 8c0ca258b..abbc2e180 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 864ca4f01..a160855dc 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 6293ac1b6..ecd2a2627 100644 --- a/nano/secure/ledger.hpp +++ b/nano/secure/ledger.hpp @@ -53,9 +53,16 @@ public: 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; From 95141130ee058f90b360a2f4627d46bdb7506e57 Mon Sep 17 00:00:00 2001 From: clemahieu Date: Fri, 22 Mar 2024 18:52:32 +0000 Subject: [PATCH 086/128] Remove debug_assert for a tautology which was causing a warning. uint8_t can't be larger than 256 (#4517) --- nano/node/messages.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/nano/node/messages.cpp b/nano/node/messages.cpp index b4a19784f..39665a4d3 100644 --- a/nano/node/messages.cpp +++ b/nano/node/messages.cpp @@ -133,7 +133,6 @@ void nano::message_header::count_v2_set (uint8_t count) { debug_assert (type == nano::message_type::confirm_ack || type == nano::message_type::confirm_req); debug_assert (flag_test (confirm_v2_flag)); // Only valid for v2 - debug_assert (count < 256); // Max 8 bits extensions &= ~(count_v2_mask_left | count_v2_mask_right); From 31f7f0acb36c81d7947269a22180c59ec598cbed Mon Sep 17 00:00:00 2001 From: clemahieu Date: Fri, 22 Mar 2024 18:52:43 +0000 Subject: [PATCH 087/128] Fix missing vote_code enumeration for websockets. (#4516) --- nano/node/websocket.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nano/node/websocket.cpp b/nano/node/websocket.cpp index 1cebbf750..464c170cd 100644 --- a/nano/node/websocket.cpp +++ b/nano/node/websocket.cpp @@ -841,6 +841,9 @@ nano::websocket::message nano::websocket::message_builder::vote_received (std::s case nano::vote_code::indeterminate: vote_type = "indeterminate"; break; + case nano::vote_code::ignored: + vote_type = "ignored"; + break; case nano::vote_code::invalid: debug_assert (false); break; From 02c31f62c26a313e620a50ec693525139e35fd16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 14 Mar 2024 18:55:23 +0100 Subject: [PATCH 088/128] Separate `vote_cache_entry` class --- nano/node/vote_cache.cpp | 22 +++++----- nano/node/vote_cache.hpp | 90 ++++++++++++++++++++-------------------- 2 files changed, 57 insertions(+), 55 deletions(-) diff --git a/nano/node/vote_cache.cpp b/nano/node/vote_cache.cpp index 4edab3149..4df5e5621 100644 --- a/nano/node/vote_cache.cpp +++ b/nano/node/vote_cache.cpp @@ -4,15 +4,15 @@ #include /* - * entry + * entvote_cache_entryry */ -nano::vote_cache::entry::entry (const nano::block_hash & hash) : +nano::vote_cache_entry::vote_cache_entry (const nano::block_hash & hash) : hash_m{ hash } { } -bool nano::vote_cache::entry::vote (const nano::account & representative, const uint64_t & timestamp, const nano::uint128_t & rep_weight, std::size_t max_voters) +bool nano::vote_cache_entry::vote (const nano::account & representative, const uint64_t & timestamp, const nano::uint128_t & rep_weight, std::size_t max_voters) { bool updated = vote_impl (representative, timestamp, rep_weight, max_voters); if (updated) @@ -22,7 +22,7 @@ bool nano::vote_cache::entry::vote (const nano::account & representative, const return updated; } -bool nano::vote_cache::entry::vote_impl (const nano::account & representative, const uint64_t & timestamp, const nano::uint128_t & rep_weight, std::size_t max_voters) +bool nano::vote_cache_entry::vote_impl (const nano::account & representative, const uint64_t & timestamp, const nano::uint128_t & rep_weight, std::size_t max_voters) { auto existing = std::find_if (voters_m.begin (), voters_m.end (), [&representative] (auto const & item) { return item.representative == representative; }); if (existing != voters_m.end ()) @@ -64,7 +64,7 @@ bool nano::vote_cache::entry::vote_impl (const nano::account & representative, c } } -std::size_t nano::vote_cache::entry::fill (std::shared_ptr const & election) const +std::size_t nano::vote_cache_entry::fill (std::shared_ptr const & election) const { std::size_t inserted = 0; for (const auto & entry : voters_m) @@ -78,32 +78,32 @@ std::size_t nano::vote_cache::entry::fill (std::shared_ptr const return inserted; } -std::size_t nano::vote_cache::entry::size () const +std::size_t nano::vote_cache_entry::size () const { return voters_m.size (); } -nano::block_hash nano::vote_cache::entry::hash () const +nano::block_hash nano::vote_cache_entry::hash () const { return hash_m; } -nano::uint128_t nano::vote_cache::entry::tally () const +nano::uint128_t nano::vote_cache_entry::tally () const { return tally_m; } -nano::uint128_t nano::vote_cache::entry::final_tally () const +nano::uint128_t nano::vote_cache_entry::final_tally () const { return final_tally_m; } -std::vector nano::vote_cache::entry::voters () const +std::vector nano::vote_cache_entry::voters () const { return voters_m; } -std::chrono::steady_clock::time_point nano::vote_cache::entry::last_vote () const +std::chrono::steady_clock::time_point nano::vote_cache_entry::last_vote () const { return last_vote_m; } diff --git a/nano/node/vote_cache.hpp b/nano/node/vote_cache.hpp index 34f84d98b..fa1bcdcdc 100644 --- a/nano/node/vote_cache.hpp +++ b/nano/node/vote_cache.hpp @@ -41,53 +41,55 @@ public: std::chrono::seconds age_cutoff{ 5 * 60 }; }; +/** + * Stores votes associated with a single block hash + */ +class vote_cache_entry final +{ +public: + struct voter_entry + { + nano::account representative; + uint64_t timestamp; + }; + +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 (nano::account const & representative, uint64_t const & timestamp, nano::uint128_t const & rep_weight, std::size_t max_voters); + + /** + * Inserts votes stored in this entry into an election + */ + std::size_t fill (std::shared_ptr const & election) const; + + std::size_t size () const; + nano::block_hash hash () const; + nano::uint128_t tally () const; + nano::uint128_t final_tally () const; + std::vector voters () const; + std::chrono::steady_clock::time_point last_vote () const; + +private: + bool vote_impl (nano::account const & representative, uint64_t const & timestamp, nano::uint128_t const & rep_weight, std::size_t max_voters); + + nano::block_hash const hash_m; + std::vector voters_m; + + nano::uint128_t tally_m{ 0 }; + nano::uint128_t final_tally_m{ 0 }; + + std::chrono::steady_clock::time_point last_vote_m{}; +}; + class vote_cache final { public: - /** - * Stores votes associated with a single block hash - */ - class entry final - { - public: - struct voter_entry - { - nano::account representative; - uint64_t timestamp; - }; - - public: - explicit 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 (nano::account const & representative, uint64_t const & timestamp, nano::uint128_t const & rep_weight, std::size_t max_voters); - - /** - * Inserts votes stored in this entry into an election - */ - std::size_t fill (std::shared_ptr const & election) const; - - std::size_t size () const; - nano::block_hash hash () const; - nano::uint128_t tally () const; - nano::uint128_t final_tally () const; - std::vector voters () const; - std::chrono::steady_clock::time_point last_vote () const; - - private: - bool vote_impl (nano::account const & representative, uint64_t const & timestamp, nano::uint128_t const & rep_weight, std::size_t max_voters); - - nano::block_hash const hash_m; - std::vector voters_m; - - nano::uint128_t tally_m{ 0 }; - nano::uint128_t final_tally_m{ 0 }; - - std::chrono::steady_clock::time_point last_vote_m{}; - }; + using entry = vote_cache_entry; public: explicit vote_cache (vote_cache_config const &, nano::stats &); From f67ca0d40c70bc53a67de846b0dae5228a22fcf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Fri, 15 Mar 2024 18:56:51 +0100 Subject: [PATCH 089/128] Increase age cutoff --- nano/node/vote_cache.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nano/node/vote_cache.hpp b/nano/node/vote_cache.hpp index fa1bcdcdc..2b9893a3c 100644 --- a/nano/node/vote_cache.hpp +++ b/nano/node/vote_cache.hpp @@ -38,7 +38,7 @@ public: public: std::size_t max_size{ 1024 * 128 }; std::size_t max_voters{ 128 }; - std::chrono::seconds age_cutoff{ 5 * 60 }; + std::chrono::seconds age_cutoff{ 15 * 60 }; }; /** From 41c83a77e1483288b4cd6f514652cd9f8d3df026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Fri, 15 Mar 2024 19:24:56 +0100 Subject: [PATCH 090/128] `vote.is_final()` helper --- nano/secure/vote.cpp | 5 +++++ nano/secure/vote.hpp | 1 + 2 files changed, 6 insertions(+) diff --git a/nano/secure/vote.cpp b/nano/secure/vote.cpp index 28002f25f..296886634 100644 --- a/nano/secure/vote.cpp +++ b/nano/secure/vote.cpp @@ -137,6 +137,11 @@ std::chrono::milliseconds nano::vote::duration () const return std::chrono::milliseconds{ 1u << (duration_bits () + 4) }; } +bool nano::vote::is_final () const +{ + return is_final_timestamp (timestamp_m); +} + void nano::vote::serialize_json (boost::property_tree::ptree & tree) const { tree.put ("account", account.to_account ()); diff --git a/nano/secure/vote.hpp b/nano/secure/vote.hpp index 7c29d5384..0737acf00 100644 --- a/nano/secure/vote.hpp +++ b/nano/secure/vote.hpp @@ -47,6 +47,7 @@ public: uint64_t timestamp () const; uint8_t duration_bits () const; std::chrono::milliseconds duration () const; + bool is_final () const; static uint64_t constexpr timestamp_mask = { 0xffff'ffff'ffff'fff0ULL }; static nano::seconds_t constexpr timestamp_max = { 0xffff'ffff'ffff'fff0ULL }; From b00c757bc73433f0f0229f4c09ec75b7979ac1b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Fri, 15 Mar 2024 19:40:17 +0100 Subject: [PATCH 091/128] Store full votes in vote cache --- nano/node/active_transactions.cpp | 34 +++----- nano/node/active_transactions.hpp | 5 -- nano/node/election.cpp | 13 ++- nano/node/vote_cache.cpp | 140 ++++++++++++++++-------------- nano/node/vote_cache.hpp | 45 ++++++---- nano/node/vote_processor.cpp | 10 +-- 6 files changed, 133 insertions(+), 114 deletions(-) diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 9603721af..e79b08a2f 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -368,9 +368,11 @@ void nano::active_transactions::trim () nano::election_insertion_result nano::active_transactions::insert (std::shared_ptr const & block_a, nano::election_behavior election_behavior_a) { - nano::unique_lock lock{ mutex }; debug_assert (block_a); debug_assert (block_a->has_sideband ()); + + nano::unique_lock lock{ mutex }; + nano::election_insertion_result result; if (stopped) @@ -413,15 +415,16 @@ nano::election_insertion_result nano::active_transactions::insert (std::shared_p result.election = existing->election; } - lock.unlock (); // end of critical section + lock.unlock (); if (result.inserted) { release_assert (result.election); - if (auto const cache = node.vote_cache.find (hash); cache) + auto cached = node.vote_cache.find (hash); + for (auto const & cached_vote : cached) { - cache->fill (result.election); + vote (cached_vote); } node.observers.active_started.notify (hash); @@ -433,7 +436,9 @@ nano::election_insertion_result nano::active_transactions::insert (std::shared_p { result.election->broadcast_vote (); } + trim (); + return result; } @@ -469,12 +474,6 @@ std::unordered_map nano::active_transactions: } } - // Process inactive votes outside of the critical section - for (auto & hash : inactive) - { - add_vote_cache (hash, vote); - } - if (!process.empty ()) { bool processed = false; @@ -609,24 +608,19 @@ bool nano::active_transactions::publish (std::shared_ptr const & bl lock.lock (); blocks.emplace (block_a->hash (), election); lock.unlock (); - if (auto const cache = node.vote_cache.find (block_a->hash ()); cache) + + auto cached = node.vote_cache.find (block_a->hash ()); + for (auto const & cached_vote : cached) { - cache->fill (election); + vote (cached_vote); } + node.stats.inc (nano::stat::type::active, nano::stat::detail::election_block_conflict); } } return result; } -void nano::active_transactions::add_vote_cache (nano::block_hash const & hash, std::shared_ptr const vote) -{ - if (node.ledger.weight (vote->account) > node.minimum_principal_weight ()) - { - node.vote_cache.vote (hash, vote); - } -} - std::size_t nano::active_transactions::election_winner_details_size () { nano::lock_guard guard{ election_winner_details_mutex }; diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index a0eef10b1..cb29b6bc9 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -199,11 +199,6 @@ private: nano::stat::type completion_type (nano::election const & election) const; // Returns a list of elections sorted by difficulty, mutex must be locked std::vector> list_active_impl (std::size_t) const; - /** - * Checks if vote passes minimum representative weight threshold and adds it to inactive vote cache - * TODO: Should be moved to `vote_cache` class - */ - void add_vote_cache (nano::block_hash const & hash, std::shared_ptr vote); void activate_successors (nano::store::read_transaction const & transaction, std::shared_ptr const & block); void notify_observers (nano::store::read_transaction const & transaction, nano::election_status const & status, std::vector const & votes); diff --git a/nano/node/election.cpp b/nano/node/election.cpp index f67806378..ca385ae07 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -644,11 +644,22 @@ bool nano::election::replace_by_weight (nano::unique_lock & lock_a, sorted.reserve (last_tally.size ()); std::copy (last_tally.begin (), last_tally.end (), std::back_inserter (sorted)); lock_a.unlock (); + // Sort in ascending order std::sort (sorted.begin (), sorted.end (), [] (auto const & left, auto const & right) { return left.second < right.second; }); + + auto votes_tally = [this] (std::vector> const & votes) { + nano::uint128_t result{ 0 }; + for (auto const & vote : votes) + { + result += node.ledger.weight (vote->account); + } + return result; + }; + // Replace if lowest tally is below inactive cache new block weight auto inactive_existing = node.vote_cache.find (hash_a); - auto inactive_tally = inactive_existing ? inactive_existing->tally () : 0; + auto inactive_tally = votes_tally (inactive_existing); if (inactive_tally > 0 && sorted.size () < max_blocks) { // If count of tally items is less than 10, remove any block without tally diff --git a/nano/node/vote_cache.cpp b/nano/node/vote_cache.cpp index 4df5e5621..e4e36a96b 100644 --- a/nano/node/vote_cache.cpp +++ b/nano/node/vote_cache.cpp @@ -3,6 +3,8 @@ #include #include +#include + /* * entvote_cache_entryry */ @@ -12,9 +14,9 @@ nano::vote_cache_entry::vote_cache_entry (const nano::block_hash & hash) : { } -bool nano::vote_cache_entry::vote (const nano::account & representative, const uint64_t & timestamp, const nano::uint128_t & rep_weight, std::size_t max_voters) +bool nano::vote_cache_entry::vote (std::shared_ptr const & vote, const nano::uint128_t & rep_weight, std::size_t max_voters) { - bool updated = vote_impl (representative, timestamp, rep_weight, max_voters); + bool updated = vote_impl (vote, rep_weight, max_voters); if (updated) { last_vote_m = std::chrono::steady_clock::now (); @@ -22,21 +24,21 @@ bool nano::vote_cache_entry::vote (const nano::account & representative, const u return updated; } -bool nano::vote_cache_entry::vote_impl (const nano::account & representative, const uint64_t & timestamp, const nano::uint128_t & rep_weight, std::size_t max_voters) +bool nano::vote_cache_entry::vote_impl (std::shared_ptr const & vote, const nano::uint128_t & rep_weight, std::size_t max_voters) { - auto existing = std::find_if (voters_m.begin (), voters_m.end (), [&representative] (auto const & item) { return item.representative == representative; }); - if (existing != voters_m.end ()) + auto const representative = vote->account; + + if (auto existing = voters.find (representative); existing != voters.end ()) { // We already have a vote from this rep // Update timestamp if newer but tally remains unchanged as we already counted this rep weight // It is not essential to keep tally up to date if rep voting weight changes, elections do tally calculations independently, so in the worst case scenario only our queue ordering will be a bit off - if (timestamp > existing->timestamp) + if (vote->timestamp () > existing->vote->timestamp ()) { - existing->timestamp = timestamp; - if (nano::vote::is_final_timestamp (timestamp)) - { - final_tally_m += rep_weight; - } + voters.modify (existing, [&vote, &rep_weight] (auto & existing) { + existing.vote = vote; + existing.weight = rep_weight; + }); return true; } else @@ -46,15 +48,31 @@ bool nano::vote_cache_entry::vote_impl (const nano::account & representative, co } else { - // Vote from an unseen representative, add to list and update tally - if (voters_m.size () < max_voters) - { - voters_m.push_back ({ representative, timestamp }); - tally_m += rep_weight; - if (nano::vote::is_final_timestamp (timestamp)) + auto should_add = [&, this] () { + if (voters.size () < max_voters) { - final_tally_m += rep_weight; + return true; } + else + { + release_assert (!voters.empty ()); + auto const & min_weight = voters.get ().begin ()->weight; + return rep_weight > min_weight; + } + }; + + // Vote from a new representative, add it to the list and update tally + if (should_add ()) + { + voters.insert ({ representative, rep_weight, vote }); + + // If we have reached the maximum number of voters, remove the lowest weight voter + if (voters.size () >= max_voters) + { + release_assert (!voters.empty ()); + voters.get ().erase (voters.get ().begin ()); + } + return true; } else @@ -64,23 +82,9 @@ bool nano::vote_cache_entry::vote_impl (const nano::account & representative, co } } -std::size_t nano::vote_cache_entry::fill (std::shared_ptr const & election) const -{ - std::size_t inserted = 0; - for (const auto & entry : voters_m) - { - auto result = election->vote (entry.representative, entry.timestamp, hash_m, nano::vote_source::cache); - if (result == nano::vote_code::vote) - { - inserted++; - } - } - return inserted; -} - std::size_t nano::vote_cache_entry::size () const { - return voters_m.size (); + return voters.size (); } nano::block_hash nano::vote_cache_entry::hash () const @@ -90,17 +94,22 @@ nano::block_hash nano::vote_cache_entry::hash () const nano::uint128_t nano::vote_cache_entry::tally () const { - return tally_m; + return std::accumulate (voters.begin (), voters.end (), nano::uint128_t{ 0 }, [] (auto sum, auto const & item) { + return sum + item.weight; + }); } nano::uint128_t nano::vote_cache_entry::final_tally () const { - return final_tally_m; + return std::accumulate (voters.begin (), voters.end (), nano::uint128_t{ 0 }, [] (auto sum, auto const & item) { + return sum + (item.vote->is_final () ? item.weight : 0); + }); } -std::vector nano::vote_cache_entry::voters () const +std::vector> nano::vote_cache_entry::votes () const { - return voters_m; + auto r = voters | std::views::transform ([] (auto const & item) { return item.vote; }); + return { r.begin (), r.end () }; } std::chrono::steady_clock::time_point nano::vote_cache_entry::last_vote () const @@ -119,39 +128,42 @@ nano::vote_cache::vote_cache (vote_cache_config const & config_a, nano::stats & { } -void nano::vote_cache::vote (const nano::block_hash & hash, const std::shared_ptr vote) +void nano::vote_cache::vote (std::shared_ptr const & vote, std::function const & filter) { - // Assert that supplied hash corresponds to a one of the hashes stored in vote - debug_assert (std::find (vote->hashes.begin (), vote->hashes.end (), hash) != vote->hashes.end ()); - auto const representative = vote->account; auto const timestamp = vote->timestamp (); auto const rep_weight = rep_weight_query (representative); - nano::unique_lock lock{ mutex }; + nano::lock_guard lock{ mutex }; - auto & cache_by_hash = cache.get (); - if (auto existing = cache_by_hash.find (hash); existing != cache_by_hash.end ()) + for (auto const & hash : vote->hashes) { - stats.inc (nano::stat::type::vote_cache, nano::stat::detail::update); - - cache_by_hash.modify (existing, [this, &representative, ×tamp, &rep_weight] (entry & ent) { - ent.vote (representative, timestamp, rep_weight, config.max_voters); - }); - } - else - { - stats.inc (nano::stat::type::vote_cache, nano::stat::detail::insert); - - entry cache_entry{ hash }; - cache_entry.vote (representative, timestamp, rep_weight, config.max_voters); - - cache.get ().insert (cache_entry); - - // When cache overflown remove the oldest entry - if (cache.size () > config.max_size) + if (!filter (hash)) { - cache.get ().pop_front (); + continue; + } + + if (auto existing = cache.find (hash); existing != cache.end ()) + { + stats.inc (nano::stat::type::vote_cache, nano::stat::detail::update); + + cache.modify (existing, [this, &vote, &rep_weight] (entry & ent) { + ent.vote (vote, rep_weight, config.max_voters); + }); + } + else + { + stats.inc (nano::stat::type::vote_cache, nano::stat::detail::insert); + + entry cache_entry{ hash }; + cache_entry.vote (vote, rep_weight, config.max_voters); + cache.insert (cache_entry); + + // Remove the oldest entry if we have reached the capacity limit + if (cache.size () > config.max_size) + { + cache.get ().pop_front (); + } } } } @@ -168,14 +180,14 @@ std::size_t nano::vote_cache::size () const return cache.size (); } -std::optional nano::vote_cache::find (const nano::block_hash & hash) const +std::vector> nano::vote_cache::find (const nano::block_hash & hash) const { nano::lock_guard lock{ mutex }; auto & cache_by_hash = cache.get (); if (auto existing = cache_by_hash.find (hash); existing != cache_by_hash.end ()) { - return *existing; + return existing->votes (); } return {}; } diff --git a/nano/node/vote_cache.hpp b/nano/node/vote_cache.hpp index 2b9893a3c..3208e9d8a 100644 --- a/nano/node/vote_cache.hpp +++ b/nano/node/vote_cache.hpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -15,6 +16,7 @@ #include #include #include +#include #include namespace mi = boost::multi_index; @@ -46,11 +48,12 @@ public: */ class vote_cache_entry final { -public: +private: struct voter_entry { nano::account representative; - uint64_t timestamp; + nano::uint128_t weight; + std::shared_ptr vote; }; public: @@ -60,29 +63,35 @@ public: * 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 (nano::account const & representative, uint64_t const & timestamp, nano::uint128_t const & rep_weight, std::size_t max_voters); - - /** - * Inserts votes stored in this entry into an election - */ - std::size_t fill (std::shared_ptr const & election) const; + bool vote (std::shared_ptr const & vote, nano::uint128_t const & rep_weight, std::size_t max_voters); std::size_t size () const; nano::block_hash hash () const; nano::uint128_t tally () const; nano::uint128_t final_tally () const; - std::vector voters () const; + std::vector> votes () const; std::chrono::steady_clock::time_point last_vote () const; private: - bool vote_impl (nano::account const & representative, uint64_t const & timestamp, nano::uint128_t const & rep_weight, std::size_t max_voters); + bool vote_impl (std::shared_ptr const & vote, nano::uint128_t const & rep_weight, std::size_t max_voters); + + // clang-format off + class tag_representative {}; + class tag_weight {}; + // clang-format on + + // clang-format off + using ordered_voters = boost::multi_index_container, + mi::member>, + mi::ordered_non_unique, + mi::member> + >>; + // clang-format on + ordered_voters voters; nano::block_hash const hash_m; - std::vector voters_m; - - nano::uint128_t tally_m{ 0 }; - nano::uint128_t final_tally_m{ 0 }; - std::chrono::steady_clock::time_point last_vote_m{}; }; @@ -97,12 +106,12 @@ public: /** * Adds a new vote to cache */ - void vote (nano::block_hash const & hash, std::shared_ptr vote); + void vote (std::shared_ptr const & vote, std::function const & filter); /** * Tries to find an entry associated with block hash */ - std::optional find (nano::block_hash const & hash) const; + std::vector> find (nano::block_hash const & hash) const; /** * Removes an entry associated with block hash, does nothing if entry does not exist @@ -154,9 +163,9 @@ private: // clang-format off using ordered_cache = boost::multi_index_container>, mi::hashed_unique, mi::const_mem_fun>, + mi::sequenced>, mi::ordered_non_unique, mi::const_mem_fun, std::greater<>> // DESC >>; diff --git a/nano/node/vote_processor.cpp b/nano/node/vote_processor.cpp index 7b70aa0e5..f78a994f3 100644 --- a/nano/node/vote_processor.cpp +++ b/nano/node/vote_processor.cpp @@ -10,8 +10,6 @@ #include #include -#include - #include using namespace std::chrono_literals; @@ -161,12 +159,12 @@ void nano::vote_processor::verify_votes (decltype (votes) const & votes_a) } } -nano::vote_code nano::vote_processor::vote_blocking (std::shared_ptr const & vote_a, std::shared_ptr const & channel_a, bool validated) +nano::vote_code nano::vote_processor::vote_blocking (std::shared_ptr const & vote, std::shared_ptr const & channel, bool validated) { auto result = nano::vote_code::invalid; - if (validated || !vote_a->validate ()) + if (validated || !vote->validate ()) { - auto vote_results = active.vote (vote_a); + auto vote_results = active.vote (vote); // Aggregate results for individual hashes bool replay = false; @@ -184,7 +182,7 @@ nano::vote_code nano::vote_processor::vote_blocking (std::shared_ptr stats.inc (nano::stat::type::vote, to_stat_detail (result)); logger.trace (nano::log::type::vote_processor, nano::log::detail::vote_processed, - nano::log::arg{ "vote", vote_a }, + nano::log::arg{ "vote", vote }, nano::log::arg{ "result", result }); return result; From 0fa894f19d56086382d66b3458b35862ea3092b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Wed, 20 Mar 2024 18:34:14 +0100 Subject: [PATCH 092/128] Fix tests --- nano/core_test/active_transactions.cpp | 38 ++++--- nano/core_test/vote_cache.cpp | 139 +++++++++++-------------- nano/core_test/vote_processor.cpp | 12 ++- nano/node/active_transactions.cpp | 11 +- nano/node/active_transactions.hpp | 6 +- nano/node/vote_cache.hpp | 4 +- 6 files changed, 102 insertions(+), 108 deletions(-) diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index d9cba0580..dc8e36360 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -235,7 +235,7 @@ TEST (active_transactions, keep_local) // ASSERT_EQ (1, node.scheduler.size ()); } -TEST (active_transactions, inactive_votes_cache) +TEST (inactive_votes_cache, basic) { nano::test::system system (1); auto & node = *system.nodes[0]; @@ -259,7 +259,7 @@ TEST (active_transactions, inactive_votes_cache) /** * This test case confirms that a non final vote cannot cause an election to become confirmed */ -TEST (active_transactions, inactive_votes_cache_non_final) +TEST (inactive_votes_cache, non_final) { nano::test::system system (1); auto & node = *system.nodes[0]; @@ -285,7 +285,7 @@ TEST (active_transactions, inactive_votes_cache_non_final) ASSERT_FALSE (election->confirmed ()); } -TEST (active_transactions, inactive_votes_cache_fork) +TEST (inactive_votes_cache, fork) { nano::test::system system{ 1 }; auto & node = *system.nodes[0]; @@ -325,7 +325,7 @@ TEST (active_transactions, inactive_votes_cache_fork) ASSERT_EQ (1, node.stats.count (nano::stat::type::election, nano::stat::detail::vote_cached)); } -TEST (active_transactions, inactive_votes_cache_existing_vote) +TEST (inactive_votes_cache, existing_vote) { nano::test::system system; nano::node_config node_config = system.default_config (); @@ -365,12 +365,13 @@ TEST (active_transactions, inactive_votes_cache_existing_vote) ASSERT_EQ (send->hash (), last_vote1.hash); ASSERT_EQ (nano::vote::timestamp_min * 1, last_vote1.timestamp); // Attempt to change vote with inactive_votes_cache - nano::unique_lock active_lock (node.active.mutex); - node.vote_cache.vote (send->hash (), vote1); - auto cache = node.vote_cache.find (send->hash ()); - ASSERT_TRUE (cache); - ASSERT_EQ (1, cache->voters ().size ()); - cache->fill (election); + node.vote_cache.vote (vote1); + auto cached = node.vote_cache.find (send->hash ()); + ASSERT_EQ (1, cached.size ()); + for (auto const & cached_vote : cached) + { + node.active.vote (cached_vote); + } // Check that election data is not changed ASSERT_EQ (2, election->votes ().size ()); auto last_vote2 (election->votes ()[key.pub]); @@ -380,7 +381,7 @@ TEST (active_transactions, inactive_votes_cache_existing_vote) ASSERT_EQ (0, node.stats.count (nano::stat::type::election, nano::stat::detail::vote_cached)); } -TEST (active_transactions, inactive_votes_cache_multiple_votes) +TEST (inactive_votes_cache, multiple_votes) { nano::test::system system; nano::node_config node_config = system.default_config (); @@ -426,8 +427,7 @@ TEST (active_transactions, inactive_votes_cache_multiple_votes) auto vote2 = nano::test::make_vote (nano::dev::genesis_key, { send1 }, 0, 0); node.vote_processor.vote (vote2, std::make_shared (node, node)); - ASSERT_TIMELY (5s, node.vote_cache.find (send1->hash ())); - ASSERT_TIMELY_EQ (5s, node.vote_cache.find (send1->hash ())->voters ().size (), 2); + ASSERT_TIMELY_EQ (5s, node.vote_cache.find (send1->hash ()).size (), 2); ASSERT_EQ (1, node.vote_cache.size ()); node.scheduler.priority.activate (nano::dev::genesis_key.pub, node.store.tx_begin_read ()); std::shared_ptr election; @@ -436,7 +436,7 @@ TEST (active_transactions, inactive_votes_cache_multiple_votes) ASSERT_EQ (2, node.stats.count (nano::stat::type::election, nano::stat::detail::vote_cached)); } -TEST (active_transactions, inactive_votes_cache_election_start) +TEST (inactive_votes_cache, election_start) { nano::test::system system; nano::node_config node_config = system.default_config (); @@ -528,8 +528,7 @@ TEST (active_transactions, inactive_votes_cache_election_start) // A late block arrival also checks the inactive votes cache ASSERT_TRUE (node.active.empty ()); auto send4_cache (node.vote_cache.find (send4->hash ())); - ASSERT_TRUE (send4_cache); - ASSERT_EQ (3, send4_cache->voters ().size ()); + ASSERT_EQ (3, send4_cache.size ()); node.process_active (send3); // An election is started for send6 but does not ASSERT_FALSE (node.block_confirmed_or_being_confirmed (send3->hash ())); @@ -982,8 +981,7 @@ TEST (active_transactions, fork_replacement_tally) node1.vote_processor.vote (vote, std::make_shared (node1, node1)); node1.vote_processor.flush (); // ensure vote arrives before the block - ASSERT_TIMELY (5s, node1.vote_cache.find (send_last->hash ())); - ASSERT_TIMELY_EQ (5s, 1, node1.vote_cache.find (send_last->hash ())->size ()); + ASSERT_TIMELY_EQ (5s, 1, node1.vote_cache.find (send_last->hash ()).size ()); node1.network.publish_filter.clear (); node2.network.flood_block (send_last); ASSERT_TIMELY (5s, node1.stats.count (nano::stat::type::message, nano::stat::detail::publish, nano::stat::dir::in) > 1); @@ -1537,7 +1535,7 @@ TEST (active_transactions, allow_limited_overflow) { // Non-final vote, so it stays in the AEC without getting confirmed auto vote = nano::test::make_vote (nano::dev::genesis_key, { block }); - node.vote_cache.vote (block->hash (), vote); + node.vote_cache.vote (vote); } // Ensure active elections overfill AEC only up to normal + hinted limit @@ -1575,7 +1573,7 @@ TEST (active_transactions, allow_limited_overflow_adapt) { // Non-final vote, so it stays in the AEC without getting confirmed auto vote = nano::test::make_vote (nano::dev::genesis_key, { block }); - node.vote_cache.vote (block->hash (), vote); + node.vote_cache.vote (vote); } // Ensure hinted election amount is bounded by hinted limit diff --git a/nano/core_test/vote_cache.cpp b/nano/core_test/vote_cache.cpp index b4ac518af..fea239bf4 100644 --- a/nano/core_test/vote_cache.cpp +++ b/nano/core_test/vote_cache.cpp @@ -42,7 +42,7 @@ TEST (vote_cache, construction) ASSERT_EQ (0, vote_cache.size ()); ASSERT_TRUE (vote_cache.empty ()); auto hash1 = nano::test::random_hash (); - ASSERT_FALSE (vote_cache.find (hash1)); + ASSERT_TRUE (vote_cache.find (hash1).empty ()); } /* @@ -57,16 +57,12 @@ TEST (vote_cache, insert_one_hash) auto rep1 = create_rep (7); auto hash1 = nano::test::random_hash (); auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1024 * 1024); - vote_cache.vote (vote1->hashes.front (), vote1); + vote_cache.vote (vote1); ASSERT_EQ (1, vote_cache.size ()); auto peek1 = vote_cache.find (hash1); - ASSERT_TRUE (peek1); - ASSERT_EQ (peek1->hash (), hash1); - ASSERT_EQ (peek1->voters ().size (), 1); - ASSERT_EQ (peek1->voters ().front ().representative, rep1.pub); // account - ASSERT_EQ (peek1->voters ().front ().timestamp, 1024 * 1024); // timestamp - ASSERT_EQ (peek1->tally (), 7); + ASSERT_EQ (peek1.size (), 1); + ASSERT_EQ (peek1.front (), vote1); auto tops = vote_cache.top (0); ASSERT_EQ (tops.size (), 1); @@ -92,17 +88,17 @@ TEST (vote_cache, insert_one_hash_many_votes) auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1 * 1024 * 1024); auto vote2 = nano::test::make_vote (rep2, { hash1 }, 2 * 1024 * 1024); auto vote3 = nano::test::make_vote (rep3, { hash1 }, 3 * 1024 * 1024); - vote_cache.vote (vote1->hashes.front (), vote1); - vote_cache.vote (vote2->hashes.front (), vote2); - vote_cache.vote (vote3->hashes.front (), vote3); + vote_cache.vote (vote1); + vote_cache.vote (vote2); + vote_cache.vote (vote3); - // We have 3 votes but for a single hash, so just one entry in vote cache ASSERT_EQ (1, vote_cache.size ()); auto peek1 = vote_cache.find (hash1); - ASSERT_TRUE (peek1); - ASSERT_EQ (peek1->voters ().size (), 3); - // Tally must be the sum of rep weights - ASSERT_EQ (peek1->tally (), 7 + 9 + 11); + ASSERT_EQ (peek1.size (), 3); + // Verify each vote is present + ASSERT_TRUE (std::find (peek1.begin (), peek1.end (), vote1) != peek1.end ()); + ASSERT_TRUE (std::find (peek1.begin (), peek1.end (), vote2) != peek1.end ()); + ASSERT_TRUE (std::find (peek1.begin (), peek1.end (), vote3) != peek1.end ()); auto tops = vote_cache.top (0); ASSERT_EQ (tops.size (), 1); @@ -136,14 +132,14 @@ TEST (vote_cache, insert_many_hashes_many_votes) auto vote3 = nano::test::make_vote (rep3, { hash3 }, 1024 * 1024); auto vote4 = nano::test::make_vote (rep4, { hash1 }, 1024 * 1024); // Insert first 3 votes in cache - vote_cache.vote (vote1->hashes.front (), vote1); - vote_cache.vote (vote2->hashes.front (), vote2); - vote_cache.vote (vote3->hashes.front (), vote3); + vote_cache.vote (vote1); + vote_cache.vote (vote2); + vote_cache.vote (vote3); // Ensure all of those are properly inserted ASSERT_EQ (3, vote_cache.size ()); - ASSERT_TRUE (vote_cache.find (hash1)); - ASSERT_TRUE (vote_cache.find (hash2)); - ASSERT_TRUE (vote_cache.find (hash3)); + ASSERT_EQ (1, vote_cache.find (hash1).size ()); + ASSERT_EQ (1, vote_cache.find (hash2).size ()); + ASSERT_EQ (1, vote_cache.find (hash3).size ()); // Ensure that first entry in queue is the one for hash3 (rep3 has the highest weight of the first 3 reps) auto tops1 = vote_cache.top (0); @@ -151,14 +147,12 @@ TEST (vote_cache, insert_many_hashes_many_votes) ASSERT_EQ (tops1[0].hash, hash3); ASSERT_EQ (tops1[0].tally, 11); - auto peek1 = vote_cache.find (tops1[0].hash); - ASSERT_TRUE (peek1); - ASSERT_EQ (peek1->voters ().size (), 1); - ASSERT_EQ (peek1->tally (), 11); - ASSERT_EQ (peek1->hash (), hash3); + auto peek1 = vote_cache.find (hash3); + ASSERT_EQ (peek1.size (), 1); + ASSERT_EQ (peek1.front (), vote3); // Now add a vote from rep4 with the highest voting weight - vote_cache.vote (vote4->hashes.front (), vote4); + vote_cache.vote (vote4); // Ensure that the first entry in queue is now the one for hash1 (rep1 + rep4 tally weight) auto tops2 = vote_cache.top (0); @@ -166,31 +160,26 @@ TEST (vote_cache, insert_many_hashes_many_votes) ASSERT_EQ (tops2[0].hash, hash1); ASSERT_EQ (tops2[0].tally, 7 + 13); - auto pop1 = vote_cache.find (tops2[0].hash); - ASSERT_TRUE (pop1); - ASSERT_EQ ((*pop1).voters ().size (), 2); - ASSERT_EQ ((*pop1).tally (), 7 + 13); - ASSERT_EQ ((*pop1).hash (), hash1); + auto pop1 = vote_cache.find (hash1); + ASSERT_EQ (pop1.size (), 2); + ASSERT_TRUE (std::find (pop1.begin (), pop1.end (), vote1) != pop1.end ()); + ASSERT_TRUE (std::find (pop1.begin (), pop1.end (), vote4) != pop1.end ()); // The next entry in queue should be hash3 (rep3 tally weight) ASSERT_EQ (tops2[1].hash, hash3); ASSERT_EQ (tops2[1].tally, 11); - auto pop2 = vote_cache.find (tops2[1].hash); - ASSERT_EQ ((*pop2).voters ().size (), 1); - ASSERT_EQ ((*pop2).tally (), 11); - ASSERT_EQ ((*pop2).hash (), hash3); - ASSERT_TRUE (vote_cache.find (hash3)); + auto pop2 = vote_cache.find (hash3); + ASSERT_EQ (pop2.size (), 1); + ASSERT_EQ (pop2.front (), vote3); // And last one should be hash2 with rep2 tally weight ASSERT_EQ (tops2[2].hash, hash2); ASSERT_EQ (tops2[2].tally, 9); - auto pop3 = vote_cache.find (tops2[2].hash); - ASSERT_EQ ((*pop3).voters ().size (), 1); - ASSERT_EQ ((*pop3).tally (), 9); - ASSERT_EQ ((*pop3).hash (), hash2); - ASSERT_TRUE (vote_cache.find (hash2)); + auto pop3 = vote_cache.find (hash2); + ASSERT_EQ (pop3.size (), 1); + ASSERT_EQ (pop3.front (), vote2); } /* @@ -206,8 +195,8 @@ TEST (vote_cache, insert_duplicate) auto rep1 = create_rep (9); auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1 * 1024 * 1024); auto vote2 = nano::test::make_vote (rep1, { hash1 }, 1 * 1024 * 1024); - vote_cache.vote (vote1->hashes.front (), vote1); - vote_cache.vote (vote2->hashes.front (), vote2); + vote_cache.vote (vote1); + vote_cache.vote (vote2); ASSERT_EQ (1, vote_cache.size ()); } @@ -223,18 +212,15 @@ TEST (vote_cache, insert_newer) auto hash1 = nano::test::random_hash (); auto rep1 = create_rep (9); auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1 * 1024 * 1024); - vote_cache.vote (vote1->hashes.front (), vote1); + vote_cache.vote (vote1); auto peek1 = vote_cache.find (hash1); - ASSERT_TRUE (peek1); + ASSERT_EQ (peek1.size (), 1); + ASSERT_EQ (peek1.front (), vote1); auto vote2 = nano::test::make_final_vote (rep1, { hash1 }); - vote_cache.vote (vote2->hashes.front (), vote2); + vote_cache.vote (vote2); auto peek2 = vote_cache.find (hash1); - ASSERT_TRUE (peek2); - ASSERT_EQ (1, vote_cache.size ()); - ASSERT_EQ (1, peek2->voters ().size ()); - // Second entry should have timestamp greater than the first one - ASSERT_GT (peek2->voters ().front ().timestamp, peek1->voters ().front ().timestamp); - ASSERT_EQ (peek2->voters ().front ().timestamp, std::numeric_limits::max ()); // final timestamp + ASSERT_EQ (peek2.size (), 1); + ASSERT_EQ (peek2.front (), vote2); // vote2 should replace vote1 as it has a higher timestamp } /* @@ -249,16 +235,15 @@ TEST (vote_cache, insert_older) auto hash1 = nano::test::random_hash (); auto rep1 = create_rep (9); auto vote1 = nano::test::make_vote (rep1, { hash1 }, 2 * 1024 * 1024); - vote_cache.vote (vote1->hashes.front (), vote1); + vote_cache.vote (vote1); auto peek1 = vote_cache.find (hash1); - ASSERT_TRUE (peek1); + ASSERT_EQ (peek1.size (), 1); + ASSERT_EQ (peek1.front (), vote1); auto vote2 = nano::test::make_vote (rep1, { hash1 }, 1 * 1024 * 1024); - vote_cache.vote (vote2->hashes.front (), vote2); + vote_cache.vote (vote2); auto peek2 = vote_cache.find (hash1); - ASSERT_TRUE (peek2); - ASSERT_EQ (1, vote_cache.size ()); - ASSERT_EQ (1, peek2->voters ().size ()); - ASSERT_EQ (peek2->voters ().front ().timestamp, peek1->voters ().front ().timestamp); // timestamp2 == timestamp1 + ASSERT_EQ (peek2.size (), 1); + ASSERT_EQ (peek2.front (), vote1); // vote1 should still be in cache as it has a higher timestamp } /* @@ -280,24 +265,24 @@ TEST (vote_cache, erase) auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1024 * 1024); auto vote2 = nano::test::make_vote (rep2, { hash2 }, 1024 * 1024); auto vote3 = nano::test::make_vote (rep3, { hash3 }, 1024 * 1024); - vote_cache.vote (vote1->hashes.front (), vote1); - vote_cache.vote (vote2->hashes.front (), vote2); - vote_cache.vote (vote3->hashes.front (), vote3); + vote_cache.vote (vote1); + vote_cache.vote (vote2); + vote_cache.vote (vote3); ASSERT_EQ (3, vote_cache.size ()); ASSERT_FALSE (vote_cache.empty ()); - ASSERT_TRUE (vote_cache.find (hash1)); - ASSERT_TRUE (vote_cache.find (hash2)); - ASSERT_TRUE (vote_cache.find (hash3)); + ASSERT_FALSE (vote_cache.find (hash1).empty ()); + ASSERT_FALSE (vote_cache.find (hash2).empty ()); + ASSERT_FALSE (vote_cache.find (hash3).empty ()); vote_cache.erase (hash2); ASSERT_EQ (2, vote_cache.size ()); - ASSERT_TRUE (vote_cache.find (hash1)); - ASSERT_FALSE (vote_cache.find (hash2)); - ASSERT_TRUE (vote_cache.find (hash3)); + ASSERT_FALSE (vote_cache.find (hash1).empty ()); + ASSERT_TRUE (vote_cache.find (hash2).empty ()); + ASSERT_FALSE (vote_cache.find (hash3).empty ()); vote_cache.erase (hash1); vote_cache.erase (hash3); - ASSERT_FALSE (vote_cache.find (hash1)); - ASSERT_FALSE (vote_cache.find (hash2)); - ASSERT_FALSE (vote_cache.find (hash3)); + ASSERT_TRUE (vote_cache.find (hash1).empty ()); + ASSERT_TRUE (vote_cache.find (hash2).empty ()); + ASSERT_TRUE (vote_cache.find (hash3).empty ()); ASSERT_TRUE (vote_cache.empty ()); } @@ -319,7 +304,7 @@ TEST (vote_cache, overfill) auto rep1 = create_rep (count - n); auto hash1 = nano::test::random_hash (); auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1024 * 1024); - vote_cache.vote (vote1->hashes.front (), vote1); + vote_cache.vote (vote1); } ASSERT_LT (vote_cache.size (), count); // Check that oldest votes are dropped first @@ -343,7 +328,7 @@ TEST (vote_cache, overfill_entry) { auto rep1 = create_rep (9); auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1024 * 1024); - vote_cache.vote (vote1->hashes.front (), vote1); + vote_cache.vote (vote1); } ASSERT_EQ (1, vote_cache.size ()); } @@ -359,9 +344,9 @@ TEST (vote_cache, age_cutoff) auto hash1 = nano::test::random_hash (); auto rep1 = create_rep (9); auto vote1 = nano::test::make_vote (rep1, { hash1 }, 3); - vote_cache.vote (vote1->hashes.front (), vote1); + vote_cache.vote (vote1); ASSERT_EQ (1, vote_cache.size ()); - ASSERT_TRUE (vote_cache.find (hash1)); + ASSERT_FALSE (vote_cache.find (hash1).empty ()); auto tops1 = vote_cache.top (0); ASSERT_EQ (tops1.size (), 1); diff --git a/nano/core_test/vote_processor.cpp b/nano/core_test/vote_processor.cpp index 4695bcf82..115dd347e 100644 --- a/nano/core_test/vote_processor.cpp +++ b/nano/core_test/vote_processor.cpp @@ -15,8 +15,14 @@ using namespace std::chrono_literals; TEST (vote_processor, codes) { - nano::test::system system (1); - auto & node (*system.nodes[0]); + nano::test::system system; + auto node_config = system.default_config (); + // Disable all election schedulers + node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; + node_config.hinted_scheduler.enabled = false; + node_config.optimistic_scheduler.enabled = false; + auto & node = *system.add_node (node_config); + auto blocks = nano::test::setup_chain (system, node, 1, nano::dev::genesis_key, false); auto vote = nano::test::make_vote (nano::dev::genesis_key, { blocks[0] }, nano::vote::timestamp_min * 1, 0); auto vote_invalid = std::make_shared (*vote); @@ -48,7 +54,7 @@ TEST (vote_processor, codes) ASSERT_EQ (nano::vote_code::invalid, node.vote_processor.vote_blocking (vote_invalid, channel)); // Once the election is removed (confirmed / dropped) the vote is again indeterminate - node.active.erase (*blocks[0]); + ASSERT_TRUE (node.active.erase (blocks[0]->qualified_root ())); ASSERT_EQ (nano::vote_code::indeterminate, node.vote_processor.vote_blocking (vote, channel)); } diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index e79b08a2f..8b8d28a9e 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -549,26 +549,29 @@ std::shared_ptr nano::active_transactions::winner (nano::block_hash return result; } -void nano::active_transactions::erase (nano::block const & block_a) +bool nano::active_transactions::erase (nano::block const & block_a) { - erase (block_a.qualified_root ()); + return erase (block_a.qualified_root ()); } -void nano::active_transactions::erase (nano::qualified_root const & root_a) +bool nano::active_transactions::erase (nano::qualified_root const & root_a) { nano::unique_lock lock{ mutex }; auto root_it (roots.get ().find (root_a)); if (root_it != roots.get ().end ()) { cleanup_election (lock, root_it->election); + return true; } + return false; } -void nano::active_transactions::erase_hash (nano::block_hash const & hash_a) +bool nano::active_transactions::erase_hash (nano::block_hash const & hash_a) { nano::unique_lock lock{ mutex }; [[maybe_unused]] auto erased (blocks.erase (hash_a)); debug_assert (erased == 1); + return erased == 1; } void nano::active_transactions::erase_oldest () diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index cb29b6bc9..091daea33 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -164,8 +164,9 @@ public: std::shared_ptr winner (nano::block_hash const &) const; // Returns a list of elections sorted by difficulty std::vector> list_active (std::size_t = std::numeric_limits::max ()); - void erase (nano::block const &); - void erase_hash (nano::block_hash const &); + bool erase (nano::block const &); + bool erase (nano::qualified_root const &); + bool erase_hash (nano::block_hash const &); void erase_oldest (); bool empty () const; std::size_t size () const; @@ -193,7 +194,6 @@ private: void trim (); void request_loop (); void request_confirm (nano::unique_lock &); - void erase (nano::qualified_root const &); // Erase all blocks from active and, if not confirmed, clear digests from network filters void cleanup_election (nano::unique_lock & lock_a, std::shared_ptr); nano::stat::type completion_type (nano::election const & election) const; diff --git a/nano/node/vote_cache.hpp b/nano/node/vote_cache.hpp index 3208e9d8a..4a939a2f9 100644 --- a/nano/node/vote_cache.hpp +++ b/nano/node/vote_cache.hpp @@ -106,7 +106,9 @@ public: /** * Adds a new vote to cache */ - void vote (std::shared_ptr const & vote, std::function const & filter); + void vote ( + std::shared_ptr const & vote, + std::function const & filter = [] (nano::block_hash const &) { return true; }); /** * Tries to find an entry associated with block hash From 7d2fbd80268be15ef323eed82bd29838503cf50d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 21 Mar 2024 11:49:25 +0100 Subject: [PATCH 093/128] Vote processed event --- nano/core_test/active_transactions.cpp | 6 ++-- nano/core_test/vote_cache.cpp | 40 +++++++++++++------------- nano/node/active_transactions.cpp | 30 ++++++++++--------- nano/node/active_transactions.hpp | 7 ++++- nano/node/node.cpp | 4 +++ nano/node/vote_cache.cpp | 21 +++++++++++++- nano/node/vote_cache.hpp | 9 ++++-- nano/node/vote_processor.cpp | 2 +- 8 files changed, 78 insertions(+), 41 deletions(-) diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index dc8e36360..6a236d301 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -365,7 +365,7 @@ TEST (inactive_votes_cache, existing_vote) ASSERT_EQ (send->hash (), last_vote1.hash); ASSERT_EQ (nano::vote::timestamp_min * 1, last_vote1.timestamp); // Attempt to change vote with inactive_votes_cache - node.vote_cache.vote (vote1); + node.vote_cache.insert (vote1); auto cached = node.vote_cache.find (send->hash ()); ASSERT_EQ (1, cached.size ()); for (auto const & cached_vote : cached) @@ -1535,7 +1535,7 @@ TEST (active_transactions, allow_limited_overflow) { // Non-final vote, so it stays in the AEC without getting confirmed auto vote = nano::test::make_vote (nano::dev::genesis_key, { block }); - node.vote_cache.vote (vote); + node.vote_cache.insert (vote); } // Ensure active elections overfill AEC only up to normal + hinted limit @@ -1573,7 +1573,7 @@ TEST (active_transactions, allow_limited_overflow_adapt) { // Non-final vote, so it stays in the AEC without getting confirmed auto vote = nano::test::make_vote (nano::dev::genesis_key, { block }); - node.vote_cache.vote (vote); + node.vote_cache.insert (vote); } // Ensure hinted election amount is bounded by hinted limit diff --git a/nano/core_test/vote_cache.cpp b/nano/core_test/vote_cache.cpp index fea239bf4..711f8a31b 100644 --- a/nano/core_test/vote_cache.cpp +++ b/nano/core_test/vote_cache.cpp @@ -57,7 +57,7 @@ TEST (vote_cache, insert_one_hash) auto rep1 = create_rep (7); auto hash1 = nano::test::random_hash (); auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1024 * 1024); - vote_cache.vote (vote1); + vote_cache.insert (vote1); ASSERT_EQ (1, vote_cache.size ()); auto peek1 = vote_cache.find (hash1); @@ -88,9 +88,9 @@ TEST (vote_cache, insert_one_hash_many_votes) auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1 * 1024 * 1024); auto vote2 = nano::test::make_vote (rep2, { hash1 }, 2 * 1024 * 1024); auto vote3 = nano::test::make_vote (rep3, { hash1 }, 3 * 1024 * 1024); - vote_cache.vote (vote1); - vote_cache.vote (vote2); - vote_cache.vote (vote3); + vote_cache.insert (vote1); + vote_cache.insert (vote2); + vote_cache.insert (vote3); ASSERT_EQ (1, vote_cache.size ()); auto peek1 = vote_cache.find (hash1); @@ -132,9 +132,9 @@ TEST (vote_cache, insert_many_hashes_many_votes) auto vote3 = nano::test::make_vote (rep3, { hash3 }, 1024 * 1024); auto vote4 = nano::test::make_vote (rep4, { hash1 }, 1024 * 1024); // Insert first 3 votes in cache - vote_cache.vote (vote1); - vote_cache.vote (vote2); - vote_cache.vote (vote3); + vote_cache.insert (vote1); + vote_cache.insert (vote2); + vote_cache.insert (vote3); // Ensure all of those are properly inserted ASSERT_EQ (3, vote_cache.size ()); ASSERT_EQ (1, vote_cache.find (hash1).size ()); @@ -152,7 +152,7 @@ TEST (vote_cache, insert_many_hashes_many_votes) ASSERT_EQ (peek1.front (), vote3); // Now add a vote from rep4 with the highest voting weight - vote_cache.vote (vote4); + vote_cache.insert (vote4); // Ensure that the first entry in queue is now the one for hash1 (rep1 + rep4 tally weight) auto tops2 = vote_cache.top (0); @@ -195,8 +195,8 @@ TEST (vote_cache, insert_duplicate) auto rep1 = create_rep (9); auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1 * 1024 * 1024); auto vote2 = nano::test::make_vote (rep1, { hash1 }, 1 * 1024 * 1024); - vote_cache.vote (vote1); - vote_cache.vote (vote2); + vote_cache.insert (vote1); + vote_cache.insert (vote2); ASSERT_EQ (1, vote_cache.size ()); } @@ -212,12 +212,12 @@ TEST (vote_cache, insert_newer) auto hash1 = nano::test::random_hash (); auto rep1 = create_rep (9); auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1 * 1024 * 1024); - vote_cache.vote (vote1); + vote_cache.insert (vote1); auto peek1 = vote_cache.find (hash1); ASSERT_EQ (peek1.size (), 1); ASSERT_EQ (peek1.front (), vote1); auto vote2 = nano::test::make_final_vote (rep1, { hash1 }); - vote_cache.vote (vote2); + vote_cache.insert (vote2); auto peek2 = vote_cache.find (hash1); ASSERT_EQ (peek2.size (), 1); ASSERT_EQ (peek2.front (), vote2); // vote2 should replace vote1 as it has a higher timestamp @@ -235,12 +235,12 @@ TEST (vote_cache, insert_older) auto hash1 = nano::test::random_hash (); auto rep1 = create_rep (9); auto vote1 = nano::test::make_vote (rep1, { hash1 }, 2 * 1024 * 1024); - vote_cache.vote (vote1); + vote_cache.insert (vote1); auto peek1 = vote_cache.find (hash1); ASSERT_EQ (peek1.size (), 1); ASSERT_EQ (peek1.front (), vote1); auto vote2 = nano::test::make_vote (rep1, { hash1 }, 1 * 1024 * 1024); - vote_cache.vote (vote2); + vote_cache.insert (vote2); auto peek2 = vote_cache.find (hash1); ASSERT_EQ (peek2.size (), 1); ASSERT_EQ (peek2.front (), vote1); // vote1 should still be in cache as it has a higher timestamp @@ -265,9 +265,9 @@ TEST (vote_cache, erase) auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1024 * 1024); auto vote2 = nano::test::make_vote (rep2, { hash2 }, 1024 * 1024); auto vote3 = nano::test::make_vote (rep3, { hash3 }, 1024 * 1024); - vote_cache.vote (vote1); - vote_cache.vote (vote2); - vote_cache.vote (vote3); + vote_cache.insert (vote1); + vote_cache.insert (vote2); + vote_cache.insert (vote3); ASSERT_EQ (3, vote_cache.size ()); ASSERT_FALSE (vote_cache.empty ()); ASSERT_FALSE (vote_cache.find (hash1).empty ()); @@ -304,7 +304,7 @@ TEST (vote_cache, overfill) auto rep1 = create_rep (count - n); auto hash1 = nano::test::random_hash (); auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1024 * 1024); - vote_cache.vote (vote1); + vote_cache.insert (vote1); } ASSERT_LT (vote_cache.size (), count); // Check that oldest votes are dropped first @@ -328,7 +328,7 @@ TEST (vote_cache, overfill_entry) { auto rep1 = create_rep (9); auto vote1 = nano::test::make_vote (rep1, { hash1 }, 1024 * 1024); - vote_cache.vote (vote1); + vote_cache.insert (vote1); } ASSERT_EQ (1, vote_cache.size ()); } @@ -344,7 +344,7 @@ TEST (vote_cache, age_cutoff) auto hash1 = nano::test::random_hash (); auto rep1 = create_rep (9); auto vote1 = nano::test::make_vote (rep1, { hash1 }, 3); - vote_cache.vote (vote1); + vote_cache.insert (vote1); ASSERT_EQ (1, vote_cache.size ()); ASSERT_FALSE (vote_cache.find (hash1).empty ()); diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 8b8d28a9e..aa816fa9e 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -419,13 +419,9 @@ nano::election_insertion_result nano::active_transactions::insert (std::shared_p if (result.inserted) { - release_assert (result.election); + debug_assert (result.election); - auto cached = node.vote_cache.find (hash); - for (auto const & cached_vote : cached) - { - vote (cached_vote); - } + trigger_vote_cache (hash); node.observers.active_started.notify (hash); vacancy_update (); @@ -442,8 +438,18 @@ nano::election_insertion_result nano::active_transactions::insert (std::shared_p return result; } +bool nano::active_transactions::trigger_vote_cache (nano::block_hash hash) +{ + auto cached = node.vote_cache.find (hash); + for (auto const & cached_vote : cached) + { + vote (cached_vote, nano::vote_source::cache); + } + return !cached.empty (); +} + // Validate a vote and apply it to the current election if one exists -std::unordered_map nano::active_transactions::vote (std::shared_ptr const & vote) +std::unordered_map nano::active_transactions::vote (std::shared_ptr const & vote, nano::vote_source source) { std::unordered_map results; std::unordered_map> process; @@ -480,7 +486,7 @@ std::unordered_map nano::active_transactions: for (auto const & [block_hash, election] : process) { - auto const vote_result = election->vote (vote->account, vote->timestamp (), block_hash); + auto const vote_result = election->vote (vote->account, vote->timestamp (), block_hash, source); results[block_hash] = vote_result; processed |= (vote_result == nano::vote_code::vote); @@ -502,6 +508,8 @@ std::unordered_map nano::active_transactions: return results.find (hash) != results.end (); })); + vote_processed.notify (vote, source, results); + return results; } @@ -612,11 +620,7 @@ bool nano::active_transactions::publish (std::shared_ptr const & bl blocks.emplace (block_a->hash (), election); lock.unlock (); - auto cached = node.vote_cache.find (block_a->hash ()); - for (auto const & cached_vote : cached) - { - vote (cached_vote); - } + trigger_vote_cache (block_a->hash ()); node.stats.inc (nano::stat::type::active, nano::stat::detail::election_block_conflict); } diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index 091daea33..5c2a07d92 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -152,7 +152,7 @@ public: */ nano::election_insertion_result insert (std::shared_ptr const &, nano::election_behavior = nano::election_behavior::normal); // Distinguishes replay votes, cannot be determined if the block is not in any election - std::unordered_map vote (std::shared_ptr const &); + std::unordered_map vote (std::shared_ptr const &, nano::vote_source = nano::vote_source::live); // Is the root of this block in the roots container bool active (nano::block const &) const; bool active (nano::qualified_root const &) const; @@ -189,6 +189,10 @@ public: void add_election_winner_details (nano::block_hash const &, std::shared_ptr const &); std::shared_ptr remove_election_winner_details (nano::block_hash const &); +public: // Events + using vote_processed_event_t = nano::observer_set const &, nano::vote_source, std::unordered_map const &>; + vote_processed_event_t vote_processed; + private: // Erase elections if we're over capacity void trim (); @@ -201,6 +205,7 @@ private: std::vector> list_active_impl (std::size_t) const; void activate_successors (nano::store::read_transaction const & transaction, std::shared_ptr const & block); void notify_observers (nano::store::read_transaction const & transaction, nano::election_status const & status, std::vector const & votes); + bool trigger_vote_cache (nano::block_hash); private: // Dependencies nano::node & node; diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 3897c5711..ca19ed4ef 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -217,6 +217,10 @@ nano::node::node (std::shared_ptr io_ctx_a, std::filesy scheduler.optimistic.activate (account, account_info, conf_info); }); + active.vote_processed.add ([this] (std::shared_ptr const & vote, nano::vote_source source, std::unordered_map const & results) { + vote_cache.observe (vote, source, results); + }); + if (!init_error ()) { // Notify election schedulers when AEC frees election slot diff --git a/nano/node/vote_cache.cpp b/nano/node/vote_cache.cpp index e4e36a96b..479f7c456 100644 --- a/nano/node/vote_cache.cpp +++ b/nano/node/vote_cache.cpp @@ -128,7 +128,25 @@ nano::vote_cache::vote_cache (vote_cache_config const & config_a, nano::stats & { } -void nano::vote_cache::vote (std::shared_ptr const & vote, std::function const & filter) +void nano::vote_cache::observe (const std::shared_ptr & vote, nano::vote_source source, std::unordered_map results) +{ + if (source == nano::vote_source::live) + { + insert (vote, [&results] (nano::block_hash const & hash) { + // This filters which hashes should be included in the vote cache + if (auto it = results.find (hash); it != results.end ()) + { + auto result = it->second; + // Cache votes with a corresponding active election (indicated by `vote_code::vote`) in case that election gets dropped + return result == nano::vote_code::vote || result == nano::vote_code::indeterminate; + } + debug_assert (false); + return false; + }); + } +} + +void nano::vote_cache::insert (std::shared_ptr const & vote, std::function filter) { auto const representative = vote->account; auto const timestamp = vote->timestamp (); @@ -138,6 +156,7 @@ void nano::vote_cache::vote (std::shared_ptr const & vote, std::func for (auto const & hash : vote->hashes) { + // Using filter callback here to avoid unnecessary relocking when processing large votes if (!filter (hash)) { continue; diff --git a/nano/node/vote_cache.hpp b/nano/node/vote_cache.hpp index 4a939a2f9..b03d288ad 100644 --- a/nano/node/vote_cache.hpp +++ b/nano/node/vote_cache.hpp @@ -106,9 +106,14 @@ public: /** * Adds a new vote to cache */ - void vote ( + void insert ( std::shared_ptr const & vote, - std::function const & filter = [] (nano::block_hash const &) { return true; }); + std::function filter = [] (nano::block_hash const &) { return true; }); + + /** + * Should be called for every processed vote, filters which votes should be added to cache + */ + void observe (std::shared_ptr const & vote, nano::vote_source source, std::unordered_map); /** * Tries to find an entry associated with block hash diff --git a/nano/node/vote_processor.cpp b/nano/node/vote_processor.cpp index f78a994f3..cb2610d0d 100644 --- a/nano/node/vote_processor.cpp +++ b/nano/node/vote_processor.cpp @@ -176,7 +176,7 @@ nano::vote_code nano::vote_processor::vote_blocking (std::shared_ptr } result = replay ? nano::vote_code::replay : (processed ? nano::vote_code::vote : nano::vote_code::indeterminate); - observers.vote.notify (vote_a, channel_a, result); + observers.vote.notify (vote, channel, result); } stats.inc (nano::stat::type::vote, to_stat_detail (result)); From 15f008b07f0d3a954d4c0f03d4f32c2074c54b98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 21 Mar 2024 14:58:45 +0100 Subject: [PATCH 094/128] Move vote republishing out of `active_transactions` --- nano/node/active_transactions.cpp | 23 +++-------------------- nano/node/node.cpp | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index aa816fa9e..c7411a4c6 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -480,27 +480,10 @@ std::unordered_map nano::active_transactions: } } - if (!process.empty ()) + for (auto const & [block_hash, election] : process) { - bool processed = false; - - 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; - - processed |= (vote_result == nano::vote_code::vote); - } - - // Republish vote if it is new and the node does not host a principal representative (or close to) - if (processed) - { - auto const reps (node.wallets.reps ()); - if (!reps.have_half_rep () && !reps.exists (vote->account)) - { - node.network.flood_vote (vote, 0.5f); - } - } + 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 diff --git a/nano/node/node.cpp b/nano/node/node.cpp index ca19ed4ef..d5a98ad1c 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -221,6 +221,21 @@ nano::node::node (std::shared_ptr io_ctx_a, std::filesy vote_cache.observe (vote, source, results); }); + // Republish vote if it is new and the node does not host a principal representative (or close to) + active.vote_processed.add ([this] (std::shared_ptr const & vote, nano::vote_source source, std::unordered_map const & results) { + bool processed = std::any_of (results.begin (), results.end (), [] (auto const & result) { + return result.second == nano::vote_code::vote; + }); + if (processed) + { + auto const reps = wallets.reps (); + if (!reps.have_half_rep () && !reps.exists (vote->account)) + { + network.flood_vote (vote, 0.5f); + } + } + }); + if (!init_error ()) { // Notify election schedulers when AEC frees election slot From 826030d586af13139535c30e2c8ae8120bf3840d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 21 Mar 2024 16:53:55 +0100 Subject: [PATCH 095/128] Report unique vote count --- nano/node/vote_cache.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/nano/node/vote_cache.cpp b/nano/node/vote_cache.cpp index 479f7c456..63d6f6060 100644 --- a/nano/node/vote_cache.cpp +++ b/nano/node/vote_cache.cpp @@ -284,8 +284,23 @@ void nano::vote_cache::cleanup () std::unique_ptr nano::vote_cache::collect_container_info (const std::string & name) const { + nano::lock_guard guard{ mutex }; + + auto count_unique_votes = [this] () { + std::unordered_set> votes; + for (auto const & entry : cache) + { + for (auto const & vote : entry.votes ()) + { + votes.insert (vote); + } + } + return votes.size (); + }; + auto composite = std::make_unique (name); - composite->add_component (std::make_unique (container_info{ "cache", size (), sizeof (ordered_cache::value_type) })); + composite->add_component (std::make_unique (container_info{ "cache", cache.size (), sizeof (ordered_cache::value_type) })); + composite->add_component (std::make_unique (container_info{ "unique", count_unique_votes (), sizeof (nano::vote) })); return composite; } From 8041e77f7cef6c26ad57d6b41bdfc8e908a3e1fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 21 Mar 2024 16:58:35 +0100 Subject: [PATCH 096/128] Adjust default max vote cache size --- nano/node/vote_cache.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nano/node/vote_cache.hpp b/nano/node/vote_cache.hpp index b03d288ad..06e4481d1 100644 --- a/nano/node/vote_cache.hpp +++ b/nano/node/vote_cache.hpp @@ -38,8 +38,8 @@ public: nano::error serialize (nano::tomlconfig & toml) const; public: - std::size_t max_size{ 1024 * 128 }; - std::size_t max_voters{ 128 }; + std::size_t max_size{ 1024 * 64 }; + std::size_t max_voters{ 64 }; std::chrono::seconds age_cutoff{ 15 * 60 }; }; From c734caf4d1ad840df83983e40b86e5d2c1f56a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 21 Mar 2024 17:23:42 +0100 Subject: [PATCH 097/128] Remove by tally index --- nano/node/vote_cache.cpp | 7 +++---- nano/node/vote_cache.hpp | 5 +---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/nano/node/vote_cache.cpp b/nano/node/vote_cache.cpp index 63d6f6060..f3eaec23c 100644 --- a/nano/node/vote_cache.cpp +++ b/nano/node/vote_cache.cpp @@ -244,13 +244,12 @@ std::vector nano::vote_cache::top (const nano::uint cleanup (); } - for (auto & entry : cache.get ()) + for (auto & entry : cache) { - if (entry.tally () < min_tally) + if (entry.tally () >= min_tally) { - break; + results.push_back ({ entry.hash (), entry.tally (), entry.final_tally () }); } - results.push_back ({ entry.hash (), entry.tally (), entry.final_tally () }); } } diff --git a/nano/node/vote_cache.hpp b/nano/node/vote_cache.hpp index 06e4481d1..55d1256cf 100644 --- a/nano/node/vote_cache.hpp +++ b/nano/node/vote_cache.hpp @@ -164,7 +164,6 @@ private: // clang-format off class tag_sequenced {}; class tag_hash {}; - class tag_tally {}; // clang-format on // clang-format off @@ -172,9 +171,7 @@ private: mi::indexed_by< mi::hashed_unique, mi::const_mem_fun>, - mi::sequenced>, - mi::ordered_non_unique, - mi::const_mem_fun, std::greater<>> // DESC + mi::sequenced> >>; // clang-format on ordered_cache cache; From 6f8828c928e8f6acbb7c7535c1bc049cdcd9fed1 Mon Sep 17 00:00:00 2001 From: Gustav Schauwecker Date: Sun, 24 Mar 2024 09:58:46 +0100 Subject: [PATCH 098/128] Apply the count parameter in `json_handler::accounts_receivable()`` (#4520) The count limit got lost during the refactor in pull request #4496 --- nano/node/json_handler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 423ff2a41..aec5648d3 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -1049,7 +1049,7 @@ void nano::json_handler::accounts_receivable () if (!ec) { boost::property_tree::ptree peers_l; - for (auto current = node.ledger.receivable_upper_bound (transaction, account, 0), end = node.ledger.receivable_end (); current != end; ++current) + for (auto current = node.ledger.receivable_upper_bound (transaction, account, 0), end = node.ledger.receivable_end (); current != end && peers_l.size () < count; ++current) { auto const & [key, info] = *current; if (include_only_confirmed && !node.ledger.block_confirmed (transaction, key.hash)) From 27cb6749be8450c9e898ff2b0d835fbec3b24795 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Fri, 22 Mar 2024 11:56:42 +0000 Subject: [PATCH 099/128] Removing inclusion of confirmation_height_processor.hpp from other headers. --- nano/core_test/active_transactions.cpp | 1 + nano/core_test/confirmation_height.cpp | 1 + nano/core_test/node.cpp | 1 + nano/core_test/request_aggregator.cpp | 1 + nano/nano_node/entry.cpp | 1 + nano/node/json_handler.cpp | 1 + nano/node/node.cpp | 4 +++- nano/node/node.hpp | 5 +++-- nano/node/wallet.cpp | 1 + nano/rpc_test/rpc.cpp | 1 + nano/slow_test/node.cpp | 1 + 11 files changed, 15 insertions(+), 3 deletions(-) diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index 6a236d301..196d65bfe 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/core_test/confirmation_height.cpp b/nano/core_test/confirmation_height.cpp index b35eadc71..eade5ba96 100644 --- a/nano/core_test/confirmation_height.cpp +++ b/nano/core_test/confirmation_height.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index 8a34f3f6c..9ceff444f 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 9860075c4..d6662f75a 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/nano_node/entry.cpp b/nano/nano_node/entry.cpp index 0d2898ab4..0e5e48571 100644 --- a/nano/nano_node/entry.cpp +++ b/nano/nano_node/entry.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index aec5648d3..9394e7364 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/node/node.cpp b/nano/node/node.cpp index d5a98ad1c..4e6ec9949 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -171,7 +172,8 @@ nano::node::node (std::shared_ptr io_ctx_a, std::filesy application_path (application_path_a), port_mapping (*this), block_processor (*this, write_database_queue), - confirmation_height_processor (ledger, write_database_queue, config.conf_height_processor_batch_min_time, logger, node_initialized_latch, flags.confirmation_height_processor_mode), + confirmation_height_processor_impl{ std::make_unique (ledger, write_database_queue, config.conf_height_processor_batch_min_time, logger, node_initialized_latch, flags.confirmation_height_processor_mode) }, + confirmation_height_processor{ *confirmation_height_processor_impl }, active_impl{ std::make_unique (*this, confirmation_height_processor, block_processor) }, active{ *active_impl }, rep_crawler (config.rep_crawler, *this), diff --git a/nano/node/node.hpp b/nano/node/node.hpp index c59a09ba1..2254c2f68 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -47,6 +46,7 @@ namespace nano { class active_transactions; +class confirmation_height_processor; class node; class work_pool; @@ -168,7 +168,8 @@ public: nano::node_observers observers; nano::port_mapping port_mapping; nano::block_processor block_processor; - nano::confirmation_height_processor confirmation_height_processor; + std::unique_ptr confirmation_height_processor_impl; + nano::confirmation_height_processor & confirmation_height_processor; std::unique_ptr active_impl; nano::active_transactions & active; nano::online_reps online_reps; diff --git a/nano/node/wallet.cpp b/nano/node/wallet.cpp index a160855dc..638c4c623 100644 --- a/nano/node/wallet.cpp +++ b/nano/node/wallet.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 32a324815..3771fd04e 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/slow_test/node.cpp b/nano/slow_test/node.cpp index 73a9ebe30..28eee6571 100644 --- a/nano/slow_test/node.cpp +++ b/nano/slow_test/node.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include From ab995e4ec6965fd7a913019e3ce1b157922d84ba Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Sat, 23 Mar 2024 16:24:36 +0000 Subject: [PATCH 100/128] Remove confirmation_height_currently_processing RPC as it's only for debugging and make the process of confirming more complicated. --- nano/node/json_handler.cpp | 15 --------- nano/node/json_handler.hpp | 1 - nano/rpc/rpc_handler.cpp | 1 - nano/rpc_test/rpc.cpp | 66 -------------------------------------- 4 files changed, 83 deletions(-) diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 9394e7364..7465385a5 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -2002,20 +2002,6 @@ void nano::json_handler::confirmation_active () response_errors (); } -void nano::json_handler::confirmation_height_currently_processing () -{ - auto hash = node.confirmation_height_processor.current (); - if (!hash.is_zero ()) - { - response_l.put ("hash", hash.to_string ()); - } - else - { - ec = nano::error_rpc::confirmation_height_not_processing; - } - response_errors (); -} - void nano::json_handler::confirmation_history () { boost::property_tree::ptree elections; @@ -5334,7 +5320,6 @@ ipc_json_handler_no_arg_func_map create_ipc_json_handler_no_arg_func_map () no_arg_funcs.emplace ("bootstrap_lazy", &nano::json_handler::bootstrap_lazy); no_arg_funcs.emplace ("bootstrap_status", &nano::json_handler::bootstrap_status); no_arg_funcs.emplace ("confirmation_active", &nano::json_handler::confirmation_active); - no_arg_funcs.emplace ("confirmation_height_currently_processing", &nano::json_handler::confirmation_height_currently_processing); no_arg_funcs.emplace ("confirmation_history", &nano::json_handler::confirmation_history); no_arg_funcs.emplace ("confirmation_info", &nano::json_handler::confirmation_info); no_arg_funcs.emplace ("confirmation_quorum", &nano::json_handler::confirmation_quorum); diff --git a/nano/node/json_handler.hpp b/nano/node/json_handler.hpp index cb7da62c7..cc8d7b7a8 100644 --- a/nano/node/json_handler.hpp +++ b/nano/node/json_handler.hpp @@ -64,7 +64,6 @@ public: void confirmation_history (); void confirmation_info (); void confirmation_quorum (); - void confirmation_height_currently_processing (); void debug_bootstrap_priority_info (); void database_txn_tracker (); void delegators (); diff --git a/nano/rpc/rpc_handler.cpp b/nano/rpc/rpc_handler.cpp index 241c46c47..4d4a6f36d 100644 --- a/nano/rpc/rpc_handler.cpp +++ b/nano/rpc/rpc_handler.cpp @@ -152,7 +152,6 @@ std::unordered_set create_rpc_control_impls () set.emplace ("backoff_info"); set.emplace ("block_create"); set.emplace ("bootstrap_lazy"); - set.emplace ("confirmation_height_currently_processing"); set.emplace ("database_txn_tracker"); set.emplace ("epoch_upgrade"); set.emplace ("keepalive"); diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 3771fd04e..9b8b3b086 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -5202,72 +5202,6 @@ TEST (rpc, online_reps) node2->stop (); } -TEST (rpc, confirmation_height_currently_processing) -{ - nano::test::system system; - nano::node_flags node_flags; - node_flags.force_use_write_database_queue = true; - nano::node_config node_config = system.default_config (); - node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - - auto node = add_ipc_enabled_node (system, node_config, node_flags); - system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv); - - auto previous_genesis_chain_hash = node->latest (nano::dev::genesis_key.pub); - { - auto transaction = node->store.tx_begin_write (); - nano::keypair key1; - nano::block_builder builder; - auto send = builder - .send () - .previous (previous_genesis_chain_hash) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio - 1) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (previous_genesis_chain_hash)) - .build (); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send)); - previous_genesis_chain_hash = send->hash (); - } - - std::shared_ptr frontier; - { - auto transaction = node->store.tx_begin_read (); - frontier = node->ledger.block (transaction, previous_genesis_chain_hash); - } - - boost::property_tree::ptree request; - request.put ("action", "confirmation_height_currently_processing"); - - auto const rpc_ctx = add_rpc (system, node); - - // Begin process for confirming the block (and setting confirmation height) - { - // Write guard prevents the confirmation height processor writing the blocks, so that we can inspect contents during the response - auto write_guard = node->write_database_queue.wait (nano::writer::testing); - nano::test::start_election (system, *node, frontier->hash ()); - - ASSERT_TIMELY_EQ (5s, node->confirmation_height_processor.current (), frontier->hash ()); - - // Make the request - { - auto response (wait_response (system, rpc_ctx, request, 10s)); - auto hash (response.get ("hash")); - ASSERT_EQ (frontier->hash ().to_string (), hash); - } - } - - // Wait until confirmation has been set and not processing anything - ASSERT_TIMELY (10s, node->confirmation_height_processor.current ().is_zero () && node->confirmation_height_processor.awaiting_processing_size () == 0); - - // Make the same request, it should now return an error - { - auto response (wait_response (system, rpc_ctx, request, 10s)); - std::error_code ec (nano::error_rpc::confirmation_height_not_processing); - ASSERT_EQ (response.get ("error"), ec.message ()); - } -} - TEST (rpc, confirmation_history) { nano::test::system system; From 3332bad6911de5a4f1f9885f67a10cd1f2201d88 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Fri, 22 Mar 2024 18:06:45 +0000 Subject: [PATCH 101/128] Adding ledger::confirm function which confirms a block and all its dependencies. Several confirmation_height tests are rewritten to directly test ledger::confirm and moved to the ledger_confirm.cpp test file. --- nano/core_test/CMakeLists.txt | 1 + nano/core_test/confirmation_height.cpp | 1266 ------------------------ nano/core_test/ledger_confirm.cpp | 886 +++++++++++++++++ nano/secure/ledger.cpp | 46 + nano/secure/ledger.hpp | 3 + 5 files changed, 936 insertions(+), 1266 deletions(-) create mode 100644 nano/core_test/ledger_confirm.cpp diff --git a/nano/core_test/CMakeLists.txt b/nano/core_test/CMakeLists.txt index 23c227442..d58a2b5ae 100644 --- a/nano/core_test/CMakeLists.txt +++ b/nano/core_test/CMakeLists.txt @@ -24,6 +24,7 @@ add_executable( frontiers_confirmation.cpp ipc.cpp ledger.cpp + ledger_confirm.cpp locks.cpp logging.cpp message.cpp diff --git a/nano/core_test/confirmation_height.cpp b/nano/core_test/confirmation_height.cpp index eade5ba96..5991615d1 100644 --- a/nano/core_test/confirmation_height.cpp +++ b/nano/core_test/confirmation_height.cpp @@ -33,285 +33,6 @@ nano::stat::detail get_stats_detail (nano::confirmation_height_mode mode_a) } } -TEST (confirmation_height, single) -{ - auto test_mode = [] (nano::confirmation_height_mode mode_a) { - auto amount (std::numeric_limits::max ()); - nano::test::system system; - nano::node_flags node_flags; - node_flags.confirmation_height_processor_mode = mode_a; - auto node = system.add_node (node_flags); - nano::keypair key1; - system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv); - nano::block_hash latest1 (node->latest (nano::dev::genesis_key.pub)); - nano::block_builder builder; - auto send1 = builder - .state () - .account (nano::dev::genesis_key.pub) - .previous (latest1) - .representative (nano::dev::genesis_key.pub) - .balance (amount - 100) - .link (key1.pub) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (latest1)) - .build (); - - // Check confirmation heights before, should be uninitialized (1 for genesis). - nano::confirmation_height_info confirmation_height_info; - add_callback_stats (*node); - auto transaction = node->store.tx_begin_read (); - ASSERT_FALSE (node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, confirmation_height_info)); - ASSERT_EQ (1, confirmation_height_info.height); - ASSERT_EQ (nano::dev::genesis->hash (), confirmation_height_info.frontier); - - node->process_active (send1); - ASSERT_TIMELY (5s, nano::test::exists (*node, { send1 })); - ASSERT_TIMELY_EQ (10s, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out), 1); - - { - auto transaction = node->store.tx_begin_write (); - ASSERT_TRUE (node->ledger.block_confirmed (transaction, send1->hash ())); - ASSERT_FALSE (node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, confirmation_height_info)); - ASSERT_EQ (2, confirmation_height_info.height); - ASSERT_EQ (send1->hash (), confirmation_height_info.frontier); - - // Rollbacks should fail as these blocks have been cemented - ASSERT_TRUE (node->ledger.rollback (transaction, latest1)); - ASSERT_TRUE (node->ledger.rollback (transaction, send1->hash ())); - ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_height, get_stats_detail (mode_a), nano::stat::dir::in)); - ASSERT_EQ (1, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out)); - ASSERT_EQ (2, node->ledger.cache.cemented_count); - - ASSERT_EQ (0, node->active.election_winner_details_size ()); - } - }; - - test_mode (nano::confirmation_height_mode::bounded); - test_mode (nano::confirmation_height_mode::unbounded); -} - -TEST (confirmation_height, multiple_accounts) -{ - auto test_mode = [] (nano::confirmation_height_mode mode_a) { - nano::test::system system; - nano::node_flags node_flags; - node_flags.confirmation_height_processor_mode = mode_a; - nano::node_config node_config = system.default_config (); - node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - auto node = system.add_node (node_config, node_flags); - nano::keypair key1; - nano::keypair key2; - nano::keypair key3; - nano::block_hash latest1 (system.nodes[0]->latest (nano::dev::genesis_key.pub)); - nano::block_builder builder; - - // Send to all accounts - auto send1 = builder - .send () - .previous (latest1) - .destination (key1.pub) - .balance (node->online_reps.delta () + 300) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (latest1)) - .build (); - auto send2 = builder - .send () - .previous (send1->hash ()) - .destination (key2.pub) - .balance (node->online_reps.delta () + 200) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send1->hash ())) - .build (); - auto send3 = builder - .send () - .previous (send2->hash ()) - .destination (key3.pub) - .balance (node->online_reps.delta () + 100) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send2->hash ())) - .build (); - - // Open all accounts - auto open1 = builder - .open () - .source (send1->hash ()) - .representative (nano::dev::genesis_key.pub) - .account (key1.pub) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (key1.pub)) - .build (); - auto open2 = builder - .open () - .source (send2->hash ()) - .representative (nano::dev::genesis_key.pub) - .account (key2.pub) - .sign (key2.prv, key2.pub) - .work (*system.work.generate (key2.pub)) - .build (); - auto open3 = builder - .open () - .source (send3->hash ()) - .representative (nano::dev::genesis_key.pub) - .account (key3.pub) - .sign (key3.prv, key3.pub) - .work (*system.work.generate (key3.pub)) - .build (); - - // Send and receive various blocks to these accounts - auto send4 = builder - .send () - .previous (open1->hash ()) - .destination (key2.pub) - .balance (50) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (open1->hash ())) - .build (); - auto send5 = builder - .send () - .previous (send4->hash ()) - .destination (key2.pub) - .balance (10) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (send4->hash ())) - .build (); - - auto receive1 = builder - .receive () - .previous (open2->hash ()) - .source (send4->hash ()) - .sign (key2.prv, key2.pub) - .work (*system.work.generate (open2->hash ())) - .build (); - auto send6 = builder - .send () - .previous (receive1->hash ()) - .destination (key3.pub) - .balance (10) - .sign (key2.prv, key2.pub) - .work (*system.work.generate (receive1->hash ())) - .build (); - auto receive2 = builder - .receive () - .previous (send6->hash ()) - .source (send5->hash ()) - .sign (key2.prv, key2.pub) - .work (*system.work.generate (send6->hash ())) - .build (); - - add_callback_stats (*node); - - { - auto transaction = node->store.tx_begin_write (); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send2)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send3)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, open1)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, open2)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, open3)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send4)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send5)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive1)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send6)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive2)); - - // Check confirmation heights of all the accounts (except genesis) are uninitialized (0), - // as we have any just added them to the ledger and not processed any live transactions yet. - nano::confirmation_height_info confirmation_height_info; - ASSERT_FALSE (node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, confirmation_height_info)); - ASSERT_EQ (1, confirmation_height_info.height); - ASSERT_EQ (nano::dev::genesis->hash (), confirmation_height_info.frontier); - ASSERT_TRUE (node->store.confirmation_height.get (transaction, key1.pub, confirmation_height_info)); - ASSERT_EQ (0, confirmation_height_info.height); - ASSERT_EQ (nano::block_hash (0), confirmation_height_info.frontier); - ASSERT_TRUE (node->store.confirmation_height.get (transaction, key2.pub, confirmation_height_info)); - ASSERT_EQ (0, confirmation_height_info.height); - ASSERT_EQ (nano::block_hash (0), confirmation_height_info.frontier); - ASSERT_TRUE (node->store.confirmation_height.get (transaction, key3.pub, confirmation_height_info)); - ASSERT_EQ (0, confirmation_height_info.height); - ASSERT_EQ (nano::block_hash (0), confirmation_height_info.frontier); - } - - // The nodes process a live receive which propagates across to all accounts - auto receive3 = builder - .receive () - .previous (open3->hash ()) - .source (send6->hash ()) - .sign (key3.prv, key3.pub) - .work (*system.work.generate (open3->hash ())) - .build (); - node->process_active (receive3); - auto election = nano::test::start_election (system, *node, receive3->hash ()); - ASSERT_NE (nullptr, election); - election->force_confirm (); - - ASSERT_TIMELY_EQ (10s, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out), 10); - - nano::confirmation_height_info confirmation_height_info; - auto & store = node->store; - auto transaction = node->store.tx_begin_read (); - ASSERT_TRUE (node->ledger.block_confirmed (transaction, receive3->hash ())); - auto account_info = node->ledger.account_info (transaction, nano::dev::genesis_key.pub); - ASSERT_TRUE (account_info); - ASSERT_FALSE (node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, confirmation_height_info)); - ASSERT_EQ (4, confirmation_height_info.height); - ASSERT_EQ (send3->hash (), confirmation_height_info.frontier); - ASSERT_EQ (4, account_info->block_count); - account_info = node->ledger.account_info (transaction, key1.pub); - ASSERT_TRUE (account_info); - ASSERT_FALSE (node->store.confirmation_height.get (transaction, key1.pub, confirmation_height_info)); - ASSERT_EQ (2, confirmation_height_info.height); - ASSERT_EQ (send4->hash (), confirmation_height_info.frontier); - ASSERT_EQ (3, account_info->block_count); - account_info = node->ledger.account_info (transaction, key2.pub); - ASSERT_TRUE (account_info); - ASSERT_FALSE (node->store.confirmation_height.get (transaction, key2.pub, confirmation_height_info)); - ASSERT_EQ (3, confirmation_height_info.height); - ASSERT_EQ (send6->hash (), confirmation_height_info.frontier); - ASSERT_EQ (4, account_info->block_count); - account_info = node->ledger.account_info (transaction, key3.pub); - ASSERT_TRUE (account_info); - ASSERT_FALSE (node->store.confirmation_height.get (transaction, key3.pub, confirmation_height_info)); - ASSERT_EQ (2, confirmation_height_info.height); - ASSERT_EQ (receive3->hash (), confirmation_height_info.frontier); - ASSERT_EQ (2, account_info->block_count); - - // The accounts for key1 and key2 have 1 more block in the chain than is confirmed. - // So this can be rolled back, but the one before that cannot. Check that this is the case - { - auto transaction = node->store.tx_begin_write (); - ASSERT_FALSE (node->ledger.rollback (transaction, node->latest (key2.pub))); - ASSERT_FALSE (node->ledger.rollback (transaction, node->latest (key1.pub))); - } - { - // These rollbacks should fail - auto transaction = node->store.tx_begin_write (); - ASSERT_TRUE (node->ledger.rollback (transaction, node->latest (key1.pub))); - ASSERT_TRUE (node->ledger.rollback (transaction, node->latest (key2.pub))); - - // Confirm the other latest can't be rolled back either - ASSERT_TRUE (node->ledger.rollback (transaction, node->latest (key3.pub))); - ASSERT_TRUE (node->ledger.rollback (transaction, node->latest (nano::dev::genesis_key.pub))); - - // Attempt some others which have been cemented - ASSERT_TRUE (node->ledger.rollback (transaction, open1->hash ())); - ASSERT_TRUE (node->ledger.rollback (transaction, send2->hash ())); - } - ASSERT_EQ (10, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (10, node->stats.count (nano::stat::type::confirmation_height, get_stats_detail (mode_a), nano::stat::dir::in)); - ASSERT_EQ (10, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out)); - ASSERT_EQ (11, node->ledger.cache.cemented_count); - - ASSERT_EQ (0, node->active.election_winner_details_size ()); - }; - - test_mode (nano::confirmation_height_mode::bounded); - test_mode (nano::confirmation_height_mode::unbounded); -} - TEST (confirmation_height, gap_bootstrap) { auto test_mode = [] (nano::confirmation_height_mode mode_a) { @@ -572,838 +293,6 @@ TEST (confirmation_height, gap_live) test_mode (nano::confirmation_height_mode::unbounded); } -TEST (confirmation_height, send_receive_between_2_accounts) -{ - auto test_mode = [] (nano::confirmation_height_mode mode_a) { - nano::test::system system; - nano::node_flags node_flags; - node_flags.confirmation_height_processor_mode = mode_a; - nano::node_config node_config = system.default_config (); - node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - auto node = system.add_node (node_config, node_flags); - nano::keypair key1; - nano::block_hash latest (node->latest (nano::dev::genesis_key.pub)); - - nano::block_builder builder; - auto send1 = builder - .send () - .previous (latest) - .destination (key1.pub) - .balance (node->online_reps.delta () + 2) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (latest)) - .build (); - auto open1 = builder - .open () - .source (send1->hash ()) - .representative (nano::dev::genesis_key.pub) - .account (key1.pub) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (key1.pub)) - .build (); - auto send2 = builder - .send () - .previous (open1->hash ()) - .destination (nano::dev::genesis_key.pub) - .balance (1000) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (open1->hash ())) - .build (); - auto send3 = builder - .send () - .previous (send2->hash ()) - .destination (nano::dev::genesis_key.pub) - .balance (900) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (send2->hash ())) - .build (); - auto send4 = builder - .send () - .previous (send3->hash ()) - .destination (nano::dev::genesis_key.pub) - .balance (500) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (send3->hash ())) - .build (); - auto receive1 = builder - .receive () - .previous (send1->hash ()) - .source (send2->hash ()) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send1->hash ())) - .build (); - auto receive2 = builder - .receive () - .previous (receive1->hash ()) - .source (send3->hash ()) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (receive1->hash ())) - .build (); - auto receive3 = builder - .receive () - .previous (receive2->hash ()) - .source (send4->hash ()) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (receive2->hash ())) - .build (); - auto send5 = builder - .send () - .previous (receive3->hash ()) - .destination (key1.pub) - .balance (node->online_reps.delta () + 1) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (receive3->hash ())) - .build (); - auto receive4 = builder - .receive () - .previous (send4->hash ()) - .source (send5->hash ()) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (send4->hash ())) - .build (); - nano::keypair key2; - auto send6 = builder - .send () - .previous (send5->hash ()) - .destination (key2.pub) - .balance (node->online_reps.delta () + 1) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send5->hash ())) - .build (); - // Unpocketed send - { - auto transaction = node->store.tx_begin_write (); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, open1)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send2)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive1)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send3)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send4)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive2)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive3)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send5)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send6)); - } - - add_callback_stats (*node); - - node->process_active (receive4); - auto election = nano::test::start_election (system, *node, receive4->hash ()); - ASSERT_NE (nullptr, election); - election->force_confirm (); - - ASSERT_TIMELY_EQ (10s, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out), 10); - - auto transaction (node->store.tx_begin_read ()); - ASSERT_TRUE (node->ledger.block_confirmed (transaction, receive4->hash ())); - nano::confirmation_height_info confirmation_height_info; - auto account_info = node->ledger.account_info (transaction, nano::dev::genesis_key.pub); - ASSERT_TRUE (account_info); - ASSERT_FALSE (node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, confirmation_height_info)); - ASSERT_EQ (6, confirmation_height_info.height); - ASSERT_EQ (send5->hash (), confirmation_height_info.frontier); - ASSERT_EQ (7, account_info->block_count); - - account_info = node->ledger.account_info (transaction, key1.pub); - ASSERT_TRUE (account_info); - ASSERT_FALSE (node->store.confirmation_height.get (transaction, key1.pub, confirmation_height_info)); - ASSERT_EQ (5, confirmation_height_info.height); - ASSERT_EQ (receive4->hash (), confirmation_height_info.frontier); - ASSERT_EQ (5, account_info->block_count); - - ASSERT_EQ (10, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (10, node->stats.count (nano::stat::type::confirmation_height, get_stats_detail (mode_a), nano::stat::dir::in)); - ASSERT_EQ (10, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out)); - ASSERT_EQ (11, node->ledger.cache.cemented_count); - - ASSERT_EQ (0, node->active.election_winner_details_size ()); - }; - - test_mode (nano::confirmation_height_mode::bounded); - test_mode (nano::confirmation_height_mode::unbounded); -} - -TEST (confirmation_height, send_receive_self) -{ - auto test_mode = [] (nano::confirmation_height_mode mode_a) { - nano::test::system system; - nano::node_flags node_flags; - node_flags.confirmation_height_processor_mode = mode_a; - nano::node_config node_config = system.default_config (); - node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - auto node = system.add_node (node_config, node_flags); - nano::block_hash latest (node->latest (nano::dev::genesis_key.pub)); - - nano::block_builder builder; - auto send1 = builder - .send () - .previous (latest) - .destination (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - 2) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (latest)) - .build (); - auto receive1 = builder - .receive () - .previous (send1->hash ()) - .source (send1->hash ()) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send1->hash ())) - .build (); - auto send2 = builder - .send () - .previous (receive1->hash ()) - .destination (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - 2) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (receive1->hash ())) - .build (); - auto send3 = builder - .send () - .previous (send2->hash ()) - .destination (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - 3) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send2->hash ())) - .build (); - auto receive2 = builder - .receive () - .previous (send3->hash ()) - .source (send2->hash ()) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send3->hash ())) - .build (); - auto receive3 = builder - .receive () - .previous (receive2->hash ()) - .source (send3->hash ()) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (receive2->hash ())) - .build (); - - // Send to another account to prevent automatic receiving on the genesis account - nano::keypair key1; - auto send4 = builder - .send () - .previous (receive3->hash ()) - .destination (key1.pub) - .balance (node->online_reps.delta ()) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (receive3->hash ())) - .build (); - { - auto transaction = node->store.tx_begin_write (); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive1)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send2)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send3)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive2)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive3)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send4)); - } - - add_callback_stats (*node); - - auto election = nano::test::start_election (system, *node, receive3->hash ()); - ASSERT_NE (nullptr, election); - election->force_confirm (); - - ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out), 6); - - auto transaction (node->store.tx_begin_read ()); - ASSERT_TRUE (node->ledger.block_confirmed (transaction, receive3->hash ())); - auto account_info = node->ledger.account_info (transaction, nano::dev::genesis_key.pub); - ASSERT_TRUE (account_info); - nano::confirmation_height_info confirmation_height_info; - ASSERT_FALSE (node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, confirmation_height_info)); - ASSERT_EQ (7, confirmation_height_info.height); - ASSERT_EQ (receive3->hash (), confirmation_height_info.frontier); - ASSERT_EQ (8, account_info->block_count); - ASSERT_EQ (6, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (6, node->stats.count (nano::stat::type::confirmation_height, get_stats_detail (mode_a), nano::stat::dir::in)); - ASSERT_EQ (6, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out)); - ASSERT_EQ (confirmation_height_info.height, node->ledger.cache.cemented_count); - ASSERT_EQ (0, node->active.election_winner_details_size ()); - }; - - test_mode (nano::confirmation_height_mode::bounded); - test_mode (nano::confirmation_height_mode::unbounded); -} - -TEST (confirmation_height, all_block_types) -{ - auto test_mode = [] (nano::confirmation_height_mode mode_a) { - nano::test::system system; - nano::node_flags node_flags; - node_flags.confirmation_height_processor_mode = mode_a; - nano::node_config node_config = system.default_config (); - node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - auto node = system.add_node (node_config, node_flags); - nano::block_hash latest (node->latest (nano::dev::genesis_key.pub)); - nano::keypair key1; - nano::keypair key2; - auto & store = node->store; - nano::block_builder builder; - auto send = builder - .send () - .previous (latest) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (latest)) - .build (); - auto send1 = builder - .send () - .previous (send->hash ()) - .destination (key2.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send->hash ())) - .build (); - - auto open = builder - .open () - .source (send->hash ()) - .representative (nano::dev::genesis_key.pub) - .account (key1.pub) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (key1.pub)) - .build (); - auto state_open = builder - .state () - .account (key2.pub) - .previous (0) - .representative (0) - .balance (nano::Gxrb_ratio) - .link (send1->hash ()) - .sign (key2.prv, key2.pub) - .work (*system.work.generate (key2.pub)) - .build (); - - auto send2 = builder - .send () - .previous (open->hash ()) - .destination (key2.pub) - .balance (0) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (open->hash ())) - .build (); - auto state_receive = builder - .state () - .account (key2.pub) - .previous (state_open->hash ()) - .representative (0) - .balance (nano::Gxrb_ratio * 2) - .link (send2->hash ()) - .sign (key2.prv, key2.pub) - .work (*system.work.generate (state_open->hash ())) - .build (); - - auto state_send = builder - .state () - .account (key2.pub) - .previous (state_receive->hash ()) - .representative (0) - .balance (nano::Gxrb_ratio) - .link (key1.pub) - .sign (key2.prv, key2.pub) - .work (*system.work.generate (state_receive->hash ())) - .build (); - auto receive = builder - .receive () - .previous (send2->hash ()) - .source (state_send->hash ()) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (send2->hash ())) - .build (); - - auto change = builder - .change () - .previous (receive->hash ()) - .representative (key2.pub) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (receive->hash ())) - .build (); - - auto state_change = builder - .state () - .account (key2.pub) - .previous (state_send->hash ()) - .representative (nano::dev::genesis_key.pub) - .balance (nano::Gxrb_ratio) - .link (0) - .sign (key2.prv, key2.pub) - .work (*system.work.generate (state_send->hash ())) - .build (); - - auto epoch = builder - .state () - .account (key2.pub) - .previous (state_change->hash ()) - .representative (nano::dev::genesis_key.pub) - .balance (nano::Gxrb_ratio) - .link (node->ledger.epoch_link (nano::epoch::epoch_1)) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (state_change->hash ())) - .build (); - - auto epoch1 = builder - .state () - .account (key1.pub) - .previous (change->hash ()) - .representative (key2.pub) - .balance (nano::Gxrb_ratio) - .link (node->ledger.epoch_link (nano::epoch::epoch_1)) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (change->hash ())) - .build (); - auto state_send1 = builder - .state () - .account (key1.pub) - .previous (epoch1->hash ()) - .representative (0) - .balance (nano::Gxrb_ratio - 1) - .link (key2.pub) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (epoch1->hash ())) - .build (); - auto state_receive2 = builder - .state () - .account (key2.pub) - .previous (epoch->hash ()) - .representative (0) - .balance (nano::Gxrb_ratio + 1) - .link (state_send1->hash ()) - .sign (key2.prv, key2.pub) - .work (*system.work.generate (epoch->hash ())) - .build (); - - auto state_send2 = builder - .state () - .account (key2.pub) - .previous (state_receive2->hash ()) - .representative (0) - .balance (nano::Gxrb_ratio) - .link (key1.pub) - .sign (key2.prv, key2.pub) - .work (*system.work.generate (state_receive2->hash ())) - .build (); - auto state_send3 = builder - .state () - .account (key2.pub) - .previous (state_send2->hash ()) - .representative (0) - .balance (nano::Gxrb_ratio - 1) - .link (key1.pub) - .sign (key2.prv, key2.pub) - .work (*system.work.generate (state_send2->hash ())) - .build (); - - auto state_send4 = builder - .state () - .account (key1.pub) - .previous (state_send1->hash ()) - .representative (0) - .balance (nano::Gxrb_ratio - 2) - .link (nano::dev::genesis_key.pub) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (state_send1->hash ())) - .build (); - auto state_receive3 = builder - .state () - .account (nano::dev::genesis_key.pub) - .previous (send1->hash ()) - .representative (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2 + 1) - .link (state_send4->hash ()) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send1->hash ())) - .build (); - - { - auto transaction (store.tx_begin_write ()); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, open)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_open)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send2)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_receive)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_send)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, change)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_change)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, epoch)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, epoch1)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_send1)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_receive2)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_send2)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_send3)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_send4)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_receive3)); - } - - add_callback_stats (*node); - auto election = nano::test::start_election (system, *node, state_send2->hash ()); - ASSERT_NE (nullptr, election); - election->force_confirm (); - - ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out), 15); - - auto transaction (node->store.tx_begin_read ()); - ASSERT_TRUE (node->ledger.block_confirmed (transaction, state_send2->hash ())); - nano::confirmation_height_info confirmation_height_info; - auto account_info = node->ledger.account_info (transaction, nano::dev::genesis_key.pub); - ASSERT_TRUE (account_info); - ASSERT_FALSE (node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, confirmation_height_info)); - ASSERT_EQ (3, confirmation_height_info.height); - ASSERT_EQ (send1->hash (), confirmation_height_info.frontier); - ASSERT_LE (4, account_info->block_count); - - account_info = node->ledger.account_info (transaction, key1.pub); - ASSERT_TRUE (account_info); - ASSERT_FALSE (node->store.confirmation_height.get (transaction, key1.pub, confirmation_height_info)); - ASSERT_EQ (state_send1->hash (), confirmation_height_info.frontier); - ASSERT_EQ (6, confirmation_height_info.height); - ASSERT_LE (7, account_info->block_count); - - account_info = node->ledger.account_info (transaction, key2.pub); - ASSERT_TRUE (account_info); - ASSERT_FALSE (node->store.confirmation_height.get (transaction, key2.pub, confirmation_height_info)); - ASSERT_EQ (7, confirmation_height_info.height); - ASSERT_EQ (state_send2->hash (), confirmation_height_info.frontier); - ASSERT_LE (8, account_info->block_count); - - ASSERT_EQ (15, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (15, node->stats.count (nano::stat::type::confirmation_height, get_stats_detail (mode_a), nano::stat::dir::in)); - ASSERT_EQ (15, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out)); - ASSERT_EQ (16, node->ledger.cache.cemented_count); - - ASSERT_EQ (0, node->active.election_winner_details_size ()); - }; - - test_mode (nano::confirmation_height_mode::bounded); - test_mode (nano::confirmation_height_mode::unbounded); -} - -// This test ensures a block that's cemented cannot be rolled back by the node -// A block is inserted and confirmed then later a different block is force inserted with a rollback attempt -TEST (confirmation_height, conflict_rollback_cemented) -{ - // functor to perform the conflict_rollback_cemented test using a certain mode - auto test_mode = [] (nano::confirmation_height_mode mode_a) { - nano::state_block_builder builder; - auto const genesis_hash = nano::dev::genesis->hash (); - - nano::test::system system; - nano::node_flags node_flags; - node_flags.confirmation_height_processor_mode = mode_a; - auto node1 = system.add_node (node_flags); - - nano::keypair key1; - // create one side of a forked transaction on node1 - auto fork1a = builder.make_block () - .previous (genesis_hash) - .account (nano::dev::genesis_key.pub) - .representative (nano::dev::genesis_key.pub) - .link (key1.pub) - .balance (nano::dev::constants.genesis_amount - 100) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (genesis_hash)) - .build (); - ASSERT_EQ (nano::block_status::progress, node1->process (fork1a)); - ASSERT_TRUE (nano::test::start_elections (system, *node1, { fork1a }, true)); - ASSERT_TIMELY (5s, nano::test::confirmed (*node1, { fork1a })); - - // create the other side of the fork on node2 - nano::keypair key2; - auto fork1b = builder.make_block () - .previous (genesis_hash) - .account (nano::dev::genesis_key.pub) - .representative (nano::dev::genesis_key.pub) - .link (key2.pub) // Different destination same 'previous' - .balance (nano::dev::constants.genesis_amount - 100) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (genesis_hash)) - .build (); - - node1->block_processor.force (fork1b); - // node2 already has send2 forced confirmed whilst node1 should have confirmed send1 and therefore we have a cemented fork on node2 - // and node2 should print an error message on the log that it cannot rollback send2 because it is already cemented - [[maybe_unused]] size_t count = 0; - ASSERT_TIMELY_EQ (5s, 1, (count = node1->stats.count (nano::stat::type::ledger, nano::stat::detail::rollback_failed))); - ASSERT_TRUE (nano::test::confirmed (*node1, { fork1a->hash () })); // fork1a should still remain after the rollback failed event - }; - - test_mode (nano::confirmation_height_mode::bounded); - test_mode (nano::confirmation_height_mode::unbounded); -} - -TEST (confirmation_heightDeathTest, rollback_added_block) -{ - if (nano::rocksdb_config::using_rocksdb_in_tests ()) - { - // Don't test this in rocksdb mode - GTEST_SKIP (); - } - // For ASSERT_DEATH_IF_SUPPORTED - testing::FLAGS_gtest_death_test_style = "threadsafe"; - - // valgrind can be noisy with death tests - if (!nano::running_within_valgrind ()) - { - nano::logger logger; - auto path (nano::unique_path ()); - auto store = nano::make_store (logger, path, nano::dev::constants); - ASSERT_TRUE (!store->init_error ()); - nano::stats stats; - nano::ledger ledger (*store, stats, nano::dev::constants); - nano::write_database_queue write_database_queue (false); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; - nano::keypair key1; - nano::block_builder builder; - auto send = builder - .send () - .previous (nano::dev::genesis->hash ()) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*pool.generate (nano::dev::genesis->hash ())) - .build (); - { - auto transaction (store->tx_begin_write ()); - store->initialize (transaction, ledger.cache, ledger.constants); - } - - uint64_t batch_write_size = 2048; - std::atomic stopped{ false }; - nano::confirmation_height_unbounded unbounded_processor ( - ledger, write_database_queue, 10ms, logger, stopped, batch_write_size, [] (auto const &) {}, [] (auto const &) {}, [] () { return 0; }); - - // Processing a block which doesn't exist should bail - ASSERT_DEATH_IF_SUPPORTED (unbounded_processor.process (send), ""); - - nano::confirmation_height_bounded bounded_processor ( - ledger, write_database_queue, 10ms, logger, stopped, batch_write_size, [] (auto const &) {}, [] (auto const &) {}, [] () { return 0; }); - // Processing a block which doesn't exist should bail - ASSERT_DEATH_IF_SUPPORTED (bounded_processor.process (send), ""); - } -} - -TEST (confirmation_height, observers) -{ - auto test_mode = [] (nano::confirmation_height_mode mode_a) { - auto amount (std::numeric_limits::max ()); - nano::test::system system; - nano::node_flags node_flags; - node_flags.confirmation_height_processor_mode = mode_a; - auto node1 = system.add_node (node_flags); - nano::keypair key1; - system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv); - nano::block_hash latest1 (node1->latest (nano::dev::genesis_key.pub)); - nano::block_builder builder; - auto send1 = builder - .send () - .previous (latest1) - .destination (key1.pub) - .balance (amount - node1->config.receive_minimum.number ()) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (latest1)) - .build (); - - add_callback_stats (*node1); - - node1->process_active (send1); - ASSERT_TIMELY_EQ (10s, node1->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out), 1); - auto transaction = node1->store.tx_begin_read (); - ASSERT_TRUE (node1->ledger.block_confirmed (transaction, send1->hash ())); - ASSERT_EQ (1, node1->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (1, node1->stats.count (nano::stat::type::confirmation_height, get_stats_detail (mode_a), nano::stat::dir::in)); - ASSERT_EQ (1, node1->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out)); - ASSERT_EQ (2, node1->ledger.cache.cemented_count); - ASSERT_EQ (0, node1->active.election_winner_details_size ()); - }; - - test_mode (nano::confirmation_height_mode::bounded); - test_mode (nano::confirmation_height_mode::unbounded); -} - -// This tests when a read has been done, but the block no longer exists by the time a write is done -TEST (confirmation_heightDeathTest, modified_chain) -{ - if (nano::rocksdb_config::using_rocksdb_in_tests ()) - { - // Don't test this in rocksdb mode - GTEST_SKIP (); - } - // For ASSERT_DEATH_IF_SUPPORTED - testing::FLAGS_gtest_death_test_style = "threadsafe"; - - // valgrind can be noisy with death tests - if (!nano::running_within_valgrind ()) - { - nano::logger logger; - auto path (nano::unique_path ()); - auto store = nano::make_store (logger, path, nano::dev::constants); - ASSERT_TRUE (!store->init_error ()); - nano::stats stats; - nano::ledger ledger (*store, stats, nano::dev::constants); - nano::write_database_queue write_database_queue (false); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; - nano::keypair key1; - nano::block_builder builder; - auto send = builder - .send () - .previous (nano::dev::genesis->hash ()) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*pool.generate (nano::dev::genesis->hash ())) - .build (); - { - auto transaction (store->tx_begin_write ()); - store->initialize (transaction, ledger.cache, ledger.constants); - ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send)); - } - - uint64_t batch_write_size = 2048; - std::atomic stopped{ false }; - nano::confirmation_height_bounded bounded_processor ( - ledger, write_database_queue, 10ms, logger, stopped, batch_write_size, [] (auto const &) {}, [] (auto const &) {}, [] () { return 0; }); - - { - // This reads the blocks in the account, but prevents any writes from occurring yet - auto scoped_write_guard = write_database_queue.wait (nano::writer::testing); - bounded_processor.process (send); - } - - // Rollback the block and now try to write, the block no longer exists so should bail - ledger.rollback (store->tx_begin_write (), send->hash ()); - { - auto scoped_write_guard = write_database_queue.wait (nano::writer::confirmation_height); - ASSERT_DEATH_IF_SUPPORTED (bounded_processor.cement_blocks (scoped_write_guard), ""); - } - - ASSERT_EQ (nano::block_status::progress, ledger.process (store->tx_begin_write (), send)); - store->confirmation_height.put (store->tx_begin_write (), nano::dev::genesis_key.pub, { 1, nano::dev::genesis->hash () }); - - nano::confirmation_height_unbounded unbounded_processor ( - ledger, write_database_queue, 10ms, logger, stopped, batch_write_size, [] (auto const &) {}, [] (auto const &) {}, [] () { return 0; }); - - { - // This reads the blocks in the account, but prevents any writes from occurring yet - auto scoped_write_guard = write_database_queue.wait (nano::writer::testing); - unbounded_processor.process (send); - } - - // Rollback the block and now try to write, the block no longer exists so should bail - ledger.rollback (store->tx_begin_write (), send->hash ()); - { - auto scoped_write_guard = write_database_queue.wait (nano::writer::confirmation_height); - ASSERT_DEATH_IF_SUPPORTED (unbounded_processor.cement_blocks (scoped_write_guard), ""); - } - } -} - -// This tests when a read has been done, but the account no longer exists by the time a write is done -TEST (confirmation_heightDeathTest, modified_chain_account_removed) -{ - if (nano::rocksdb_config::using_rocksdb_in_tests ()) - { - // Don't test this in rocksdb mode - GTEST_SKIP (); - } - // For ASSERT_DEATH_IF_SUPPORTED - testing::FLAGS_gtest_death_test_style = "threadsafe"; - - // valgrind can be noisy with death tests - if (!nano::running_within_valgrind ()) - { - nano::logger logger; - auto path (nano::unique_path ()); - auto store = nano::make_store (logger, path, nano::dev::constants); - ASSERT_TRUE (!store->init_error ()); - nano::stats stats; - nano::ledger ledger (*store, stats, nano::dev::constants); - nano::write_database_queue write_database_queue (false); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; - nano::keypair key1; - nano::block_builder builder; - auto send = builder - .send () - .previous (nano::dev::genesis->hash ()) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*pool.generate (nano::dev::genesis->hash ())) - .build (); - auto open = builder - .state () - .account (key1.pub) - .previous (0) - .representative (0) - .balance (nano::Gxrb_ratio) - .link (send->hash ()) - .sign (key1.prv, key1.pub) - .work (*pool.generate (key1.pub)) - .build (); - { - auto transaction (store->tx_begin_write ()); - store->initialize (transaction, ledger.cache, ledger.constants); - ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send)); - ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open)); - } - - uint64_t batch_write_size = 2048; - std::atomic stopped{ false }; - nano::confirmation_height_unbounded unbounded_processor ( - ledger, write_database_queue, 10ms, logger, stopped, batch_write_size, [] (auto const &) {}, [] (auto const &) {}, [] () { return 0; }); - - { - // This reads the blocks in the account, but prevents any writes from occurring yet - auto scoped_write_guard = write_database_queue.wait (nano::writer::testing); - unbounded_processor.process (open); - } - - // Rollback the block and now try to write, the send should be cemented but the account which the open block belongs no longer exists so should bail - ledger.rollback (store->tx_begin_write (), open->hash ()); - { - auto scoped_write_guard = write_database_queue.wait (nano::writer::confirmation_height); - ASSERT_DEATH_IF_SUPPORTED (unbounded_processor.cement_blocks (scoped_write_guard), ""); - } - - // Reset conditions and test with the bounded processor - ASSERT_EQ (nano::block_status::progress, ledger.process (store->tx_begin_write (), open)); - store->confirmation_height.put (store->tx_begin_write (), nano::dev::genesis_key.pub, { 1, nano::dev::genesis->hash () }); - - nano::confirmation_height_bounded bounded_processor ( - ledger, write_database_queue, 10ms, logger, stopped, batch_write_size, [] (auto const &) {}, [] (auto const &) {}, [] () { return 0; }); - - { - // This reads the blocks in the account, but prevents any writes from occurring yet - auto scoped_write_guard = write_database_queue.wait (nano::writer::testing); - bounded_processor.process (open); - } - - // Rollback the block and now try to write, the send should be cemented but the account which the open block belongs no longer exists so should bail - ledger.rollback (store->tx_begin_write (), open->hash ()); - auto scoped_write_guard = write_database_queue.wait (nano::writer::confirmation_height); - ASSERT_DEATH_IF_SUPPORTED (bounded_processor.cement_blocks (scoped_write_guard), ""); - } -} - -namespace nano -{ TEST (confirmation_height, pending_observer_callbacks) { auto test_mode = [] (nano::confirmation_height_mode mode_a) { @@ -1459,7 +348,6 @@ TEST (confirmation_height, pending_observer_callbacks) test_mode (nano::confirmation_height_mode::bounded); test_mode (nano::confirmation_height_mode::unbounded); } -} // The callback and confirmation history should only be updated after confirmation height is set (and not just after voting) TEST (confirmation_height, callback_confirmed_history) @@ -1940,72 +828,6 @@ TEST (confirmation_height, cemented_gap_below_no_cache) test_mode (nano::confirmation_height_mode::bounded); test_mode (nano::confirmation_height_mode::unbounded); } - -TEST (confirmation_height, election_winner_details_clearing) -{ - auto test_mode = [] (nano::confirmation_height_mode mode_a) { - nano::test::system system{}; - - nano::node_flags node_flags{}; - node_flags.confirmation_height_processor_mode = mode_a; - - nano::node_config node_config = system.default_config (); - node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - - auto node = system.add_node (node_config, node_flags); - auto const latest = node->latest (nano::dev::genesis_key.pub); - - nano::keypair key1{}; - nano::send_block_builder builder{}; - - auto const send1 = builder.make_block () - .previous (latest) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (latest)) - .build (); - ASSERT_EQ (nano::block_status::progress, node->process (send1)); - - auto const send2 = builder.make_block () - .previous (send1->hash ()) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send1->hash ())) - .build (); - ASSERT_EQ (nano::block_status::progress, node->process (send2)); - - auto const send3 = builder.make_block () - .previous (send2->hash ()) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 3) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send2->hash ())) - .build (); - ASSERT_EQ (nano::block_status::progress, node->process (send3)); - - node->process_confirmed (nano::election_status{ send2 }); - ASSERT_TIMELY (5s, node->block_confirmed (send2->hash ())); - ASSERT_TIMELY_EQ (5s, 1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::inactive_conf_height, nano::stat::dir::out)); - - node->process_confirmed (nano::election_status{ send3 }); - ASSERT_TIMELY (5s, node->block_confirmed (send3->hash ())); - - // Add an already cemented block with fake election details. It should get removed - node->active.add_election_winner_details (send3->hash (), nullptr); - node->confirmation_height_processor.add (send3); - ASSERT_TIMELY_EQ (10s, node->active.election_winner_details_size (), 0); - ASSERT_EQ (4, node->ledger.cache.cemented_count); - - EXPECT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::inactive_conf_height, nano::stat::dir::out)); - EXPECT_EQ (3, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - EXPECT_EQ (3, node->stats.count (nano::stat::type::confirmation_height, get_stats_detail (mode_a), nano::stat::dir::in)); - }; - - test_mode (nano::confirmation_height_mode::bounded); - test_mode (nano::confirmation_height_mode::unbounded); -} } TEST (confirmation_height, election_winner_details_clearing_node_process_confirmed) @@ -2098,91 +920,3 @@ TEST (confirmation_height, unbounded_block_cache_iteration) ASSERT_EQ (2, stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed_unbounded, nano::stat::dir::in)); ASSERT_EQ (3, ledger.cache.cemented_count); } - -TEST (confirmation_height, pruned_source) -{ - nano::logger logger; - auto path (nano::unique_path ()); - auto store = nano::make_store (logger, path, nano::dev::constants); - ASSERT_TRUE (!store->init_error ()); - nano::stats stats; - nano::ledger ledger (*store, stats, nano::dev::constants); - ledger.pruning = true; - nano::write_database_queue write_database_queue (false); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; - nano::keypair key1, key2; - nano::block_builder builder; - auto send1 = builder - .state () - .account (nano::dev::genesis_key.pub) - .previous (nano::dev::genesis->hash ()) - .representative (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - 100) - .link (key1.pub) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*pool.generate (nano::dev::genesis->hash ())) - .build (); - auto open1 = builder - .state () - .account (key1.pub) - .previous (0) - .representative (key1.pub) - .balance (100) - .link (send1->hash ()) - .sign (key1.prv, key1.pub) - .work (*pool.generate (key1.pub)) - .build (); - auto send2 = builder - .state () - .account (key1.pub) - .previous (open1->hash ()) - .representative (key1.pub) - .balance (50) - .link (key2.pub) - .sign (key1.prv, key1.pub) - .work (*pool.generate (open1->hash ())) - .build (); - auto send3 = builder - .state () - .account (key1.pub) - .previous (send2->hash ()) - .representative (key1.pub) - .balance (25) - .link (key2.pub) - .sign (key1.prv, key1.pub) - .work (*pool.generate (send2->hash ())) - .build (); - auto open2 = builder - .state () - .account (key2.pub) - .previous (0) - .representative (key1.pub) - .balance (50) - .link (send2->hash ()) - .sign (key2.prv, key2.pub) - .work (*pool.generate (key2.pub)) - .build (); - { - auto transaction (store->tx_begin_write ()); - store->initialize (transaction, ledger.cache, nano::dev::constants); - ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send1)); - ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open1)); - ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2)); - ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send3)); - ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open2)); - } - uint64_t batch_write_size = 2; - std::atomic stopped{ false }; - bool first_time{ true }; - nano::confirmation_height_bounded bounded_processor ( - ledger, write_database_queue, 10ms, logger, stopped, batch_write_size, [&] (auto const & cemented_blocks_a) { - if (first_time) - { - // Prune the send - auto transaction (store->tx_begin_write ()); - ASSERT_EQ (2, ledger.pruning_action (transaction, send2->hash (), 2)); - } - first_time = false; }, - [] (auto const &) {}, [] () { return 0; }); - bounded_processor.process (open2); -} diff --git a/nano/core_test/ledger_confirm.cpp b/nano/core_test/ledger_confirm.cpp new file mode 100644 index 000000000..f22e36b0e --- /dev/null +++ b/nano/core_test/ledger_confirm.cpp @@ -0,0 +1,886 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std::chrono_literals; + +TEST (ledger_confirm, single) +{ + auto amount (std::numeric_limits::max ()); + nano::test::system system; + nano::node_flags node_flags; + auto node = system.add_node (node_flags); + nano::keypair key1; + system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv); + nano::block_hash latest1 (node->latest (nano::dev::genesis_key.pub)); + nano::block_builder builder; + auto send1 = builder + .state () + .account (nano::dev::genesis_key.pub) + .previous (latest1) + .representative (nano::dev::genesis_key.pub) + .balance (amount - 100) + .link (key1.pub) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (latest1)) + .build (); + + // Check confirmation heights before, should be uninitialized (1 for genesis). + auto transaction = node->store.tx_begin_write (); + ASSERT_EQ (1, node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().height); + ASSERT_EQ (nano::dev::genesis->hash (), node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().frontier); + + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1)); + ASSERT_FALSE (node->ledger.block_confirmed (transaction, send1->hash ())); + node->ledger.confirm (transaction, send1->hash ()); + ASSERT_TRUE (node->ledger.block_confirmed (transaction, send1->hash ())); + ASSERT_EQ (2, node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().height); + ASSERT_EQ (send1->hash (), node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().frontier); + + // Rollbacks should fail as these blocks have been cemented + ASSERT_TRUE (node->ledger.rollback (transaction, latest1)); + ASSERT_TRUE (node->ledger.rollback (transaction, send1->hash ())); + ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); + ASSERT_EQ (2, node->ledger.cache.cemented_count); +} + +TEST (ledger_confirm, multiple_accounts) +{ + nano::test::system system; + nano::node_flags node_flags; + nano::node_config node_config = system.default_config (); + node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; + auto node = system.add_node (node_config, node_flags); + nano::keypair key1; + nano::keypair key2; + nano::keypair key3; + nano::block_hash latest1 (system.nodes[0]->latest (nano::dev::genesis_key.pub)); + nano::block_builder builder; + + // Send to all accounts + auto send1 = builder + .send () + .previous (latest1) + .destination (key1.pub) + .balance (node->online_reps.delta () + 300) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (latest1)) + .build (); + auto send2 = builder + .send () + .previous (send1->hash ()) + .destination (key2.pub) + .balance (node->online_reps.delta () + 200) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (send1->hash ())) + .build (); + auto send3 = builder + .send () + .previous (send2->hash ()) + .destination (key3.pub) + .balance (node->online_reps.delta () + 100) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (send2->hash ())) + .build (); + + // Open all accounts + auto open1 = builder + .open () + .source (send1->hash ()) + .representative (nano::dev::genesis_key.pub) + .account (key1.pub) + .sign (key1.prv, key1.pub) + .work (*system.work.generate (key1.pub)) + .build (); + auto open2 = builder + .open () + .source (send2->hash ()) + .representative (nano::dev::genesis_key.pub) + .account (key2.pub) + .sign (key2.prv, key2.pub) + .work (*system.work.generate (key2.pub)) + .build (); + auto open3 = builder + .open () + .source (send3->hash ()) + .representative (nano::dev::genesis_key.pub) + .account (key3.pub) + .sign (key3.prv, key3.pub) + .work (*system.work.generate (key3.pub)) + .build (); + + // Send and receive various blocks to these accounts + auto send4 = builder + .send () + .previous (open1->hash ()) + .destination (key2.pub) + .balance (50) + .sign (key1.prv, key1.pub) + .work (*system.work.generate (open1->hash ())) + .build (); + auto send5 = builder + .send () + .previous (send4->hash ()) + .destination (key2.pub) + .balance (10) + .sign (key1.prv, key1.pub) + .work (*system.work.generate (send4->hash ())) + .build (); + + auto receive1 = builder + .receive () + .previous (open2->hash ()) + .source (send4->hash ()) + .sign (key2.prv, key2.pub) + .work (*system.work.generate (open2->hash ())) + .build (); + auto send6 = builder + .send () + .previous (receive1->hash ()) + .destination (key3.pub) + .balance (10) + .sign (key2.prv, key2.pub) + .work (*system.work.generate (receive1->hash ())) + .build (); + auto receive2 = builder + .receive () + .previous (send6->hash ()) + .source (send5->hash ()) + .sign (key2.prv, key2.pub) + .work (*system.work.generate (send6->hash ())) + .build (); + + auto transaction = node->store.tx_begin_write (); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send2)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send3)); + + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, open1)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, open2)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, open3)); + + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send4)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send5)); + + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive1)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send6)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive2)); + + // Check confirmation heights of all the accounts (except genesis) are uninitialized (0), + // as we have any just added them to the ledger and not processed any live transactions yet. + ASSERT_EQ (1, node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().height); + ASSERT_EQ (nano::dev::genesis->hash (), node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().frontier); + ASSERT_FALSE (node->store.confirmation_height.get (transaction, key1.pub)); + ASSERT_FALSE (node->store.confirmation_height.get (transaction, key2.pub)); + ASSERT_FALSE (node->store.confirmation_height.get (transaction, key3.pub)); + + // The nodes process a live receive which propagates across to all accounts + auto receive3 = builder + .receive () + .previous (open3->hash ()) + .source (send6->hash ()) + .sign (key3.prv, key3.pub) + .work (*system.work.generate (open3->hash ())) + .build (); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive3)); + auto confirmed = node->ledger.confirm (transaction, receive3->hash ()); + ASSERT_EQ (10, confirmed.size ()); + ASSERT_EQ (10, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); + ASSERT_EQ (11, node->ledger.cache.cemented_count); + + ASSERT_TRUE (node->ledger.block_confirmed (transaction, receive3->hash ())); + ASSERT_EQ (4, node->ledger.account_info (transaction, nano::dev::genesis_key.pub).value ().block_count); + ASSERT_EQ (4, node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().height); + ASSERT_EQ (send3->hash (), node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().frontier); + ASSERT_EQ (3, node->ledger.account_info (transaction, key1.pub).value ().block_count); + ASSERT_EQ (2, node->store.confirmation_height.get (transaction, key1.pub).value ().height); + ASSERT_EQ (send4->hash (), node->store.confirmation_height.get (transaction, key1.pub).value ().frontier); + ASSERT_EQ (4, node->ledger.account_info (transaction, key2.pub).value ().block_count); + ASSERT_EQ (3, node->store.confirmation_height.get (transaction, key2.pub).value ().height); + ASSERT_EQ (send6->hash (), node->store.confirmation_height.get (transaction, key2.pub).value ().frontier); + ASSERT_EQ (2, node->ledger.account_info (transaction, key3.pub).value ().block_count); + ASSERT_EQ (2, node->store.confirmation_height.get (transaction, key3.pub).value ().height); + ASSERT_EQ (receive3->hash (), node->store.confirmation_height.get (transaction, key3.pub).value ().frontier); + + // The accounts for key1 and key2 have 1 more block in the chain than is confirmed. + // So this can be rolled back, but the one before that cannot. Check that this is the case + ASSERT_FALSE (node->ledger.rollback (transaction, node->ledger.latest (transaction, key2.pub))); + ASSERT_FALSE (node->ledger.rollback (transaction, node->ledger.latest (transaction, key1.pub))); + ASSERT_TRUE (node->ledger.rollback (transaction, node->ledger.latest (transaction, key1.pub))); + ASSERT_TRUE (node->ledger.rollback (transaction, node->ledger.latest (transaction, key2.pub))); + + // Confirm the other latest can't be rolled back either + ASSERT_TRUE (node->ledger.rollback (transaction, node->ledger.latest (transaction, key3.pub))); + ASSERT_TRUE (node->ledger.rollback (transaction, node->ledger.latest (transaction, nano::dev::genesis_key.pub))); + + // Attempt some others which have been cemented + ASSERT_TRUE (node->ledger.rollback (transaction, open1->hash ())); + ASSERT_TRUE (node->ledger.rollback (transaction, send2->hash ())); +} + +TEST (ledger_confirm, send_receive_between_2_accounts) +{ + nano::test::system system; + nano::node_flags node_flags; + nano::node_config node_config = system.default_config (); + node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; + auto node = system.add_node (node_config, node_flags); + nano::keypair key1; + nano::block_hash latest (node->latest (nano::dev::genesis_key.pub)); + + nano::block_builder builder; + auto send1 = builder + .send () + .previous (latest) + .destination (key1.pub) + .balance (node->online_reps.delta () + 2) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (latest)) + .build (); + auto open1 = builder + .open () + .source (send1->hash ()) + .representative (nano::dev::genesis_key.pub) + .account (key1.pub) + .sign (key1.prv, key1.pub) + .work (*system.work.generate (key1.pub)) + .build (); + auto send2 = builder + .send () + .previous (open1->hash ()) + .destination (nano::dev::genesis_key.pub) + .balance (1000) + .sign (key1.prv, key1.pub) + .work (*system.work.generate (open1->hash ())) + .build (); + auto send3 = builder + .send () + .previous (send2->hash ()) + .destination (nano::dev::genesis_key.pub) + .balance (900) + .sign (key1.prv, key1.pub) + .work (*system.work.generate (send2->hash ())) + .build (); + auto send4 = builder + .send () + .previous (send3->hash ()) + .destination (nano::dev::genesis_key.pub) + .balance (500) + .sign (key1.prv, key1.pub) + .work (*system.work.generate (send3->hash ())) + .build (); + auto receive1 = builder + .receive () + .previous (send1->hash ()) + .source (send2->hash ()) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (send1->hash ())) + .build (); + auto receive2 = builder + .receive () + .previous (receive1->hash ()) + .source (send3->hash ()) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (receive1->hash ())) + .build (); + auto receive3 = builder + .receive () + .previous (receive2->hash ()) + .source (send4->hash ()) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (receive2->hash ())) + .build (); + auto send5 = builder + .send () + .previous (receive3->hash ()) + .destination (key1.pub) + .balance (node->online_reps.delta () + 1) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (receive3->hash ())) + .build (); + auto receive4 = builder + .receive () + .previous (send4->hash ()) + .source (send5->hash ()) + .sign (key1.prv, key1.pub) + .work (*system.work.generate (send4->hash ())) + .build (); + nano::keypair key2; + auto send6 = builder + .send () + .previous (send5->hash ()) + .destination (key2.pub) + .balance (node->online_reps.delta () + 1) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (send5->hash ())) + .build (); + // Unpocketed send + + auto transaction = node->store.tx_begin_write (); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, open1)); + + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send2)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive1)); + + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send3)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send4)); + + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive2)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive3)); + + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send5)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send6)); + + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive4)); + auto confirmed = node->ledger.confirm (transaction, receive4->hash ()); + ASSERT_EQ (10, confirmed.size ()); + ASSERT_EQ (10, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); + ASSERT_EQ (11, node->ledger.cache.cemented_count); + + ASSERT_TRUE (node->ledger.block_confirmed (transaction, receive4->hash ())); + ASSERT_EQ (7, node->ledger.account_info (transaction, nano::dev::genesis_key.pub).value ().block_count); + ASSERT_EQ (6, node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().height); + ASSERT_EQ (send5->hash (), node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().frontier); + + ASSERT_EQ (5, node->ledger.account_info (transaction, key1.pub).value ().block_count); + ASSERT_EQ (5, node->store.confirmation_height.get (transaction, key1.pub).value ().height); + ASSERT_EQ (receive4->hash (), node->store.confirmation_height.get (transaction, key1.pub).value ().frontier); +} + +TEST (ledger_confirm, send_receive_self) +{ + nano::test::system system; + nano::node_flags node_flags; + nano::node_config node_config = system.default_config (); + node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; + auto node = system.add_node (node_config, node_flags); + nano::block_hash latest (node->latest (nano::dev::genesis_key.pub)); + + nano::block_builder builder; + auto send1 = builder + .send () + .previous (latest) + .destination (nano::dev::genesis_key.pub) + .balance (nano::dev::constants.genesis_amount - 2) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (latest)) + .build (); + auto receive1 = builder + .receive () + .previous (send1->hash ()) + .source (send1->hash ()) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (send1->hash ())) + .build (); + auto send2 = builder + .send () + .previous (receive1->hash ()) + .destination (nano::dev::genesis_key.pub) + .balance (nano::dev::constants.genesis_amount - 2) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (receive1->hash ())) + .build (); + auto send3 = builder + .send () + .previous (send2->hash ()) + .destination (nano::dev::genesis_key.pub) + .balance (nano::dev::constants.genesis_amount - 3) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (send2->hash ())) + .build (); + auto receive2 = builder + .receive () + .previous (send3->hash ()) + .source (send2->hash ()) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (send3->hash ())) + .build (); + auto receive3 = builder + .receive () + .previous (receive2->hash ()) + .source (send3->hash ()) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (receive2->hash ())) + .build (); + + // Send to another account to prevent automatic receiving on the genesis account + nano::keypair key1; + auto send4 = builder + .send () + .previous (receive3->hash ()) + .destination (key1.pub) + .balance (node->online_reps.delta ()) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (receive3->hash ())) + .build (); + + auto transaction = node->store.tx_begin_write (); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive1)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send2)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send3)); + + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive2)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive3)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send4)); + + auto confirmed = node->ledger.confirm (transaction, receive3->hash ()); + ASSERT_EQ (6, confirmed.size ()); + ASSERT_EQ (6, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); + + ASSERT_TRUE (node->ledger.block_confirmed (transaction, receive3->hash ())); + ASSERT_EQ (8, node->ledger.account_info (transaction, nano::dev::genesis_key.pub).value ().block_count); + ASSERT_EQ (7, node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().height); + ASSERT_EQ (receive3->hash (), node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().frontier); + ASSERT_EQ (7, node->ledger.cache.cemented_count); +} + +TEST (ledger_confirm, all_block_types) +{ + nano::test::system system; + nano::node_flags node_flags; + nano::node_config node_config = system.default_config (); + node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; + auto node = system.add_node (node_config, node_flags); + nano::block_hash latest (node->latest (nano::dev::genesis_key.pub)); + nano::keypair key1; + nano::keypair key2; + auto & store = node->store; + nano::block_builder builder; + auto send = builder + .send () + .previous (latest) + .destination (key1.pub) + .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (latest)) + .build (); + auto send1 = builder + .send () + .previous (send->hash ()) + .destination (key2.pub) + .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (send->hash ())) + .build (); + + auto open = builder + .open () + .source (send->hash ()) + .representative (nano::dev::genesis_key.pub) + .account (key1.pub) + .sign (key1.prv, key1.pub) + .work (*system.work.generate (key1.pub)) + .build (); + auto state_open = builder + .state () + .account (key2.pub) + .previous (0) + .representative (0) + .balance (nano::Gxrb_ratio) + .link (send1->hash ()) + .sign (key2.prv, key2.pub) + .work (*system.work.generate (key2.pub)) + .build (); + + auto send2 = builder + .send () + .previous (open->hash ()) + .destination (key2.pub) + .balance (0) + .sign (key1.prv, key1.pub) + .work (*system.work.generate (open->hash ())) + .build (); + auto state_receive = builder + .state () + .account (key2.pub) + .previous (state_open->hash ()) + .representative (0) + .balance (nano::Gxrb_ratio * 2) + .link (send2->hash ()) + .sign (key2.prv, key2.pub) + .work (*system.work.generate (state_open->hash ())) + .build (); + + auto state_send = builder + .state () + .account (key2.pub) + .previous (state_receive->hash ()) + .representative (0) + .balance (nano::Gxrb_ratio) + .link (key1.pub) + .sign (key2.prv, key2.pub) + .work (*system.work.generate (state_receive->hash ())) + .build (); + auto receive = builder + .receive () + .previous (send2->hash ()) + .source (state_send->hash ()) + .sign (key1.prv, key1.pub) + .work (*system.work.generate (send2->hash ())) + .build (); + + auto change = builder + .change () + .previous (receive->hash ()) + .representative (key2.pub) + .sign (key1.prv, key1.pub) + .work (*system.work.generate (receive->hash ())) + .build (); + + auto state_change = builder + .state () + .account (key2.pub) + .previous (state_send->hash ()) + .representative (nano::dev::genesis_key.pub) + .balance (nano::Gxrb_ratio) + .link (0) + .sign (key2.prv, key2.pub) + .work (*system.work.generate (state_send->hash ())) + .build (); + + auto epoch = builder + .state () + .account (key2.pub) + .previous (state_change->hash ()) + .representative (nano::dev::genesis_key.pub) + .balance (nano::Gxrb_ratio) + .link (node->ledger.epoch_link (nano::epoch::epoch_1)) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (state_change->hash ())) + .build (); + + auto epoch1 = builder + .state () + .account (key1.pub) + .previous (change->hash ()) + .representative (key2.pub) + .balance (nano::Gxrb_ratio) + .link (node->ledger.epoch_link (nano::epoch::epoch_1)) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (change->hash ())) + .build (); + auto state_send1 = builder + .state () + .account (key1.pub) + .previous (epoch1->hash ()) + .representative (0) + .balance (nano::Gxrb_ratio - 1) + .link (key2.pub) + .sign (key1.prv, key1.pub) + .work (*system.work.generate (epoch1->hash ())) + .build (); + auto state_receive2 = builder + .state () + .account (key2.pub) + .previous (epoch->hash ()) + .representative (0) + .balance (nano::Gxrb_ratio + 1) + .link (state_send1->hash ()) + .sign (key2.prv, key2.pub) + .work (*system.work.generate (epoch->hash ())) + .build (); + + auto state_send2 = builder + .state () + .account (key2.pub) + .previous (state_receive2->hash ()) + .representative (0) + .balance (nano::Gxrb_ratio) + .link (key1.pub) + .sign (key2.prv, key2.pub) + .work (*system.work.generate (state_receive2->hash ())) + .build (); + auto state_send3 = builder + .state () + .account (key2.pub) + .previous (state_send2->hash ()) + .representative (0) + .balance (nano::Gxrb_ratio - 1) + .link (key1.pub) + .sign (key2.prv, key2.pub) + .work (*system.work.generate (state_send2->hash ())) + .build (); + + auto state_send4 = builder + .state () + .account (key1.pub) + .previous (state_send1->hash ()) + .representative (0) + .balance (nano::Gxrb_ratio - 2) + .link (nano::dev::genesis_key.pub) + .sign (key1.prv, key1.pub) + .work (*system.work.generate (state_send1->hash ())) + .build (); + auto state_receive3 = builder + .state () + .account (nano::dev::genesis_key.pub) + .previous (send1->hash ()) + .representative (nano::dev::genesis_key.pub) + .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2 + 1) + .link (state_send4->hash ()) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (send1->hash ())) + .build (); + + auto transaction (store.tx_begin_write ()); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, open)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_open)); + + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send2)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_receive)); + + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_send)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, change)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_change)); + + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, epoch)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, epoch1)); + + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_send1)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_receive2)); + + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_send2)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_send3)); + + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_send4)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, state_receive3)); + + auto confirmed = node->ledger.confirm (transaction, state_send2->hash ()); + ASSERT_EQ (15, confirmed.size ()); + ASSERT_EQ (15, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); + ASSERT_EQ (16, node->ledger.cache.cemented_count); + + ASSERT_TRUE (node->ledger.block_confirmed (transaction, state_send2->hash ())); + nano::confirmation_height_info confirmation_height_info; + ASSERT_LE (4, node->ledger.account_info (transaction, nano::dev::genesis_key.pub).value ().block_count); + ASSERT_EQ (3, node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().height); + ASSERT_EQ (send1->hash (), node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().frontier); + + ASSERT_LE (7, node->ledger.account_info (transaction, key1.pub).value ().block_count); + ASSERT_EQ (6, node->store.confirmation_height.get (transaction, key1.pub).value ().height); + ASSERT_EQ (state_send1->hash (), node->store.confirmation_height.get (transaction, key1.pub).value ().frontier); + + ASSERT_EQ (8, node->ledger.account_info (transaction, key2.pub).value ().block_count); + ASSERT_EQ (7, node->store.confirmation_height.get (transaction, key2.pub).value ().height); + ASSERT_EQ (state_send2->hash (), node->store.confirmation_height.get (transaction, key2.pub).value ().frontier); +} + +// This test ensures a block that's cemented cannot be rolled back by the node +// A block is inserted and confirmed then later a different block is force inserted with a rollback attempt +TEST (ledger_confirm, conflict_rollback_cemented) +{ + nano::state_block_builder builder; + auto const genesis_hash = nano::dev::genesis->hash (); + + nano::test::system system; + nano::node_flags node_flags; + auto node1 = system.add_node (node_flags); + + nano::keypair key1; + // create one side of a forked transaction on node1 + auto fork1a = builder.make_block () + .previous (genesis_hash) + .account (nano::dev::genesis_key.pub) + .representative (nano::dev::genesis_key.pub) + .link (key1.pub) + .balance (nano::dev::constants.genesis_amount - 100) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (genesis_hash)) + .build (); + { + auto transaction = node1->store.tx_begin_write (); + ASSERT_EQ (nano::block_status::progress, node1->ledger.process (transaction, fork1a)); + node1->ledger.confirm (transaction, fork1a->hash ()); + } + ASSERT_TRUE (nano::test::confirmed (*node1, { fork1a })); + + // create the other side of the fork on node2 + nano::keypair key2; + auto fork1b = builder.make_block () + .previous (genesis_hash) + .account (nano::dev::genesis_key.pub) + .representative (nano::dev::genesis_key.pub) + .link (key2.pub) // Different destination same 'previous' + .balance (nano::dev::constants.genesis_amount - 100) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (genesis_hash)) + .build (); + + node1->block_processor.force (fork1b); + // node2 already has send2 forced confirmed whilst node1 should have confirmed send1 and therefore we have a cemented fork on node2 + // and node2 should print an error message on the log that it cannot rollback send2 because it is already cemented + [[maybe_unused]] size_t count = 0; + ASSERT_TIMELY_EQ (5s, 1, (count = node1->stats.count (nano::stat::type::ledger, nano::stat::detail::rollback_failed))); + ASSERT_TRUE (nano::test::confirmed (*node1, { fork1a->hash () })); // fork1a should still remain after the rollback failed event +} + +TEST (ledger_confirm, observers) +{ + auto amount (std::numeric_limits::max ()); + nano::test::system system; + nano::node_flags node_flags; + auto node1 = system.add_node (node_flags); + nano::keypair key1; + nano::block_hash latest1 (node1->latest (nano::dev::genesis_key.pub)); + nano::block_builder builder; + auto send1 = builder + .send () + .previous (latest1) + .destination (key1.pub) + .balance (amount - node1->config.receive_minimum.number ()) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (latest1)) + .build (); + + auto transaction = node1->store.tx_begin_write (); + ASSERT_EQ (nano::block_status::progress, node1->ledger.process (transaction, send1)); + node1->ledger.confirm (transaction, send1->hash ()); + ASSERT_TRUE (node1->ledger.block_confirmed (transaction, send1->hash ())); + ASSERT_EQ (1, node1->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); + ASSERT_EQ (2, node1->ledger.cache.cemented_count); +} + +TEST (ledger_confirm, election_winner_details_clearing_node_process_confirmed) +{ + // Make sure election_winner_details is also cleared if the block never enters the confirmation height processor from node::process_confirmed + nano::test::system system (1); + auto node = system.nodes.front (); + + nano::block_builder builder; + auto send = builder + .send () + .previous (nano::dev::genesis->hash ()) + .destination (nano::dev::genesis_key.pub) + .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (nano::dev::genesis->hash ())) + .build (); + // Add to election_winner_details. Use an unrealistic iteration so that it should fall into the else case and do a cleanup + node->active.add_election_winner_details (send->hash (), nullptr); + nano::election_status election; + election.winner = send; + node->process_confirmed (election, 1000000); + ASSERT_EQ (0, node->active.election_winner_details_size ()); +} + +TEST (ledger_confirm, pruned_source) +{ + nano::logger logger; + auto path (nano::unique_path ()); + auto store = nano::make_store (logger, path, nano::dev::constants); + ASSERT_TRUE (!store->init_error ()); + nano::stats stats; + nano::ledger ledger (*store, stats, nano::dev::constants); + ledger.pruning = true; + nano::write_database_queue write_database_queue (false); + nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + nano::keypair key1, key2; + nano::block_builder builder; + auto send1 = builder + .state () + .account (nano::dev::genesis_key.pub) + .previous (nano::dev::genesis->hash ()) + .representative (nano::dev::genesis_key.pub) + .balance (nano::dev::constants.genesis_amount - 100) + .link (key1.pub) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*pool.generate (nano::dev::genesis->hash ())) + .build (); + auto open1 = builder + .state () + .account (key1.pub) + .previous (0) + .representative (key1.pub) + .balance (100) + .link (send1->hash ()) + .sign (key1.prv, key1.pub) + .work (*pool.generate (key1.pub)) + .build (); + auto send2 = builder + .state () + .account (key1.pub) + .previous (open1->hash ()) + .representative (key1.pub) + .balance (50) + .link (key2.pub) + .sign (key1.prv, key1.pub) + .work (*pool.generate (open1->hash ())) + .build (); + auto send3 = builder + .state () + .account (key1.pub) + .previous (send2->hash ()) + .representative (key1.pub) + .balance (25) + .link (key2.pub) + .sign (key1.prv, key1.pub) + .work (*pool.generate (send2->hash ())) + .build (); + auto open2 = builder + .state () + .account (key2.pub) + .previous (0) + .representative (key1.pub) + .balance (50) + .link (send2->hash ()) + .sign (key2.prv, key2.pub) + .work (*pool.generate (key2.pub)) + .build (); + auto transaction (store->tx_begin_write ()); + store->initialize (transaction, ledger.cache, nano::dev::constants); + ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send1)); + ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open1)); + ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2)); + ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send3)); + ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open2)); + ASSERT_EQ (2, ledger.pruning_action (transaction, send2->hash (), 2)); + ASSERT_FALSE (ledger.block_exists (transaction, send2->hash ())); + ASSERT_FALSE (ledger.block_confirmed (transaction, open2->hash ())); + auto confirmed = ledger.confirm (transaction, open2->hash ()); + ASSERT_TRUE (ledger.block_confirmed (transaction, open2->hash ())); +} + +// Test that if a block is marked to be confirmed that doesn't exist in the ledger the program aborts +TEST (ledger_confirmDeathTest, rollback_added_block) +{ + // For ASSERT_DEATH_IF_SUPPORTED + testing::FLAGS_gtest_death_test_style = "threadsafe"; + + // valgrind can be noisy with death tests + if (!nano::running_within_valgrind ()) + { + nano::logger logger; + auto path (nano::unique_path ()); + auto store = nano::make_store (logger, path, nano::dev::constants); + ASSERT_TRUE (!store->init_error ()); + nano::stats stats; + nano::ledger ledger (*store, stats, nano::dev::constants); + nano::write_database_queue write_database_queue (false); + nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + nano::keypair key1; + nano::block_builder builder; + auto send = builder + .send () + .previous (nano::dev::genesis->hash ()) + .destination (key1.pub) + .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*pool.generate (nano::dev::genesis->hash ())) + .build (); + auto transaction (store->tx_begin_write ()); + store->initialize (transaction, ledger.cache, ledger.constants); + ASSERT_DEATH_IF_SUPPORTED (ledger.confirm (transaction, send->hash ()), ""); + } +} diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index ec675bb58..f245400ee 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -21,6 +21,8 @@ #include #include +#include + #include namespace @@ -875,6 +877,50 @@ std::optional nano::ledger::pending_info (store::transaction return store.pending.get (transaction, key); } +std::deque> nano::ledger::confirm (nano::store::write_transaction const & transaction, nano::block_hash const & hash) +{ + std::deque> result; + std::stack stack; + stack.push (hash); + while (!stack.empty ()) + { + auto hash = stack.top (); + auto block = this->block (transaction, hash); + release_assert (block); + auto dependents = dependent_blocks (transaction, *block); + for (auto const & dependent : dependents) + { + if (!dependent.is_zero () && !block_confirmed (transaction, dependent)) + { + stack.push (dependent); + } + } + if (stack.top () == hash) + { + stack.pop (); + if (!block_confirmed (transaction, hash)) + { + result.push_back (block); + confirm (transaction, *block); + } + } + else + { + // unconfirmed dependencies were added + } + } + return result; +} + +void nano::ledger::confirm (nano::store::write_transaction const & transaction, nano::block const & block) +{ + debug_assert ((!store.confirmation_height.get (transaction, block.account ()) && block.sideband ().height == 1) || store.confirmation_height.get (transaction, block.account ()).value ().height + 1 == block.sideband ().height); + confirmation_height_info info{ block.sideband ().height, block.hash () }; + store.confirmation_height.put (transaction, block.account (), info); + ++cache.cemented_count; + stats.inc (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed); +} + nano::block_status nano::ledger::process (store::write_transaction const & transaction_a, std::shared_ptr block_a) { debug_assert (!constants.work.validate_entry (*block_a) || constants.genesis == nano::dev::genesis); diff --git a/nano/secure/ledger.hpp b/nano/secure/ledger.hpp index ecd2a2627..1d25da133 100644 --- a/nano/secure/ledger.hpp +++ b/nano/secure/ledger.hpp @@ -7,6 +7,7 @@ #include #include +#include #include namespace nano::store @@ -76,6 +77,7 @@ public: std::string block_text (nano::block_hash const &); std::pair hash_root_random (store::transaction const &) const; std::optional pending_info (store::transaction const & transaction, nano::pending_key const & key) const; + std::deque> confirm (nano::store::write_transaction const & transaction, nano::block_hash const & hash); nano::block_status process (store::write_transaction const & transaction, std::shared_ptr block); bool rollback (store::write_transaction const &, nano::block_hash const &, std::vector> &); bool rollback (store::write_transaction const &, nano::block_hash const &); @@ -115,6 +117,7 @@ private: // Returns the next receivable entry equal or greater than 'key' std::optional> receivable_lower_bound (store::transaction const & tx, nano::account const & account, nano::block_hash const & hash) const; void initialize (nano::generate_cache_flags const &); + void confirm (nano::store::write_transaction const & transaction, nano::block const & block); }; std::unique_ptr collect_container_info (ledger & ledger, std::string const & name); From 328bf8c5abf969d11c419355315896543a48f79f Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Fri, 22 Mar 2024 18:16:01 +0000 Subject: [PATCH 102/128] Renaming confirmation_height_processor::is_processing_block to exists --- nano/core_test/confirmation_height.cpp | 2 +- nano/node/confirmation_height_processor.cpp | 2 +- nano/node/confirmation_height_processor.hpp | 2 +- nano/node/json_handler.cpp | 2 +- nano/node/node.cpp | 2 +- nano/node/wallet.cpp | 2 +- nano/rpc_test/rpc.cpp | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/nano/core_test/confirmation_height.cpp b/nano/core_test/confirmation_height.cpp index 5991615d1..2b2492d37 100644 --- a/nano/core_test/confirmation_height.cpp +++ b/nano/core_test/confirmation_height.cpp @@ -904,7 +904,7 @@ TEST (confirmation_height, unbounded_block_cache_iteration) confirmation_height_processor.add (send1); // The most uncemented block (previous block) should be seen as processing by the unbounded processor - while (!confirmation_height_processor.is_processing_block (send->hash ())) + while (!confirmation_height_processor.exists (send->hash ())) { ASSERT_LT (timer.since_start (), 10s); } diff --git a/nano/node/confirmation_height_processor.cpp b/nano/node/confirmation_height_processor.cpp index e60816444..396452cf4 100644 --- a/nano/node/confirmation_height_processor.cpp +++ b/nano/node/confirmation_height_processor.cpp @@ -230,7 +230,7 @@ bool nano::confirmation_height_processor::is_processing_added_block (nano::block return original_hashes_pending.count (hash_a) > 0 || awaiting_processing.get ().count (hash_a) > 0; } -bool nano::confirmation_height_processor::is_processing_block (nano::block_hash const & hash_a) const +bool nano::confirmation_height_processor::exists (nano::block_hash const & hash_a) const { return is_processing_added_block (hash_a) || unbounded_processor.has_iterated_over_block (hash_a); } diff --git a/nano/node/confirmation_height_processor.hpp b/nano/node/confirmation_height_processor.hpp index 828a61611..764c5cba1 100644 --- a/nano/node/confirmation_height_processor.hpp +++ b/nano/node/confirmation_height_processor.hpp @@ -40,7 +40,7 @@ public: void run (confirmation_height_mode); std::size_t awaiting_processing_size () const; bool is_processing_added_block (nano::block_hash const & hash_a) const; - bool is_processing_block (nano::block_hash const &) const; + bool exists (nano::block_hash const &) const; nano::block_hash current () const; /* diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 7465385a5..c54baa7ab 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -1206,7 +1206,7 @@ void nano::json_handler::block_confirm () if (!node.ledger.block_confirmed (transaction, hash)) { // Start new confirmation for unconfirmed (or not being confirmed) block - if (!node.confirmation_height_processor.is_processing_block (hash)) + if (!node.confirmation_height_processor.exists (hash)) { node.start_election (std::move (block_l)); } diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 4e6ec9949..811788aac 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -1187,7 +1187,7 @@ bool nano::node::block_confirmed (nano::block_hash const & hash_a) bool nano::node::block_confirmed_or_being_confirmed (nano::store::transaction const & transaction, nano::block_hash const & hash_a) { - return confirmation_height_processor.is_processing_block (hash_a) || ledger.block_confirmed (transaction, hash_a); + return confirmation_height_processor.exists (hash_a) || ledger.block_confirmed (transaction, hash_a); } bool nano::node::block_confirmed_or_being_confirmed (nano::block_hash const & hash_a) diff --git a/nano/node/wallet.cpp b/nano/node/wallet.cpp index 638c4c623..28a3586df 100644 --- a/nano/node/wallet.cpp +++ b/nano/node/wallet.cpp @@ -1198,7 +1198,7 @@ bool nano::wallet::search_receivable (store::transaction const & wallet_transact // Receive confirmed block receive_async (hash, representative, amount, account, [] (std::shared_ptr const &) {}); } - else if (!wallets.node.confirmation_height_processor.is_processing_block (hash)) + else if (!wallets.node.confirmation_height_processor.exists (hash)) { auto block = wallets.node.ledger.block (block_transaction, hash); if (block) diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 9b8b3b086..44d72c7eb 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -5820,7 +5820,7 @@ TEST (rpc, block_confirmed) ASSERT_TRUE (nano::test::start_elections (system, *node, { send }, true)); // Wait until the confirmation height has been set - ASSERT_TIMELY (5s, node->ledger.block_confirmed (node->store.tx_begin_read (), send->hash ()) && !node->confirmation_height_processor.is_processing_block (send->hash ())); + ASSERT_TIMELY (5s, node->ledger.block_confirmed (node->store.tx_begin_read (), send->hash ()) && !node->confirmation_height_processor.exists (send->hash ())); // Requesting confirmation for this should now succeed request.put ("hash", send->hash ().to_string ()); From 2fa92c40f7122bc1ef83e7577476095ca7609c73 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Sat, 23 Mar 2024 13:28:24 +0000 Subject: [PATCH 103/128] Change cemented observers to be nano::observer_sets --- nano/core_test/request_aggregator.cpp | 2 -- nano/node/active_transactions.cpp | 4 ++-- nano/node/confirmation_height_processor.cpp | 26 ++------------------- nano/node/confirmation_height_processor.hpp | 16 +++---------- nano/node/node.cpp | 2 +- 5 files changed, 8 insertions(+), 42 deletions(-) diff --git a/nano/core_test/request_aggregator.cpp b/nano/core_test/request_aggregator.cpp index d6662f75a..da55fd013 100644 --- a/nano/core_test/request_aggregator.cpp +++ b/nano/core_test/request_aggregator.cpp @@ -451,8 +451,6 @@ TEST (request_aggregator, cannot_vote) nano::node_flags flags; flags.disable_request_loop = true; auto & node (*system.add_node (flags)); - // This prevents activation of blocks which are cemented - node.confirmation_height_processor.cemented_observers.clear (); nano::state_block_builder builder; auto send1 = builder.make_block () .account (nano::dev::genesis_key.pub) diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index c7411a4c6..c8bdeb5fe 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -26,12 +26,12 @@ nano::active_transactions::active_transactions (nano::node & node_a, nano::confi count_by_behavior.fill (0); // Zero initialize array // Register a callback which will get called after a block is cemented - confirmation_height_processor.add_cemented_observer ([this] (std::shared_ptr const & callback_block_a) { + confirmation_height_processor.cemented_observers.add ([this] (std::shared_ptr const & callback_block_a) { this->block_cemented_callback (callback_block_a); }); // Register a callback which will get called if a block is already cemented - confirmation_height_processor.add_block_already_cemented_observer ([this] (nano::block_hash const & hash_a) { + confirmation_height_processor.block_already_cemented_observers.add ([this] (nano::block_hash const & hash_a) { this->block_already_cemented_callback (hash_a); }); diff --git a/nano/node/confirmation_height_processor.cpp b/nano/node/confirmation_height_processor.cpp index 396452cf4..5161980ee 100644 --- a/nano/node/confirmation_height_processor.cpp +++ b/nano/node/confirmation_height_processor.cpp @@ -173,45 +173,23 @@ void nano::confirmation_height_processor::set_next_hash () awaiting_processing.get ().pop_front (); } -// Not thread-safe, only call before this processor has begun cementing -void nano::confirmation_height_processor::add_cemented_observer (std::function const &)> const & callback_a) -{ - cemented_observers.push_back (callback_a); -} - -// Not thread-safe, only call before this processor has begun cementing -void nano::confirmation_height_processor::add_block_already_cemented_observer (std::function const & callback_a) -{ - block_already_cemented_observers.push_back (callback_a); -} - void nano::confirmation_height_processor::notify_cemented (std::vector> const & cemented_blocks) { for (auto const & block_callback_data : cemented_blocks) { - for (auto const & observer : cemented_observers) - { - observer (block_callback_data); - } + cemented_observers.notify (block_callback_data); } } void nano::confirmation_height_processor::notify_already_cemented (nano::block_hash const & hash_already_cemented_a) { - for (auto const & observer : block_already_cemented_observers) - { - observer (hash_already_cemented_a); - } + block_already_cemented_observers.notify (hash_already_cemented_a); } std::unique_ptr nano::collect_container_info (confirmation_height_processor & confirmation_height_processor_a, std::string const & name_a) { auto composite = std::make_unique (name_a); - std::size_t cemented_observers_count = confirmation_height_processor_a.cemented_observers.size (); - std::size_t block_already_cemented_observers_count = confirmation_height_processor_a.block_already_cemented_observers.size (); - composite->add_component (std::make_unique (container_info{ "cemented_observers", cemented_observers_count, sizeof (decltype (confirmation_height_processor_a.cemented_observers)::value_type) })); - composite->add_component (std::make_unique (container_info{ "block_already_cemented_observers", block_already_cemented_observers_count, sizeof (decltype (confirmation_height_processor_a.block_already_cemented_observers)::value_type) })); composite->add_component (std::make_unique (container_info{ "awaiting_processing", confirmation_height_processor_a.awaiting_processing_size (), sizeof (decltype (confirmation_height_processor_a.awaiting_processing)::value_type) })); composite->add_component (collect_container_info (confirmation_height_processor_a.bounded_processor, "bounded_processor")); composite->add_component (collect_container_info (confirmation_height_processor_a.unbounded_processor, "unbounded_processor")); diff --git a/nano/node/confirmation_height_processor.hpp b/nano/node/confirmation_height_processor.hpp index 764c5cba1..2ab69fd62 100644 --- a/nano/node/confirmation_height_processor.hpp +++ b/nano/node/confirmation_height_processor.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -43,16 +44,8 @@ public: bool exists (nano::block_hash const &) const; nano::block_hash current () const; - /* - * Called for each newly cemented block - * Called from confirmation height processor thread - */ - void add_cemented_observer (std::function const &)> const &); - /* - * Called when the block was added to the confirmation height processor but is already confirmed - * Called from confirmation height processor thread - */ - void add_block_already_cemented_observer (std::function const &); + nano::observer_set const &> cemented_observers; + nano::observer_set block_already_cemented_observers; private: mutable nano::mutex mutex{ mutex_identifier (mutexes::confirmation_height_processor) }; @@ -88,9 +81,6 @@ private: nano::condition_variable condition; std::atomic stopped{ false }; - // No mutex needed for the observers as these should be set up during initialization of the node - std::vector const &)>> cemented_observers; - std::vector> block_already_cemented_observers; nano::ledger & ledger; nano::write_database_queue & write_database_queue; diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 811788aac..3a6634ada 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -466,7 +466,7 @@ nano::node::node (std::shared_ptr io_ctx_a, std::filesy std::exit (1); } } - confirmation_height_processor.add_cemented_observer ([this] (auto const & block) { + confirmation_height_processor.cemented_observers.add ([this] (auto const & block) { if (block->is_send ()) { auto transaction = store.tx_begin_read (); From ae31275d0d1d336093b55e0c7f5b5140eb0aaf34 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Fri, 22 Mar 2024 18:06:27 +0000 Subject: [PATCH 104/128] Add confirming_set class which will replace confirmation_height_processor --- nano/core_test/CMakeLists.txt | 1 + nano/core_test/confirming_set.cpp | 68 +++++++++++++++++ nano/node/CMakeLists.txt | 2 + nano/node/confirming_set.cpp | 120 ++++++++++++++++++++++++++++++ nano/node/confirming_set.hpp | 57 ++++++++++++++ 5 files changed, 248 insertions(+) create mode 100644 nano/core_test/confirming_set.cpp create mode 100644 nano/node/confirming_set.cpp create mode 100644 nano/node/confirming_set.hpp diff --git a/nano/core_test/CMakeLists.txt b/nano/core_test/CMakeLists.txt index d58a2b5ae..74410c67b 100644 --- a/nano/core_test/CMakeLists.txt +++ b/nano/core_test/CMakeLists.txt @@ -14,6 +14,7 @@ add_executable( cli.cpp confirmation_height.cpp confirmation_solicitor.cpp + confirming_set.cpp conflicts.cpp difficulty.cpp distributed_work.cpp diff --git a/nano/core_test/confirming_set.cpp b/nano/core_test/confirming_set.cpp new file mode 100644 index 000000000..44df1ea18 --- /dev/null +++ b/nano/core_test/confirming_set.cpp @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +using namespace std::chrono_literals; + +TEST (confirming_set, construction) +{ + auto ctx = nano::test::context::ledger_empty (); + nano::write_database_queue write_queue{ false }; + nano::confirming_set confirming_set (ctx.ledger (), write_queue); +} + +TEST (confirming_set, add_exists) +{ + auto ctx = nano::test::context::ledger_send_receive (); + nano::write_database_queue write_queue{ false }; + nano::confirming_set confirming_set (ctx.ledger (), write_queue); + auto send = ctx.blocks ()[0]; + confirming_set.add (send->hash ()); + ASSERT_TRUE (confirming_set.exists (send->hash ())); +} + +TEST (confirming_set, process_one) +{ + auto ctx = nano::test::context::ledger_send_receive (); + nano::write_database_queue write_queue{ false }; + nano::confirming_set confirming_set (ctx.ledger (), write_queue); + std::atomic count = 0; + std::mutex mutex; + std::condition_variable condition; + confirming_set.cemented_observers.add ([&] (auto const &) { ++count; condition.notify_all (); }); + confirming_set.add (ctx.blocks ()[0]->hash ()); + nano::test::start_stop_guard guard{ confirming_set }; + std::unique_lock lock{ mutex }; + ASSERT_TRUE (condition.wait_for (lock, 5s, [&] () { return count == 1; })); + ASSERT_EQ (1, ctx.stats ().count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); + ASSERT_EQ (2, ctx.ledger ().cache.cemented_count); +} + +TEST (confirming_set, process_multiple) +{ + auto ctx = nano::test::context::ledger_send_receive (); + nano::write_database_queue write_queue{ false }; + nano::confirming_set confirming_set (ctx.ledger (), write_queue); + std::atomic count = 0; + std::mutex mutex; + std::condition_variable condition; + confirming_set.cemented_observers.add ([&] (auto const &) { ++count; condition.notify_all (); }); + confirming_set.add (ctx.blocks ()[0]->hash ()); + confirming_set.add (ctx.blocks ()[1]->hash ()); + nano::test::start_stop_guard guard{ confirming_set }; + std::unique_lock lock{ mutex }; + ASSERT_TRUE (condition.wait_for (lock, 5s, [&] () { return count == 2; })); + ASSERT_EQ (2, ctx.stats ().count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); + ASSERT_EQ (3, ctx.ledger ().cache.cemented_count); +} diff --git a/nano/node/CMakeLists.txt b/nano/node/CMakeLists.txt index 0b8ad15e7..b21cdae1a 100644 --- a/nano/node/CMakeLists.txt +++ b/nano/node/CMakeLists.txt @@ -59,6 +59,8 @@ add_library( cli.cpp common.hpp common.cpp + confirming_set.hpp + confirming_set.cpp confirmation_height_bounded.hpp confirmation_height_bounded.cpp confirmation_height_processor.hpp diff --git a/nano/node/confirming_set.cpp b/nano/node/confirming_set.cpp new file mode 100644 index 000000000..5db631aed --- /dev/null +++ b/nano/node/confirming_set.cpp @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include + +nano::confirming_set::confirming_set (nano::ledger & ledger, nano::write_database_queue & write_queue, std::chrono::milliseconds batch_time) : + ledger{ ledger }, + write_queue{ write_queue }, + batch_time{ batch_time } +{ +} + +nano::confirming_set::~confirming_set () +{ + debug_assert (!thread.joinable ()); +} + +void nano::confirming_set::add (nano::block_hash const & hash) +{ + std::lock_guard lock{ mutex }; + set.insert (hash); + condition.notify_all (); +} + +void nano::confirming_set::start () +{ + thread = std::thread{ [this] () { run (); } }; +} + +void nano::confirming_set::stop () +{ + { + std::lock_guard lock{ mutex }; + stopped = true; + condition.notify_all (); + } + if (thread.joinable ()) + { + thread.join (); + } +} + +bool nano::confirming_set::exists (nano::block_hash const & hash) const +{ + std::lock_guard lock{ mutex }; + return set.count (hash) != 0 || processing.count (hash) != 0; +} + +std::size_t nano::confirming_set::size () const +{ + std::lock_guard lock{ mutex }; + return set.size () + processing.size (); +} + +void nano::confirming_set::run () +{ + nano::thread_role::set (nano::thread_role::name::confirmation_height_processing); + std::unique_lock lock{ mutex }; + // Run the confirmation loop until stopped + while (!stopped) + { + condition.wait (lock, [&] () { return !set.empty () || stopped; }); + // Loop if there are items to process + if (!stopped && !set.empty ()) + { + std::deque> cemented; + std::deque already; + // Move items in to back buffer and release lock so more items can be added to the front buffer + processing = std::move (this->set); + // Process all items in the back buffer + for (auto i = processing.begin (), n = processing.end (); !stopped && i != n;) + { + lock.unlock (); // Waiting for db write is potentially slow + auto guard = write_queue.wait (nano::writer::confirmation_height); + auto tx = ledger.store.tx_begin_write ({ nano::tables::confirmation_height }); + lock.lock (); + // Process items in the back buffer within a single transaction for a limited amount of time + for (auto timeout = std::chrono::steady_clock::now () + batch_time; !stopped && std::chrono::steady_clock::now () < timeout && i != n; ++i) + { + auto item = *i; + lock.unlock (); + auto added = ledger.confirm (tx, item); + if (!added.empty ()) + { + // Confirming this block may implicitly confirm more + cemented.insert (cemented.end (), added.begin (), added.end ()); + } + else + { + already.push_back (item); + } + lock.lock (); + } + } + lock.unlock (); + for (auto const & i : cemented) + { + cemented_observers.notify (i); + } + for (auto const & i : already) + { + block_already_cemented_observers.notify (i); + } + lock.lock (); + // Clear and free back buffer by re-initializing + processing = decltype (processing){}; + } + } +} + +std::unique_ptr nano::confirming_set::collect_container_info (std::string const & name) const +{ + std::lock_guard guard{ mutex }; + + auto composite = std::make_unique (name); + composite->add_component (std::make_unique (container_info{ "set", set.size (), sizeof (typename decltype (set)::value_type) })); + composite->add_component (std::make_unique (container_info{ "processing", processing.size (), sizeof (typename decltype (processing)::value_type) })); + return composite; +} diff --git a/nano/node/confirming_set.hpp b/nano/node/confirming_set.hpp new file mode 100644 index 000000000..5c5de14bd --- /dev/null +++ b/nano/node/confirming_set.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +namespace nano +{ +class block; +class ledger; +class write_database_queue; +} + +namespace nano +{ +/** + * Set of blocks to be durably confirmed + */ +class confirming_set final +{ + friend class confirmation_heightDeathTest_missing_block_Test; + friend class confirmation_height_pruned_source_Test; + +public: + confirming_set (nano::ledger & ledger, nano::write_database_queue & write_queue, std::chrono::milliseconds batch_time = std::chrono::milliseconds{ 500 }); + ~confirming_set (); + // Adds a block to the set of blocks to be confirmed + void add (nano::block_hash const & hash); + void start (); + void stop (); + // Added blocks will remain in this set until after ledger has them marked as confirmed. + bool exists (nano::block_hash const & hash) const; + std::size_t size () const; + std::unique_ptr collect_container_info (std::string const & name) const; + + // Observers will be called once ledger has blocks marked as confirmed + nano::observer_set> cemented_observers; + nano::observer_set block_already_cemented_observers; + +private: + void run (); + nano::ledger & ledger; + nano::write_database_queue & write_queue; + std::chrono::milliseconds batch_time; + std::unordered_set set; + std::unordered_set processing; + bool stopped{ false }; + mutable std::mutex mutex; + std::condition_variable condition; + std::thread thread; +}; +} From c14163649b6adec31017d94d2827bb6f6f84ad40 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Sat, 23 Mar 2024 19:17:15 +0000 Subject: [PATCH 105/128] Removing confirmation_height tests that test implementation details --- nano/core_test/confirmation_height.cpp | 647 ------------------------- 1 file changed, 647 deletions(-) diff --git a/nano/core_test/confirmation_height.cpp b/nano/core_test/confirmation_height.cpp index 2b2492d37..34c4ac9e5 100644 --- a/nano/core_test/confirmation_height.cpp +++ b/nano/core_test/confirmation_height.cpp @@ -33,266 +33,6 @@ nano::stat::detail get_stats_detail (nano::confirmation_height_mode mode_a) } } -TEST (confirmation_height, gap_bootstrap) -{ - auto test_mode = [] (nano::confirmation_height_mode mode_a) { - nano::test::system system{}; - nano::node_flags node_flags{}; - node_flags.confirmation_height_processor_mode = mode_a; - auto & node1 = *system.add_node (node_flags); - nano::keypair destination{}; - nano::block_builder builder; - auto send1 = builder - .state () - .account (nano::dev::genesis_key.pub) - .previous (nano::dev::genesis->hash ()) - .representative (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) - .link (destination.pub) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (0) - .build (); - node1.work_generate_blocking (*send1); - auto send2 = builder - .state () - .account (nano::dev::genesis_key.pub) - .previous (send1->hash ()) - .representative (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - 2 * nano::Gxrb_ratio) - .link (destination.pub) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (0) - .build (); - node1.work_generate_blocking (*send2); - auto send3 = builder - .state () - .account (nano::dev::genesis_key.pub) - .previous (send2->hash ()) - .representative (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - 3 * nano::Gxrb_ratio) - .link (destination.pub) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (0) - .build (); - node1.work_generate_blocking (*send3); - auto open1 = builder - .open () - .source (send1->hash ()) - .representative (destination.pub) - .account (destination.pub) - .sign (destination.prv, destination.pub) - .work (0) - .build (); - node1.work_generate_blocking (*open1); - - // Receive - auto receive1 = builder - .receive () - .previous (open1->hash ()) - .source (send2->hash ()) - .sign (destination.prv, destination.pub) - .work (0) - .build (); - node1.work_generate_blocking (*receive1); - auto receive2 = builder - .receive () - .previous (receive1->hash ()) - .source (send3->hash ()) - .sign (destination.prv, destination.pub) - .work (0) - .build (); - node1.work_generate_blocking (*receive2); - - node1.block_processor.add (send1); - node1.block_processor.add (send2); - node1.block_processor.add (send3); - node1.block_processor.add (receive1); - ASSERT_TIMELY (5s, node1.block (send3->hash ()) != nullptr); - - add_callback_stats (node1); - - // Receive 2 comes in on the live network, however the chain has not been finished so it gets added to unchecked - node1.process_active (receive2); - // Waits for the unchecked_map to process the 4 blocks added to the block_processor, saving them in the unchecked table - auto check_block_is_listed = [&] (nano::store::transaction const & transaction_a, nano::block_hash const & block_hash_a) { - return !node1.unchecked.get (block_hash_a).empty (); - }; - ASSERT_TIMELY (5s, check_block_is_listed (node1.store.tx_begin_read (), receive2->previous ())); - - // Confirmation heights should not be updated - { - auto transaction (node1.store.tx_begin_read ()); - auto unchecked_count (node1.unchecked.count ()); - ASSERT_EQ (unchecked_count, 2); - - nano::confirmation_height_info confirmation_height_info; - ASSERT_FALSE (node1.store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, confirmation_height_info)); - ASSERT_EQ (1, confirmation_height_info.height); - ASSERT_EQ (nano::dev::genesis->hash (), confirmation_height_info.frontier); - } - - // Now complete the chain where the block comes in on the bootstrap network. - node1.block_processor.add (open1); - - ASSERT_TIMELY_EQ (5s, node1.unchecked.count (), 0); - // Confirmation height should be unchanged and unchecked should now be 0 - { - auto transaction = node1.store.tx_begin_read (); - nano::confirmation_height_info confirmation_height_info; - ASSERT_FALSE (node1.store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, confirmation_height_info)); - ASSERT_EQ (1, confirmation_height_info.height); - ASSERT_EQ (nano::dev::genesis->hash (), confirmation_height_info.frontier); - ASSERT_TRUE (node1.store.confirmation_height.get (transaction, destination.pub, confirmation_height_info)); - ASSERT_EQ (0, confirmation_height_info.height); - ASSERT_EQ (nano::block_hash (0), confirmation_height_info.frontier); - } - ASSERT_EQ (0, node1.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (0, node1.stats.count (nano::stat::type::confirmation_height, get_stats_detail (mode_a), nano::stat::dir::in)); - ASSERT_EQ (0, node1.stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out)); - ASSERT_EQ (1, node1.ledger.cache.cemented_count); - - ASSERT_EQ (0, node1.active.election_winner_details_size ()); - }; - - test_mode (nano::confirmation_height_mode::bounded); - test_mode (nano::confirmation_height_mode::unbounded); -} - -TEST (confirmation_height, gap_live) -{ - auto test_mode = [] (nano::confirmation_height_mode mode_a) { - nano::test::system system{}; - nano::node_flags node_flags{}; - node_flags.confirmation_height_processor_mode = mode_a; - nano::node_config node_config = system.default_config (); - node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - auto node = system.add_node (node_config, node_flags); - node_config.peering_port = system.get_available_port (); - node_config.receive_minimum = nano::dev::constants.genesis_amount; // Prevent auto-receive & open1/receive1/receive2 blocks conflicts - system.add_node (node_config, node_flags); - nano::keypair destination; - system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv); - system.wallet (1)->insert_adhoc (destination.prv); - - nano::block_builder builder; - auto send1 = builder - .state () - .account (nano::dev::genesis_key.pub) - .previous (nano::dev::genesis->hash ()) - .representative (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - 1) - .link (destination.pub) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (0) - .build (); - node->work_generate_blocking (*send1); - auto send2 = builder - .state () - .account (nano::dev::genesis_key.pub) - .previous (send1->hash ()) - .representative (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - 2) - .link (destination.pub) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (0) - .build (); - node->work_generate_blocking (*send2); - auto send3 = builder - .state () - .account (nano::dev::genesis_key.pub) - .previous (send2->hash ()) - .representative (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - 3) - .link (destination.pub) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (0) - .build (); - node->work_generate_blocking (*send3); - - auto open1 = builder - .open () - .source (send1->hash ()) - .representative (destination.pub) - .account (destination.pub) - .sign (destination.prv, destination.pub) - .work (0) - .build (); - node->work_generate_blocking (*open1); - auto receive1 = builder - .receive () - .previous (open1->hash ()) - .source (send2->hash ()) - .sign (destination.prv, destination.pub) - .work (0) - .build (); - node->work_generate_blocking (*receive1); - auto receive2 = builder - .receive () - .previous (receive1->hash ()) - .source (send3->hash ()) - .sign (destination.prv, destination.pub) - .work (0) - .build (); - node->work_generate_blocking (*receive2); - - node->block_processor.add (send1); - node->block_processor.add (send2); - node->block_processor.add (send3); - // node->block_processor.add (open1); Witheld for test - node->block_processor.add (receive1); - ASSERT_TIMELY (5s, nano::test::exists (*node, { send1, send2, send3 })); - ASSERT_TIMELY (5s, node->unchecked.exists ({ open1->hash (), receive1->hash () })); - - add_callback_stats (*node); - - // Receive 2 comes in on the live network, however the chain has not been finished so it gets added to unchecked - node->process_active (receive2); - ASSERT_TIMELY (5s, node->unchecked.exists ({ receive1->hash (), receive2->hash () })); - - // Confirmation heights should not be updated - { - auto transaction = node->store.tx_begin_read (); - nano::confirmation_height_info confirmation_height_info; - ASSERT_FALSE (node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, confirmation_height_info)); - ASSERT_EQ (1, confirmation_height_info.height); - ASSERT_EQ (nano::dev::genesis->hash (), confirmation_height_info.frontier); - } - - // Vote and confirm all existing blocks - nano::test::start_election (system, *node, send1->hash ()); - ASSERT_TIMELY_EQ (10s, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out), 3); - - // Now complete the chain where the block comes in on the live network - node->process_active (open1); - - ASSERT_TIMELY_EQ (10s, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out), 6); - - // This should confirm the open block and the source of the receive blocks - auto transaction = node->store.tx_begin_read (); - auto unchecked_count = node->unchecked.count (); - ASSERT_EQ (unchecked_count, 0); - - nano::confirmation_height_info confirmation_height_info{}; - ASSERT_TRUE (node->ledger.block_confirmed (transaction, receive2->hash ())); - ASSERT_FALSE (node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, confirmation_height_info)); - ASSERT_EQ (4, confirmation_height_info.height); - ASSERT_EQ (send3->hash (), confirmation_height_info.frontier); - ASSERT_FALSE (node->store.confirmation_height.get (transaction, destination.pub, confirmation_height_info)); - ASSERT_EQ (3, confirmation_height_info.height); - ASSERT_EQ (receive2->hash (), confirmation_height_info.frontier); - - ASSERT_EQ (6, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (6, node->stats.count (nano::stat::type::confirmation_height, get_stats_detail (mode_a), nano::stat::dir::in)); - ASSERT_EQ (6, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out)); - ASSERT_EQ (7, node->ledger.cache.cemented_count); - - ASSERT_EQ (0, node->active.election_winner_details_size ()); - }; - - test_mode (nano::confirmation_height_mode::bounded); - test_mode (nano::confirmation_height_mode::unbounded); -} - TEST (confirmation_height, pending_observer_callbacks) { auto test_mode = [] (nano::confirmation_height_mode mode_a) { @@ -510,326 +250,6 @@ TEST (confirmation_height, dependent_election) test_mode (nano::confirmation_height_mode::unbounded); } -// This test checks that a receive block with uncemented blocks below cements them too. -TEST (confirmation_height, cemented_gap_below_receive) -{ - auto test_mode = [] (nano::confirmation_height_mode mode_a) { - nano::test::system system; - nano::node_flags node_flags; - node_flags.confirmation_height_processor_mode = mode_a; - nano::node_config node_config = system.default_config (); - node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - auto node = system.add_node (node_config, node_flags); - - nano::block_hash latest (node->latest (nano::dev::genesis_key.pub)); - - nano::keypair key1; - nano::block_builder builder; - system.wallet (0)->insert_adhoc (key1.prv); - - auto send = builder - .send () - .previous (latest) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (latest)) - .build (); - auto send1 = builder - .send () - .previous (send->hash ()) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send->hash ())) - .build (); - nano::keypair dummy_key; - auto dummy_send = builder - .send () - .previous (send1->hash ()) - .destination (dummy_key.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 3) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send1->hash ())) - .build (); - - auto open = builder - .open () - .source (send->hash ()) - .representative (nano::dev::genesis_key.pub) - .account (key1.pub) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (key1.pub)) - .build (); - auto receive1 = builder - .receive () - .previous (open->hash ()) - .source (send1->hash ()) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (open->hash ())) - .build (); - auto send2 = builder - .send () - .previous (receive1->hash ()) - .destination (nano::dev::genesis_key.pub) - .balance (nano::Gxrb_ratio) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (receive1->hash ())) - .build (); - - auto receive2 = builder - .receive () - .previous (dummy_send->hash ()) - .source (send2->hash ()) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (dummy_send->hash ())) - .build (); - auto dummy_send1 = builder - .send () - .previous (receive2->hash ()) - .destination (dummy_key.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 3) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (receive2->hash ())) - .build (); - - nano::keypair key2; - system.wallet (0)->insert_adhoc (key2.prv); - auto send3 = builder - .send () - .previous (dummy_send1->hash ()) - .destination (key2.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 4) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (dummy_send1->hash ())) - .build (); - auto dummy_send2 = builder - .send () - .previous (send3->hash ()) - .destination (dummy_key.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 5) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send3->hash ())) - .build (); - - auto open1 = builder - .open () - .source (send3->hash ()) - .representative (nano::dev::genesis_key.pub) - .account (key2.pub) - .sign (key2.prv, key2.pub) - .work (*system.work.generate (key2.pub)) - .build (); - - { - auto transaction = node->store.tx_begin_write (); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, dummy_send)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, open)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive1)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send2)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive2)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, dummy_send1)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send3)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, dummy_send2)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, open1)); - } - - std::vector observer_order; - nano::mutex mutex; - add_callback_stats (*node, &observer_order, &mutex); - - auto election = nano::test::start_election (system, *node, open1->hash ()); - ASSERT_NE (nullptr, election); - election->force_confirm (); - ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out), 10); - - auto transaction = node->store.tx_begin_read (); - ASSERT_TRUE (node->ledger.block_confirmed (transaction, open1->hash ())); - ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_quorum, nano::stat::dir::out)); - ASSERT_EQ (0, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_conf_height, nano::stat::dir::out)); - ASSERT_EQ (9, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::inactive_conf_height, nano::stat::dir::out)); - ASSERT_EQ (10, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (10, node->stats.count (nano::stat::type::confirmation_height, get_stats_detail (mode_a), nano::stat::dir::in)); - ASSERT_EQ (11, node->ledger.cache.cemented_count); - ASSERT_EQ (0, node->active.election_winner_details_size ()); - - // Check that the order of callbacks is correct - std::vector expected_order = { send->hash (), open->hash (), send1->hash (), receive1->hash (), send2->hash (), dummy_send->hash (), receive2->hash (), dummy_send1->hash (), send3->hash (), open1->hash () }; - nano::lock_guard guard (mutex); - ASSERT_EQ (observer_order, expected_order); - }; - - test_mode (nano::confirmation_height_mode::bounded); - test_mode (nano::confirmation_height_mode::unbounded); -} - -// This test checks that a receive block with uncemented blocks below cements them too, compared with the test above, this -// is the first write in this chain. -TEST (confirmation_height, cemented_gap_below_no_cache) -{ - auto test_mode = [] (nano::confirmation_height_mode mode_a) { - nano::test::system system; - nano::node_flags node_flags; - node_flags.confirmation_height_processor_mode = mode_a; - nano::node_config node_config = system.default_config (); - node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - auto node = system.add_node (node_config, node_flags); - - nano::block_hash latest (node->latest (nano::dev::genesis_key.pub)); - - nano::keypair key1; - system.wallet (0)->insert_adhoc (key1.prv); - - nano::block_builder builder; - auto send = builder - .send () - .previous (latest) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (latest)) - .build (); - auto send1 = builder - .send () - .previous (send->hash ()) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send->hash ())) - .build (); - nano::keypair dummy_key; - auto dummy_send = builder - .send () - .previous (send1->hash ()) - .destination (dummy_key.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 3) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send1->hash ())) - .build (); - - auto open = builder - .open () - .source (send->hash ()) - .representative (nano::dev::genesis_key.pub) - .account (key1.pub) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (key1.pub)) - .build (); - auto receive1 = builder - .receive () - .previous (open->hash ()) - .source (send1->hash ()) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (open->hash ())) - .build (); - auto send2 = builder - .send () - .previous (receive1->hash ()) - .destination (nano::dev::genesis_key.pub) - .balance (nano::Gxrb_ratio) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (receive1->hash ())) - .build (); - - auto receive2 = builder - .receive () - .previous (dummy_send->hash ()) - .source (send2->hash ()) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (dummy_send->hash ())) - .build (); - auto dummy_send1 = builder - .send () - .previous (receive2->hash ()) - .destination (dummy_key.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 3) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (receive2->hash ())) - .build (); - - nano::keypair key2; - system.wallet (0)->insert_adhoc (key2.prv); - auto send3 = builder - .send () - .previous (dummy_send1->hash ()) - .destination (key2.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 4) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (dummy_send1->hash ())) - .build (); - auto dummy_send2 = builder - .send () - .previous (send3->hash ()) - .destination (dummy_key.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 5) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send3->hash ())) - .build (); - - auto open1 = builder - .open () - .source (send3->hash ()) - .representative (nano::dev::genesis_key.pub) - .account (key2.pub) - .sign (key2.prv, key2.pub) - .work (*system.work.generate (key2.pub)) - .build (); - - { - auto transaction = node->store.tx_begin_write (); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, dummy_send)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, open)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive1)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send2)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, receive2)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, dummy_send1)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send3)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, dummy_send2)); - - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, open1)); - } - - // Force some blocks to be cemented so that the cached confirmed info variable is empty - { - auto transaction (node->store.tx_begin_write ()); - node->store.confirmation_height.put (transaction, nano::dev::genesis_key.pub, nano::confirmation_height_info{ 3, send1->hash () }); - node->store.confirmation_height.put (transaction, key1.pub, nano::confirmation_height_info{ 2, receive1->hash () }); - } - - add_callback_stats (*node); - - auto election = nano::test::start_election (system, *node, open1->hash ()); - ASSERT_NE (nullptr, election); - election->force_confirm (); - ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out), 6); - - auto transaction = node->store.tx_begin_read (); - ASSERT_TRUE (node->ledger.block_confirmed (transaction, open1->hash ())); - ASSERT_EQ (node->active.election_winner_details_size (), 0); - ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_quorum, nano::stat::dir::out)); - ASSERT_EQ (0, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_conf_height, nano::stat::dir::out)); - ASSERT_EQ (5, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::inactive_conf_height, nano::stat::dir::out)); - ASSERT_EQ (6, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (6, node->stats.count (nano::stat::type::confirmation_height, get_stats_detail (mode_a), nano::stat::dir::in)); - ASSERT_EQ (7, node->ledger.cache.cemented_count); - }; - - test_mode (nano::confirmation_height_mode::bounded); - test_mode (nano::confirmation_height_mode::unbounded); -} -} - TEST (confirmation_height, election_winner_details_clearing_node_process_confirmed) { // Make sure election_winner_details is also cleared if the block never enters the confirmation height processor from node::process_confirmed @@ -852,71 +272,4 @@ TEST (confirmation_height, election_winner_details_clearing_node_process_confirm node->process_confirmed (election, 1000000); ASSERT_EQ (0, node->active.election_winner_details_size ()); } - -TEST (confirmation_height, unbounded_block_cache_iteration) -{ - if (nano::rocksdb_config::using_rocksdb_in_tests ()) - { - // Don't test this in rocksdb mode - GTEST_SKIP (); - } - nano::logger logger; - auto path (nano::unique_path ()); - auto store = nano::make_store (logger, path, nano::dev::constants); - ASSERT_TRUE (!store->init_error ()); - nano::stats stats; - nano::ledger ledger (*store, stats, nano::dev::constants); - nano::write_database_queue write_database_queue (false); - boost::latch initialized_latch{ 0 }; - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; - nano::keypair key1; - nano::block_builder builder; - auto send = builder - .send () - .previous (nano::dev::genesis->hash ()) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*pool.generate (nano::dev::genesis->hash ())) - .build (); - auto send1 = builder - .send () - .previous (send->hash ()) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*pool.generate (send->hash ())) - .build (); - { - auto transaction (store->tx_begin_write ()); - store->initialize (transaction, ledger.cache, nano::dev::constants); - ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send)); - ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send1)); - } - - nano::confirmation_height_processor confirmation_height_processor (ledger, write_database_queue, 10ms, logger, initialized_latch, nano::confirmation_height_mode::unbounded); - nano::timer<> timer; - timer.start (); - { - // Prevent conf height processor doing any writes, so that we can query is_processing_block correctly - auto write_guard = write_database_queue.wait (nano::writer::testing); - // Add the frontier block - confirmation_height_processor.add (send1); - - // The most uncemented block (previous block) should be seen as processing by the unbounded processor - while (!confirmation_height_processor.exists (send->hash ())) - { - ASSERT_LT (timer.since_start (), 10s); - } - } - - // Wait until the current block is finished processing - while (!confirmation_height_processor.current ().is_zero ()) - { - ASSERT_LT (timer.since_start (), 10s); - } - - ASSERT_EQ (2, stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (2, stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed_unbounded, nano::stat::dir::in)); - ASSERT_EQ (3, ledger.cache.cemented_count); } From ddb3cf55623b0be9c99b94052de353497dc1c22b Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Sat, 23 Mar 2024 13:52:55 +0000 Subject: [PATCH 106/128] Replacing confirmation_height_processor with confirming_set --- nano/core_test/CMakeLists.txt | 1 - nano/core_test/active_transactions.cpp | 4 +- nano/core_test/confirmation_height.cpp | 275 ------------------------- nano/core_test/confirming_set.cpp | 212 +++++++++++++++++++ nano/core_test/node.cpp | 4 +- nano/core_test/request_aggregator.cpp | 6 +- nano/core_test/toml.cpp | 8 +- nano/nano_node/entry.cpp | 4 +- nano/node/active_transactions.cpp | 7 +- nano/node/active_transactions.hpp | 6 +- nano/node/json_handler.cpp | 2 +- nano/node/node.cpp | 13 +- nano/node/node.hpp | 6 +- nano/node/nodeconfig.cpp | 8 +- nano/node/nodeconfig.hpp | 3 +- nano/node/wallet.cpp | 2 +- nano/rpc_test/rpc.cpp | 2 +- nano/slow_test/node.cpp | 115 +---------- 18 files changed, 260 insertions(+), 418 deletions(-) delete mode 100644 nano/core_test/confirmation_height.cpp diff --git a/nano/core_test/CMakeLists.txt b/nano/core_test/CMakeLists.txt index 74410c67b..b4111eead 100644 --- a/nano/core_test/CMakeLists.txt +++ b/nano/core_test/CMakeLists.txt @@ -12,7 +12,6 @@ add_executable( bootstrap_ascending.cpp bootstrap_server.cpp cli.cpp - confirmation_height.cpp confirmation_solicitor.cpp confirming_set.cpp conflicts.cpp diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index 196d65bfe..4360d1f26 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include #include @@ -1244,7 +1244,7 @@ TEST (active_transactions, activate_inactive) ASSERT_NE (nullptr, election); election->force_confirm (); - ASSERT_TIMELY (5s, !node.confirmation_height_processor.is_processing_added_block (send2->hash ())); + ASSERT_TIMELY (5s, !node.confirmation_height_processor.exists (send2->hash ())); ASSERT_TIMELY (5s, node.block_confirmed (send2->hash ())); ASSERT_TIMELY (5s, node.block_confirmed (send->hash ())); diff --git a/nano/core_test/confirmation_height.cpp b/nano/core_test/confirmation_height.cpp deleted file mode 100644 index 34c4ac9e5..000000000 --- a/nano/core_test/confirmation_height.cpp +++ /dev/null @@ -1,275 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace std::chrono_literals; - -namespace -{ -void add_callback_stats (nano::node & node, std::vector * observer_order = nullptr, nano::mutex * mutex = nullptr) -{ - node.observers.blocks.add ([&stats = node.stats, observer_order, mutex] (nano::election_status const & status_a, std::vector const &, nano::account const &, nano::amount const &, bool, bool) { - stats.inc (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out); - if (mutex) - { - nano::lock_guard guard (*mutex); - debug_assert (observer_order); - observer_order->push_back (status_a.winner->hash ()); - } - }); -} -nano::stat::detail get_stats_detail (nano::confirmation_height_mode mode_a) -{ - debug_assert (mode_a == nano::confirmation_height_mode::bounded || mode_a == nano::confirmation_height_mode::unbounded); - return (mode_a == nano::confirmation_height_mode::bounded) ? nano::stat::detail::blocks_confirmed_bounded : nano::stat::detail::blocks_confirmed_unbounded; -} -} - -TEST (confirmation_height, pending_observer_callbacks) -{ - auto test_mode = [] (nano::confirmation_height_mode mode_a) { - nano::test::system system; - nano::node_flags node_flags; - node_flags.confirmation_height_processor_mode = mode_a; - nano::node_config node_config = system.default_config (); - node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - auto node = system.add_node (node_config, node_flags); - - system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv); - nano::block_hash latest (node->latest (nano::dev::genesis_key.pub)); - - nano::keypair key1; - nano::block_builder builder; - auto send = builder - .send () - .previous (latest) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (latest)) - .build (); - auto send1 = builder - .send () - .previous (send->hash ()) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send->hash ())) - .build (); - - { - auto transaction = node->store.tx_begin_write (); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1)); - } - - add_callback_stats (*node); - - node->confirmation_height_processor.add (send1); - - // Callback is performed for all blocks that are confirmed - ASSERT_TIMELY_EQ (5s, 2, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out)) - ASSERT_TIMELY_EQ (5s, 2, node->ledger.stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); - - ASSERT_EQ (2, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (2, node->stats.count (nano::stat::type::confirmation_height, get_stats_detail (mode_a), nano::stat::dir::in)); - ASSERT_EQ (3, node->ledger.cache.cemented_count); - ASSERT_EQ (0, node->active.election_winner_details_size ()); - }; - - test_mode (nano::confirmation_height_mode::bounded); - test_mode (nano::confirmation_height_mode::unbounded); -} - -// The callback and confirmation history should only be updated after confirmation height is set (and not just after voting) -TEST (confirmation_height, callback_confirmed_history) -{ - auto test_mode = [] (nano::confirmation_height_mode mode_a) { - nano::test::system system; - nano::node_flags node_flags; - node_flags.force_use_write_database_queue = true; - node_flags.confirmation_height_processor_mode = mode_a; - nano::node_config node_config = system.default_config (); - node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - auto node = system.add_node (node_config, node_flags); - - nano::block_hash latest (node->latest (nano::dev::genesis_key.pub)); - - nano::keypair key1; - nano::block_builder builder; - auto send = builder - .send () - .previous (latest) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (latest)) - .build (); - { - auto transaction = node->store.tx_begin_write (); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send)); - } - - auto send1 = builder - .send () - .previous (send->hash ()) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send->hash ())) - .build (); - - add_callback_stats (*node); - - node->process_active (send1); - std::shared_ptr election; - ASSERT_TIMELY (5s, election = nano::test::start_election (system, *node, send1->hash ())); - { - // The write guard prevents the confirmation height processor doing any writes - auto write_guard = node->write_database_queue.wait (nano::writer::testing); - - // Confirm send1 - election->force_confirm (); - ASSERT_TIMELY_EQ (10s, node->active.size (), 0); - ASSERT_EQ (0, node->active.recently_cemented.list ().size ()); - ASSERT_TRUE (node->active.empty ()); - - auto transaction = node->store.tx_begin_read (); - ASSERT_FALSE (node->ledger.block_confirmed (transaction, send->hash ())); - - ASSERT_TIMELY (10s, node->write_database_queue.contains (nano::writer::confirmation_height)); - - // Confirm that no inactive callbacks have been called when the confirmation height processor has already iterated over it, waiting to write - ASSERT_EQ (0, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::inactive_conf_height, nano::stat::dir::out)); - } - - ASSERT_TIMELY (10s, !node->write_database_queue.contains (nano::writer::confirmation_height)); - - auto transaction = node->store.tx_begin_read (); - ASSERT_TRUE (node->ledger.block_confirmed (transaction, send->hash ())); - - ASSERT_TIMELY_EQ (10s, node->active.size (), 0); - ASSERT_TIMELY_EQ (10s, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_quorum, nano::stat::dir::out), 1); - - // Each block that's confirmed is in the recently_cemented history - ASSERT_EQ (2, node->active.recently_cemented.list ().size ()); - ASSERT_TRUE (node->active.empty ()); - - // Confirm the callback is not called under this circumstance - ASSERT_EQ (2, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out)); - ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_quorum, nano::stat::dir::out)); - ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::inactive_conf_height, nano::stat::dir::out)); - ASSERT_EQ (2, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (2, node->stats.count (nano::stat::type::confirmation_height, get_stats_detail (mode_a), nano::stat::dir::in)); - ASSERT_EQ (3, node->ledger.cache.cemented_count); - ASSERT_EQ (0, node->active.election_winner_details_size ()); - }; - - test_mode (nano::confirmation_height_mode::bounded); - test_mode (nano::confirmation_height_mode::unbounded); -} - -namespace nano -{ -TEST (confirmation_height, dependent_election) -{ - auto test_mode = [] (nano::confirmation_height_mode mode_a) { - nano::test::system system; - nano::node_flags node_flags; - node_flags.confirmation_height_processor_mode = mode_a; - node_flags.force_use_write_database_queue = true; - nano::node_config node_config = system.default_config (); - node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - auto node = system.add_node (node_config, node_flags); - - nano::block_hash latest (node->latest (nano::dev::genesis_key.pub)); - - nano::keypair key1; - nano::block_builder builder; - auto send = builder - .send () - .previous (latest) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (latest)) - .build (); - auto send1 = builder - .send () - .previous (send->hash ()) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send->hash ())) - .build (); - auto send2 = builder - .send () - .previous (send1->hash ()) - .destination (key1.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 3) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send1->hash ())) - .build (); - { - auto transaction = node->store.tx_begin_write (); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1)); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send2)); - } - - add_callback_stats (*node); - - // This election should be confirmed as active_conf_height - ASSERT_TRUE (nano::test::start_election (system, *node, send1->hash ())); - // Start an election and confirm it - auto election = nano::test::start_election (system, *node, send2->hash ()); - ASSERT_NE (nullptr, election); - election->force_confirm (); - - ASSERT_TIMELY_EQ (5s, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out), 3); - - ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_quorum, nano::stat::dir::out)); - ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_conf_height, nano::stat::dir::out)); - ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::inactive_conf_height, nano::stat::dir::out)); - ASSERT_EQ (3, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (3, node->stats.count (nano::stat::type::confirmation_height, get_stats_detail (mode_a), nano::stat::dir::in)); - ASSERT_EQ (4, node->ledger.cache.cemented_count); - - ASSERT_EQ (0, node->active.election_winner_details_size ()); - }; - - test_mode (nano::confirmation_height_mode::bounded); - test_mode (nano::confirmation_height_mode::unbounded); -} - -TEST (confirmation_height, election_winner_details_clearing_node_process_confirmed) -{ - // Make sure election_winner_details is also cleared if the block never enters the confirmation height processor from node::process_confirmed - nano::test::system system (1); - auto node = system.nodes.front (); - - nano::block_builder builder; - auto send = builder - .send () - .previous (nano::dev::genesis->hash ()) - .destination (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (nano::dev::genesis->hash ())) - .build (); - // Add to election_winner_details. Use an unrealistic iteration so that it should fall into the else case and do a cleanup - node->active.add_election_winner_details (send->hash (), nullptr); - nano::election_status election; - election.winner = send; - node->process_confirmed (election, 1000000); - ASSERT_EQ (0, node->active.election_winner_details_size ()); -} -} diff --git a/nano/core_test/confirming_set.cpp b/nano/core_test/confirming_set.cpp index 44df1ea18..6050d4375 100644 --- a/nano/core_test/confirming_set.cpp +++ b/nano/core_test/confirming_set.cpp @@ -66,3 +66,215 @@ TEST (confirming_set, process_multiple) ASSERT_EQ (2, ctx.stats ().count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); ASSERT_EQ (3, ctx.ledger ().cache.cemented_count); } + +TEST (confirmation_callback, observer_callbacks) +{ + nano::test::system system; + nano::node_flags node_flags; + nano::node_config node_config = system.default_config (); + node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; + auto node = system.add_node (node_config, node_flags); + + system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv); + nano::block_hash latest (node->latest (nano::dev::genesis_key.pub)); + + nano::keypair key1; + nano::block_builder builder; + auto send = builder + .send () + .previous (latest) + .destination (key1.pub) + .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (latest)) + .build (); + auto send1 = builder + .send () + .previous (send->hash ()) + .destination (key1.pub) + .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (send->hash ())) + .build (); + + { + auto transaction = node->store.tx_begin_write (); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1)); + } + + node->confirmation_height_processor.add (send1->hash ()); + + // Callback is performed for all blocks that are confirmed + ASSERT_TIMELY_EQ (5s, 2, node->ledger.stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); + + ASSERT_EQ (2, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); + ASSERT_EQ (3, node->ledger.cache.cemented_count); + ASSERT_EQ (0, node->active.election_winner_details_size ()); +} + +// The callback and confirmation history should only be updated after confirmation height is set (and not just after voting) +TEST (confirmation_callback, confirmed_history) +{ + nano::test::system system; + nano::node_flags node_flags; + node_flags.force_use_write_database_queue = true; + node_flags.disable_ascending_bootstrap = true; + nano::node_config node_config = system.default_config (); + node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; + auto node = system.add_node (node_config, node_flags); + + nano::block_hash latest (node->latest (nano::dev::genesis_key.pub)); + + nano::keypair key1; + nano::block_builder builder; + auto send = builder + .send () + .previous (latest) + .destination (key1.pub) + .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (latest)) + .build (); + { + auto transaction = node->store.tx_begin_write (); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send)); + } + + auto send1 = builder + .send () + .previous (send->hash ()) + .destination (key1.pub) + .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (send->hash ())) + .build (); + + node->process_active (send1); + std::shared_ptr election; + ASSERT_TIMELY (5s, election = nano::test::start_election (system, *node, send1->hash ())); + { + // The write guard prevents the confirmation height processor doing any writes + auto write_guard = node->write_database_queue.wait (nano::writer::testing); + + // Confirm send1 + election->force_confirm (); + ASSERT_TIMELY_EQ (10s, node->active.size (), 0); + ASSERT_EQ (0, node->active.recently_cemented.list ().size ()); + ASSERT_TRUE (node->active.empty ()); + + auto transaction = node->store.tx_begin_read (); + ASSERT_FALSE (node->ledger.block_confirmed (transaction, send->hash ())); + + ASSERT_TIMELY (10s, node->write_database_queue.contains (nano::writer::confirmation_height)); + + // Confirm that no inactive callbacks have been called when the confirmation height processor has already iterated over it, waiting to write + ASSERT_EQ (0, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::inactive_conf_height, nano::stat::dir::out)); + } + + ASSERT_TIMELY (10s, !node->write_database_queue.contains (nano::writer::confirmation_height)); + + auto transaction = node->store.tx_begin_read (); + ASSERT_TRUE (node->ledger.block_confirmed (transaction, send->hash ())); + + ASSERT_TIMELY_EQ (10s, node->active.size (), 0); + ASSERT_TIMELY_EQ (10s, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_quorum, nano::stat::dir::out), 1); + + // Each block that's confirmed is in the recently_cemented history + ASSERT_EQ (2, node->active.recently_cemented.list ().size ()); + ASSERT_TRUE (node->active.empty ()); + + // Confirm the callback is not called under this circumstance + ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_quorum, nano::stat::dir::out)); + ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::inactive_conf_height, nano::stat::dir::out)); + ASSERT_EQ (2, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); + ASSERT_EQ (3, node->ledger.cache.cemented_count); + ASSERT_EQ (0, node->active.election_winner_details_size ()); +} + +TEST (confirmation_callback, dependent_election) +{ + nano::test::system system; + nano::node_flags node_flags; + node_flags.force_use_write_database_queue = true; + nano::node_config node_config = system.default_config (); + node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; + auto node = system.add_node (node_config, node_flags); + + nano::block_hash latest (node->latest (nano::dev::genesis_key.pub)); + + nano::keypair key1; + nano::block_builder builder; + auto send = builder + .send () + .previous (latest) + .destination (key1.pub) + .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (latest)) + .build (); + auto send1 = builder + .send () + .previous (send->hash ()) + .destination (key1.pub) + .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (send->hash ())) + .build (); + auto send2 = builder + .send () + .previous (send1->hash ()) + .destination (key1.pub) + .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 3) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (send1->hash ())) + .build (); + { + auto transaction = node->store.tx_begin_write (); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1)); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send2)); + } + + // This election should be confirmed as active_conf_height + ASSERT_TRUE (nano::test::start_election (system, *node, send1->hash ())); + // Start an election and confirm it + auto election = nano::test::start_election (system, *node, send2->hash ()); + ASSERT_NE (nullptr, election); + election->force_confirm (); + + // Wait for blocks to be confirmed in ledger, callbacks will happen after + ASSERT_TIMELY_EQ (5s, 3, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); + // Once the item added to the confirming set no longer exists, callbacks have completed + ASSERT_TIMELY (5s, !node->confirmation_height_processor.exists (send2->hash ())); + + ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_quorum, nano::stat::dir::out)); + ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_conf_height, nano::stat::dir::out)); + ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::inactive_conf_height, nano::stat::dir::out)); + ASSERT_EQ (4, node->ledger.cache.cemented_count); + + ASSERT_EQ (0, node->active.election_winner_details_size ()); +} + +TEST (confirmation_callback, election_winner_details_clearing_node_process_confirmed) +{ + // Make sure election_winner_details is also cleared if the block never enters the confirmation height processor from node::process_confirmed + nano::test::system system (1); + auto node = system.nodes.front (); + + nano::block_builder builder; + auto send = builder + .send () + .previous (nano::dev::genesis->hash ()) + .destination (nano::dev::genesis_key.pub) + .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (nano::dev::genesis->hash ())) + .build (); + // Add to election_winner_details. Use an unrealistic iteration so that it should fall into the else case and do a cleanup + node->active.add_election_winner_details (send->hash (), nullptr); + nano::election_status election; + election.winner = send; + node->process_confirmed (election, 1000000); + ASSERT_EQ (0, node->active.election_winner_details_size ()); +} diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index 9ceff444f..988008f2b 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include #include @@ -1984,7 +1984,7 @@ TEST (node, DISABLED_local_votes_cache_batch) .work (*node.work_generate_blocking (nano::dev::genesis->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, node.ledger.process (node.store.tx_begin_write (), send1)); - node.confirmation_height_processor.add (send1); + node.confirmation_height_processor.add (send1->hash ()); ASSERT_TIMELY (5s, node.ledger.block_confirmed (node.store.tx_begin_read (), send1->hash ())); auto send2 = nano::state_block_builder () .account (nano::dev::genesis_key.pub) diff --git a/nano/core_test/request_aggregator.cpp b/nano/core_test/request_aggregator.cpp index da55fd013..ebaa75534 100644 --- a/nano/core_test/request_aggregator.cpp +++ b/nano/core_test/request_aggregator.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include #include @@ -80,7 +80,7 @@ TEST (request_aggregator, one_update) .work (*node.work_generate_blocking (nano::dev::genesis->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, node.ledger.process (node.store.tx_begin_write (), send1)); - node.confirmation_height_processor.add (send1); + node.confirmation_height_processor.add (send1->hash ()); ASSERT_TIMELY (5s, node.ledger.block_confirmed (node.store.tx_begin_read (), send1->hash ())); auto send2 = nano::state_block_builder () .account (nano::dev::genesis_key.pub) @@ -146,7 +146,7 @@ TEST (request_aggregator, two) .work (*node.work_generate_blocking (nano::dev::genesis->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, node.ledger.process (node.store.tx_begin_write (), send1)); - node.confirmation_height_processor.add (send1); + node.confirmation_height_processor.add (send1->hash ()); ASSERT_TIMELY (5s, node.ledger.block_confirmed (node.store.tx_begin_read (), send1->hash ())); auto send2 = builder.make_block () .account (nano::dev::genesis_key.pub) diff --git a/nano/core_test/toml.cpp b/nano/core_test/toml.cpp index c57c301eb..ae6f432a7 100644 --- a/nano/core_test/toml.cpp +++ b/nano/core_test/toml.cpp @@ -164,7 +164,7 @@ TEST (toml, daemon_config_deserialize_defaults) ASSERT_EQ (conf.node.bootstrap_serving_threads, defaults.node.bootstrap_serving_threads); ASSERT_EQ (conf.node.bootstrap_frontier_request_count, defaults.node.bootstrap_frontier_request_count); ASSERT_EQ (conf.node.bootstrap_fraction_numerator, defaults.node.bootstrap_fraction_numerator); - ASSERT_EQ (conf.node.conf_height_processor_batch_min_time, defaults.node.conf_height_processor_batch_min_time); + ASSERT_EQ (conf.node.confirming_set_batch_time, defaults.node.confirming_set_batch_time); ASSERT_EQ (conf.node.confirmation_history_size, defaults.node.confirmation_history_size); ASSERT_EQ (conf.node.enable_voting, defaults.node.enable_voting); ASSERT_EQ (conf.node.external_address, defaults.node.external_address); @@ -396,7 +396,7 @@ TEST (toml, daemon_config_deserialize_no_defaults) bootstrap_serving_threads = 999 bootstrap_frontier_request_count = 9999 bootstrap_fraction_numerator = 999 - conf_height_processor_batch_min_time = 999 + confirming_set_batch_time = 999 confirmation_history_size = 999 enable_voting = false external_address = "0:0:0:0:0:ffff:7f01:101" @@ -586,7 +586,7 @@ TEST (toml, daemon_config_deserialize_no_defaults) ASSERT_NE (conf.node.bootstrap_serving_threads, defaults.node.bootstrap_serving_threads); ASSERT_NE (conf.node.bootstrap_frontier_request_count, defaults.node.bootstrap_frontier_request_count); ASSERT_NE (conf.node.bootstrap_fraction_numerator, defaults.node.bootstrap_fraction_numerator); - ASSERT_NE (conf.node.conf_height_processor_batch_min_time, defaults.node.conf_height_processor_batch_min_time); + ASSERT_NE (conf.node.confirming_set_batch_time, defaults.node.confirming_set_batch_time); ASSERT_NE (conf.node.confirmation_history_size, defaults.node.confirmation_history_size); ASSERT_NE (conf.node.enable_voting, defaults.node.enable_voting); ASSERT_NE (conf.node.external_address, defaults.node.external_address); @@ -1017,4 +1017,4 @@ TEST (toml, log_config_no_required) confg.deserialize_toml (toml); ASSERT_FALSE (toml.get_error ()) << toml.get_error ().get_message (); -} \ No newline at end of file +} diff --git a/nano/nano_node/entry.cpp b/nano/nano_node/entry.cpp index 0e5e48571..9b8486ed8 100644 --- a/nano/nano_node/entry.cpp +++ b/nano/nano_node/entry.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include #include @@ -1213,7 +1213,7 @@ int main (int argc, char * const * argv) // Confirm blocks for node1 for (auto & block : blocks) { - node1->confirmation_height_processor.add (block); + node1->confirmation_height_processor.add (block->hash ()); } while (node1->ledger.cache.cemented_count != node1->ledger.cache.block_count) { diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index c8bdeb5fe..4393f0973 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -1,8 +1,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -15,7 +15,7 @@ using namespace std::chrono; -nano::active_transactions::active_transactions (nano::node & node_a, nano::confirmation_height_processor & confirmation_height_processor_a, nano::block_processor & block_processor_a) : +nano::active_transactions::active_transactions (nano::node & node_a, nano::confirming_set & confirmation_height_processor_a, nano::block_processor & block_processor_a) : node{ node_a }, confirmation_height_processor{ confirmation_height_processor_a }, block_processor{ block_processor_a }, @@ -82,6 +82,7 @@ void nano::active_transactions::stop () void nano::active_transactions::block_cemented_callback (std::shared_ptr const & block) { + debug_assert (node.block_confirmed (block->hash ())); if (auto election_l = election (block->qualified_root ())) { election_l->try_confirm (block->hash ()); @@ -95,7 +96,7 @@ void nano::active_transactions::block_cemented_callback (std::shared_ptrget_status (); votes = election->votes_with_weight (); } - if (confirmation_height_processor.is_processing_added_block (block->hash ())) + if (confirmation_height_processor.exists (block->hash ())) { status.type = nano::election_status_type::active_confirmed_quorum; } diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index 5c2a07d92..08885480a 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -28,9 +28,9 @@ class active_transactions; class block; class block_sideband; class block_processor; +class confirming_set; class election; class vote; -class confirmation_height_processor; class stats; } namespace nano::store @@ -141,7 +141,7 @@ private: // Elections std::unordered_map> blocks; public: - active_transactions (nano::node &, nano::confirmation_height_processor &, nano::block_processor &); + active_transactions (nano::node &, nano::confirming_set &, nano::block_processor &); ~active_transactions (); void start (); @@ -209,7 +209,7 @@ private: private: // Dependencies nano::node & node; - nano::confirmation_height_processor & confirmation_height_processor; + nano::confirming_set & confirmation_height_processor; nano::block_processor & block_processor; public: diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index c54baa7ab..fe217b759 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 3a6634ada..a032e60bf 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include #include @@ -172,9 +172,9 @@ nano::node::node (std::shared_ptr io_ctx_a, std::filesy application_path (application_path_a), port_mapping (*this), block_processor (*this, write_database_queue), - confirmation_height_processor_impl{ std::make_unique (ledger, write_database_queue, config.conf_height_processor_batch_min_time, logger, node_initialized_latch, flags.confirmation_height_processor_mode) }, - confirmation_height_processor{ *confirmation_height_processor_impl }, - active_impl{ std::make_unique (*this, confirmation_height_processor, block_processor) }, + confirming_set_impl{ std::make_unique (ledger, write_database_queue, config.confirming_set_batch_time) }, + confirming_set{ *confirming_set_impl }, + active_impl{ std::make_unique (*this, confirming_set, block_processor) }, active{ *active_impl }, rep_crawler (config.rep_crawler, *this), rep_tiers{ ledger, network_params, online_reps, stats, logger }, @@ -570,7 +570,7 @@ std::unique_ptr nano::collect_container_info (no 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")); + composite->add_component (node.confirmation_height_processor.collect_container_info ("confirmation_queue")); composite->add_component (collect_container_info (node.distributed_work, "distributed_work")); composite->add_component (collect_container_info (node.aggregator, "request_aggregator")); composite->add_component (node.scheduler.collect_container_info ("election_scheduler")); @@ -681,6 +681,7 @@ void nano::node::start () active.start (); generator.start (); final_generator.start (); + confirmation_height_processor.start (); scheduler.start (); backlog.start (); bootstrap_server.start (); @@ -1259,7 +1260,7 @@ void nano::node::process_confirmed (nano::election_status const & status_a, uint { logger.trace (nano::log::type::node, nano::log::detail::process_confirmed, nano::log::arg{ "block", block_l }); - confirmation_height_processor.add (block_l); + confirmation_height_processor.add (block_l->hash ()); } else if (iteration_a < num_iters) { diff --git a/nano/node/node.hpp b/nano/node/node.hpp index 2254c2f68..8c1a0476b 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -46,7 +46,7 @@ namespace nano { class active_transactions; -class confirmation_height_processor; +class confirming_set; class node; class work_pool; @@ -168,8 +168,8 @@ public: nano::node_observers observers; nano::port_mapping port_mapping; nano::block_processor block_processor; - std::unique_ptr confirmation_height_processor_impl; - nano::confirmation_height_processor & confirmation_height_processor; + std::unique_ptr confirmation_height_processor_impl; + nano::confirming_set & confirmation_height_processor; std::unique_ptr active_impl; nano::active_transactions & active; nano::online_reps online_reps; diff --git a/nano/node/nodeconfig.cpp b/nano/node/nodeconfig.cpp index c240c583c..ad597389b 100644 --- a/nano/node/nodeconfig.cpp +++ b/nano/node/nodeconfig.cpp @@ -126,7 +126,7 @@ nano::error nano::node_config::serialize_toml (nano::tomlconfig & toml) const toml.put ("bootstrap_bandwidth_limit", bootstrap_bandwidth_limit, "Outbound bootstrap traffic limit in bytes/sec after which messages will be dropped.\nNote: changing to unlimited bandwidth (0) is not recommended for limited connections.\ntype:uint64"); toml.put ("bootstrap_bandwidth_burst_ratio", bootstrap_bandwidth_burst_ratio, "Burst ratio for outbound bootstrap traffic.\ntype:double"); - toml.put ("conf_height_processor_batch_min_time", conf_height_processor_batch_min_time.count (), "Minimum write batching time when there are blocks pending confirmation height.\ntype:milliseconds"); + toml.put ("confirming_set_batch_time", confirming_set_batch_time.count (), "Maximum time the confirming set will hold the database write transaction.\ntype:milliseconds"); toml.put ("backup_before_upgrade", backup_before_upgrade, "Backup the ledger database before performing upgrades.\nWarning: uses more disk storage and increases startup time when upgrading.\ntype:bool"); toml.put ("max_work_generate_multiplier", max_work_generate_multiplier, "Maximum allowed difficulty multiplier for work generation.\ntype:double,[1..]"); toml.put ("frontiers_confirmation", serialize_frontiers_confirmation (frontiers_confirmation), "Mode controlling frontier confirmation rate.\ntype:string,{auto,always,disabled}"); @@ -432,9 +432,9 @@ nano::error nano::node_config::deserialize_toml (nano::tomlconfig & toml) toml.get ("backup_before_upgrade", backup_before_upgrade); - auto conf_height_processor_batch_min_time_l (conf_height_processor_batch_min_time.count ()); - toml.get ("conf_height_processor_batch_min_time", conf_height_processor_batch_min_time_l); - conf_height_processor_batch_min_time = std::chrono::milliseconds (conf_height_processor_batch_min_time_l); + auto confirming_set_batch_time_l (confirming_set_batch_time.count ()); + toml.get ("confirming_set_batch_time", confirming_set_batch_time_l); + confirming_set_batch_time = std::chrono::milliseconds (confirming_set_batch_time_l); toml.get ("max_work_generate_multiplier", max_work_generate_multiplier); diff --git a/nano/node/nodeconfig.hpp b/nano/node/nodeconfig.hpp index b93611aca..2b8f45357 100644 --- a/nano/node/nodeconfig.hpp +++ b/nano/node/nodeconfig.hpp @@ -119,7 +119,7 @@ public: /** Bootstrap traffic does not need bursts */ double bootstrap_bandwidth_burst_ratio{ 1. }; nano::bootstrap_ascending_config bootstrap_ascending; - std::chrono::milliseconds conf_height_processor_batch_min_time{ 50 }; + std::chrono::milliseconds confirming_set_batch_time{ 250 }; bool backup_before_upgrade{ false }; double max_work_generate_multiplier{ 64. }; uint32_t max_queued_requests{ 512 }; @@ -175,7 +175,6 @@ public: bool fast_bootstrap{ false }; bool read_only{ false }; bool disable_connection_cleanup{ false }; - nano::confirmation_height_mode confirmation_height_processor_mode{ nano::confirmation_height_mode::automatic }; nano::generate_cache_flags generate_cache; bool inactive_node{ false }; std::size_t block_processor_batch_size{ 0 }; diff --git a/nano/node/wallet.cpp b/nano/node/wallet.cpp index 28a3586df..a322b0037 100644 --- a/nano/node/wallet.cpp +++ b/nano/node/wallet.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 44d72c7eb..66c10fac8 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/nano/slow_test/node.cpp b/nano/slow_test/node.cpp index 28eee6571..2c1ff3c9e 100644 --- a/nano/slow_test/node.cpp +++ b/nano/slow_test/node.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include @@ -644,7 +644,6 @@ TEST (confirmation_height, many_accounts_single_confirmation) system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv); // The number of frontiers should be more than the nano::confirmation_height::unbounded_cutoff to test the amount of blocks confirmed is correct. - node->confirmation_height_processor.batch_write_size = 500; auto const num_accounts = nano::confirmation_height::unbounded_cutoff * 2 + 50; nano::keypair last_keypair = nano::dev::genesis_key; nano::block_builder builder; @@ -728,7 +727,6 @@ TEST (confirmation_height, many_accounts_many_confirmations) auto node = system.add_node (node_config); system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv); - node->confirmation_height_processor.batch_write_size = 500; auto const num_accounts = nano::confirmation_height::unbounded_cutoff * 2 + 50; auto latest_genesis = node->latest (nano::dev::genesis_key.pub); nano::block_builder builder; @@ -807,7 +805,6 @@ TEST (confirmation_height, long_chains) nano::block_hash latest (node->latest (nano::dev::genesis_key.pub)); system.wallet (0)->insert_adhoc (key1.prv); - node->confirmation_height_processor.batch_write_size = 500; auto const num_blocks = nano::confirmation_height::unbounded_cutoff * 2 + 50; nano::block_builder builder; @@ -978,10 +975,10 @@ TEST (confirmation_height, dynamic_algorithm) } } - node->confirmation_height_processor.add (state_blocks.front ()); + node->confirmation_height_processor.add (state_blocks.front ()->hash ()); ASSERT_TIMELY_EQ (20s, node->ledger.cache.cemented_count, 2); - node->confirmation_height_processor.add (latest_genesis); + node->confirmation_height_processor.add (latest_genesis->hash ()); ASSERT_TIMELY_EQ (20s, node->ledger.cache.cemented_count, num_blocks + 1); @@ -991,98 +988,6 @@ TEST (confirmation_height, dynamic_algorithm) ASSERT_TIMELY_EQ (10s, node->active.election_winner_details_size (), 0); } -/* - * This tests an issue of incorrect cemented block counts during the transition of conf height algorithms - * The scenario was as follows: - * - There is at least 1 pending write entry in the unbounded conf height processor - * - 0 blocks currently awaiting processing in the main conf height processor class - * - A block was confirmed when hit the chain in the pending write above but was not a block higher than it. - * - It must be in `confirmation_height_processor::pause ()` function so that `pause` is set (and the difference between the number - * of blocks uncemented is > unbounded_cutoff so that it hits the bounded processor), the main `run` loop on the conf height processor is iterated. - * - * This cause unbounded pending entries not to be written, and then the bounded processor would write them, causing some inconsistencies. - */ -TEST (confirmation_height, dynamic_algorithm_no_transition_while_pending) -{ - // Repeat in case of intermittent issues not replicating the issue talked about above. - for (auto _ = 0; _ < 3; ++_) - { - nano::test::system system; - nano::node_config node_config = system.default_config (); - node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - nano::node_flags node_flags; - node_flags.force_use_write_database_queue = true; - auto node = system.add_node (node_config, node_flags); - nano::keypair key; - system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv); - - auto latest_genesis = node->latest (nano::dev::genesis_key.pub); - std::vector> state_blocks; - auto const num_blocks = nano::confirmation_height::unbounded_cutoff - 2; - - auto add_block_to_genesis_chain = [&] (store::write_transaction & transaction) { - static int num = 0; - nano::block_builder builder; - auto send = builder - .state () - .account (nano::dev::genesis_key.pub) - .previous (latest_genesis) - .representative (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - num - 1) - .link (key.pub) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (latest_genesis)) - .build (); - latest_genesis = send->hash (); - state_blocks.push_back (send); - ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send)); - ++num; - }; - - for (auto i = 0; i < num_blocks; ++i) - { - auto transaction = node->store.tx_begin_write (); - add_block_to_genesis_chain (transaction); - } - - { - auto write_guard = node->write_database_queue.wait (nano::writer::testing); - // To limit any data races we are not calling node.block_confirm - node->confirmation_height_processor.add (state_blocks.back ()); - - nano::timer<> timer; - timer.start (); - while (node->confirmation_height_processor.current ().is_zero ()) - { - ASSERT_LT (timer.since_start (), 2s); - } - - // Pausing prevents any writes in the outer while loop in the confirmation height processor (implementation detail) - node->confirmation_height_processor.pause (); - - timer.restart (); - ASSERT_TIMELY (10s, node->confirmation_height_processor.unbounded_processor.pending_writes_size != 0); - - { - // Make it so that the number of blocks exceed the unbounded cutoff would go into the bounded processor (but shouldn't due to unbounded pending writes) - auto transaction = node->store.tx_begin_write (); - add_block_to_genesis_chain (transaction); - add_block_to_genesis_chain (transaction); - } - // Make sure this is at a height lower than the block in the add () call above - node->confirmation_height_processor.add (state_blocks.front ()); - node->confirmation_height_processor.unpause (); - } - - ASSERT_TIMELY_EQ (10s, node->ledger.cache.cemented_count, num_blocks + 1); - - ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in), num_blocks); - ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed_bounded, nano::stat::dir::in), 0); - ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed_unbounded, nano::stat::dir::in), num_blocks); - ASSERT_TIMELY_EQ (10s, node->active.election_winner_details_size (), 0); - } -} - TEST (confirmation_height, many_accounts_send_receive_self) { nano::test::system system; @@ -1091,7 +996,6 @@ TEST (confirmation_height, many_accounts_send_receive_self) node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; node_config.active_elections_size = 400000; nano::node_flags node_flags; - node_flags.confirmation_height_processor_mode = nano::confirmation_height_mode::unbounded; auto node = system.add_node (node_config); system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv); @@ -1239,7 +1143,8 @@ TEST (confirmation_height, many_accounts_send_receive_self_no_elections) boost::latch initialized_latch{ 0 }; nano::block_hash block_hash_being_processed{ 0 }; - nano::confirmation_height_processor confirmation_height_processor{ ledger, write_database_queue, 10ms, logger, initialized_latch, confirmation_height_mode::automatic }; + nano::write_database_queue write_queue{ false }; + nano::confirming_set confirmation_height_processor{ ledger, write_queue }; auto const num_accounts = 100000; @@ -1284,7 +1189,7 @@ TEST (confirmation_height, many_accounts_send_receive_self_no_elections) for (auto & open_block : open_blocks) { - confirmation_height_processor.add (open_block); + confirmation_height_processor.add (open_block->hash ()); } system.deadline_set (1000s); @@ -1335,8 +1240,8 @@ TEST (confirmation_height, many_accounts_send_receive_self_no_elections) // Now send and receive to self for (int i = 0; i < open_blocks.size (); ++i) { - confirmation_height_processor.add (send_blocks[i]); - confirmation_height_processor.add (receive_blocks[i]); + confirmation_height_processor.add (send_blocks[i]->hash ()); + confirmation_height_processor.add (receive_blocks[i]->hash ()); } system.deadline_set (1000s); @@ -1346,7 +1251,7 @@ TEST (confirmation_height, many_accounts_send_receive_self_no_elections) ASSERT_NO_ERROR (system.poll ()); } - while (!confirmation_height_processor.current ().is_zero ()) + while (confirmation_height_processor.size () > 0) { ASSERT_NO_ERROR (system.poll ()); } @@ -2173,7 +2078,7 @@ TEST (node, wallet_create_block_confirm_conflicts) election->force_confirm (); } - ASSERT_TIMELY (120s, node->ledger.block_confirmed (node->store.tx_begin_read (), latest) && node->confirmation_height_processor.current () == 0); + ASSERT_TIMELY (120s, node->ledger.block_confirmed (node->store.tx_begin_read (), latest) && node->confirmation_height_processor.size () == 0); done = true; t.join (); } From 2f24a98bb8d52f5441714c058892cb27ef90a0ee Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Sat, 23 Mar 2024 18:40:57 +0000 Subject: [PATCH 107/128] Remove confirmation_height_processor and rename usages to confirming_set. --- CMakeLists.txt | 1 - nano/core_test/active_transactions.cpp | 2 +- nano/core_test/confirming_set.cpp | 4 +- nano/core_test/node.cpp | 2 +- nano/core_test/request_aggregator.cpp | 4 +- nano/lib/locks.cpp | 4 +- nano/lib/locks.hpp | 1 - nano/nano_node/entry.cpp | 2 +- nano/node/CMakeLists.txt | 6 - nano/node/active_transactions.cpp | 10 +- nano/node/active_transactions.hpp | 2 +- nano/node/confirmation_height_bounded.cpp | 580 -------------------- nano/node/confirmation_height_bounded.hpp | 135 ----- nano/node/confirmation_height_processor.cpp | 225 -------- nano/node/confirmation_height_processor.hpp | 113 ---- nano/node/confirmation_height_unbounded.cpp | 498 ----------------- nano/node/confirmation_height_unbounded.hpp | 114 ---- nano/node/json_handler.cpp | 2 +- nano/node/node.cpp | 12 +- nano/node/node.hpp | 4 +- nano/node/wallet.cpp | 2 +- nano/rpc_test/rpc.cpp | 2 +- nano/secure/common.hpp | 7 - nano/slow_test/node.cpp | 16 +- 24 files changed, 33 insertions(+), 1715 deletions(-) delete mode 100644 nano/node/confirmation_height_bounded.cpp delete mode 100644 nano/node/confirmation_height_bounded.hpp delete mode 100644 nano/node/confirmation_height_processor.cpp delete mode 100644 nano/node/confirmation_height_processor.hpp delete mode 100644 nano/node/confirmation_height_unbounded.cpp delete mode 100644 nano/node/confirmation_height_unbounded.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c411cee10..10d92541c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,7 +97,6 @@ set_property( block_arrival block_processor block_uniquer - confirmation_height_processor dropped_elections, election_winner_details gap_cache diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index 4360d1f26..734eeeb70 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -1244,7 +1244,7 @@ TEST (active_transactions, activate_inactive) ASSERT_NE (nullptr, election); election->force_confirm (); - ASSERT_TIMELY (5s, !node.confirmation_height_processor.exists (send2->hash ())); + ASSERT_TIMELY (5s, !node.confirming_set.exists (send2->hash ())); ASSERT_TIMELY (5s, node.block_confirmed (send2->hash ())); ASSERT_TIMELY (5s, node.block_confirmed (send->hash ())); diff --git a/nano/core_test/confirming_set.cpp b/nano/core_test/confirming_set.cpp index 6050d4375..d7137eb54 100644 --- a/nano/core_test/confirming_set.cpp +++ b/nano/core_test/confirming_set.cpp @@ -103,7 +103,7 @@ TEST (confirmation_callback, observer_callbacks) ASSERT_EQ (nano::block_status::progress, node->ledger.process (transaction, send1)); } - node->confirmation_height_processor.add (send1->hash ()); + node->confirming_set.add (send1->hash ()); // Callback is performed for all blocks that are confirmed ASSERT_TIMELY_EQ (5s, 2, node->ledger.stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); @@ -246,7 +246,7 @@ TEST (confirmation_callback, dependent_election) // Wait for blocks to be confirmed in ledger, callbacks will happen after ASSERT_TIMELY_EQ (5s, 3, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); // Once the item added to the confirming set no longer exists, callbacks have completed - ASSERT_TIMELY (5s, !node->confirmation_height_processor.exists (send2->hash ())); + ASSERT_TIMELY (5s, !node->confirming_set.exists (send2->hash ())); ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_quorum, nano::stat::dir::out)); ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_conf_height, nano::stat::dir::out)); diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index 988008f2b..f9bc35ec5 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -1984,7 +1984,7 @@ TEST (node, DISABLED_local_votes_cache_batch) .work (*node.work_generate_blocking (nano::dev::genesis->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, node.ledger.process (node.store.tx_begin_write (), send1)); - node.confirmation_height_processor.add (send1->hash ()); + node.confirming_set.add (send1->hash ()); ASSERT_TIMELY (5s, node.ledger.block_confirmed (node.store.tx_begin_read (), send1->hash ())); auto send2 = nano::state_block_builder () .account (nano::dev::genesis_key.pub) diff --git a/nano/core_test/request_aggregator.cpp b/nano/core_test/request_aggregator.cpp index ebaa75534..99d429ab0 100644 --- a/nano/core_test/request_aggregator.cpp +++ b/nano/core_test/request_aggregator.cpp @@ -80,7 +80,7 @@ TEST (request_aggregator, one_update) .work (*node.work_generate_blocking (nano::dev::genesis->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, node.ledger.process (node.store.tx_begin_write (), send1)); - node.confirmation_height_processor.add (send1->hash ()); + node.confirming_set.add (send1->hash ()); ASSERT_TIMELY (5s, node.ledger.block_confirmed (node.store.tx_begin_read (), send1->hash ())); auto send2 = nano::state_block_builder () .account (nano::dev::genesis_key.pub) @@ -146,7 +146,7 @@ TEST (request_aggregator, two) .work (*node.work_generate_blocking (nano::dev::genesis->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, node.ledger.process (node.store.tx_begin_write (), send1)); - node.confirmation_height_processor.add (send1->hash ()); + node.confirming_set.add (send1->hash ()); ASSERT_TIMELY (5s, node.ledger.block_confirmed (node.store.tx_begin_read (), send1->hash ())); auto send2 = builder.make_block () .account (nano::dev::genesis_key.pub) diff --git a/nano/lib/locks.cpp b/nano/lib/locks.cpp index 231ef13c3..f90f08b5e 100644 --- a/nano/lib/locks.cpp +++ b/nano/lib/locks.cpp @@ -257,8 +257,6 @@ char const * nano::mutex_identifier (mutexes mutex) return "block_uniquer"; case mutexes::blockstore_cache: return "blockstore_cache"; - case mutexes::confirmation_height_processor: - return "confirmation_height_processor"; case mutexes::election_winner_details: return "election_winner_details"; case mutexes::gap_cache: @@ -286,4 +284,4 @@ char const * nano::mutex_identifier (mutexes mutex) } throw std::runtime_error ("Invalid mutexes enum specified"); -} \ No newline at end of file +} diff --git a/nano/lib/locks.hpp b/nano/lib/locks.hpp index 1bbe0a7fa..0b5f76631 100644 --- a/nano/lib/locks.hpp +++ b/nano/lib/locks.hpp @@ -23,7 +23,6 @@ enum class mutexes block_processor, block_uniquer, blockstore_cache, - confirmation_height_processor, election_winner_details, gap_cache, network_filter, diff --git a/nano/nano_node/entry.cpp b/nano/nano_node/entry.cpp index 9b8486ed8..55e6d7f5c 100644 --- a/nano/nano_node/entry.cpp +++ b/nano/nano_node/entry.cpp @@ -1213,7 +1213,7 @@ int main (int argc, char * const * argv) // Confirm blocks for node1 for (auto & block : blocks) { - node1->confirmation_height_processor.add (block->hash ()); + node1->confirming_set.add (block->hash ()); } while (node1->ledger.cache.cemented_count != node1->ledger.cache.block_count) { diff --git a/nano/node/CMakeLists.txt b/nano/node/CMakeLists.txt index b21cdae1a..7600ef9d1 100644 --- a/nano/node/CMakeLists.txt +++ b/nano/node/CMakeLists.txt @@ -61,12 +61,6 @@ add_library( common.cpp confirming_set.hpp confirming_set.cpp - confirmation_height_bounded.hpp - confirmation_height_bounded.cpp - confirmation_height_processor.hpp - confirmation_height_processor.cpp - confirmation_height_unbounded.hpp - confirmation_height_unbounded.cpp confirmation_solicitor.hpp confirmation_solicitor.cpp daemonconfig.hpp diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 4393f0973..708078a53 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -15,9 +15,9 @@ using namespace std::chrono; -nano::active_transactions::active_transactions (nano::node & node_a, nano::confirming_set & confirmation_height_processor_a, nano::block_processor & block_processor_a) : +nano::active_transactions::active_transactions (nano::node & node_a, nano::confirming_set & confirming_set, nano::block_processor & block_processor_a) : node{ node_a }, - confirmation_height_processor{ confirmation_height_processor_a }, + confirming_set{ confirming_set }, block_processor{ block_processor_a }, recently_confirmed{ 65536 }, recently_cemented{ node.config.confirmation_history_size }, @@ -26,12 +26,12 @@ nano::active_transactions::active_transactions (nano::node & node_a, nano::confi count_by_behavior.fill (0); // Zero initialize array // Register a callback which will get called after a block is cemented - confirmation_height_processor.cemented_observers.add ([this] (std::shared_ptr const & callback_block_a) { + confirming_set.cemented_observers.add ([this] (std::shared_ptr const & callback_block_a) { this->block_cemented_callback (callback_block_a); }); // Register a callback which will get called if a block is already cemented - confirmation_height_processor.block_already_cemented_observers.add ([this] (nano::block_hash const & hash_a) { + confirming_set.block_already_cemented_observers.add ([this] (nano::block_hash const & hash_a) { this->block_already_cemented_callback (hash_a); }); @@ -96,7 +96,7 @@ void nano::active_transactions::block_cemented_callback (std::shared_ptrget_status (); votes = election->votes_with_weight (); } - if (confirmation_height_processor.exists (block->hash ())) + if (confirming_set.exists (block->hash ())) { status.type = nano::election_status_type::active_confirmed_quorum; } diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index 08885480a..7fded5f62 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -209,7 +209,7 @@ private: private: // Dependencies nano::node & node; - nano::confirming_set & confirmation_height_processor; + nano::confirming_set & confirming_set; nano::block_processor & block_processor; public: diff --git a/nano/node/confirmation_height_bounded.cpp b/nano/node/confirmation_height_bounded.cpp deleted file mode 100644 index dfb7dea6d..000000000 --- a/nano/node/confirmation_height_bounded.cpp +++ /dev/null @@ -1,580 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -nano::confirmation_height_bounded::confirmation_height_bounded (nano::ledger & ledger_a, nano::write_database_queue & write_database_queue_a, std::chrono::milliseconds batch_separate_pending_min_time_a, nano::logger & logger_a, std::atomic & stopped_a, uint64_t & batch_write_size_a, std::function> const &)> const & notify_observers_callback_a, std::function const & notify_block_already_cemented_observers_callback_a, std::function const & awaiting_processing_size_callback_a) : - ledger (ledger_a), - write_database_queue (write_database_queue_a), - batch_separate_pending_min_time (batch_separate_pending_min_time_a), - logger (logger_a), - stopped (stopped_a), - batch_write_size (batch_write_size_a), - notify_observers_callback (notify_observers_callback_a), - notify_block_already_cemented_observers_callback (notify_block_already_cemented_observers_callback_a), - awaiting_processing_size_callback (awaiting_processing_size_callback_a) -{ -} - -// The next block hash to iterate over, the priority is as follows: -// 1 - The next block in the account chain for the last processed receive (if there is any) -// 2 - The next receive block which is closest to genesis -// 3 - The last checkpoint hit. -// 4 - The hash that was passed in originally. Either all checkpoints were exhausted (this can happen when there are many accounts to genesis) -// or all other blocks have been processed. -nano::confirmation_height_bounded::top_and_next_hash nano::confirmation_height_bounded::get_next_block (boost::optional const & next_in_receive_chain_a, boost::circular_buffer_space_optimized const & checkpoints_a, boost::circular_buffer_space_optimized const & receive_source_pairs, boost::optional & receive_details_a, nano::block const & original_block) -{ - top_and_next_hash next; - if (next_in_receive_chain_a.is_initialized ()) - { - next = *next_in_receive_chain_a; - } - else if (!receive_source_pairs.empty ()) - { - auto next_receive_source_pair = receive_source_pairs.back (); - receive_details_a = next_receive_source_pair.receive_details; - next = { next_receive_source_pair.source_hash, receive_details_a->next, receive_details_a->height + 1 }; - } - else if (!checkpoints_a.empty ()) - { - next = { checkpoints_a.back (), boost::none, 0 }; - } - else - { - next = { original_block.hash (), boost::none, 0 }; - } - - return next; -} - -void nano::confirmation_height_bounded::process (std::shared_ptr original_block) -{ - if (pending_empty ()) - { - clear_process_vars (); - timer.restart (); - } - - boost::optional next_in_receive_chain; - boost::circular_buffer_space_optimized checkpoints{ max_items }; - boost::circular_buffer_space_optimized receive_source_pairs{ max_items }; - nano::block_hash current; - bool first_iter = true; - auto transaction (ledger.store.tx_begin_read ()); - do - { - boost::optional receive_details; - auto hash_to_process = get_next_block (next_in_receive_chain, checkpoints, receive_source_pairs, receive_details, *original_block); - current = hash_to_process.top; - - auto top_level_hash = current; - std::shared_ptr block; - if (first_iter) - { - debug_assert (current == original_block->hash ()); - block = original_block; - } - else - { - block = ledger.block (transaction, current); - } - - if (!block) - { - if (ledger.pruning && ledger.store.pruned.exists (transaction, current)) - { - if (!receive_source_pairs.empty ()) - { - receive_source_pairs.pop_back (); - } - continue; - } - else - { - logger.critical (nano::log::type::conf_processor_bounded, "Ledger mismatch trying to set confirmation height for block {} (bounded processor)", current.to_string ()); - - release_assert (block); - } - } - auto account = block->account (); - - // Checks if we have encountered this account before but not commited changes yet, if so then update the cached confirmation height - nano::confirmation_height_info confirmation_height_info; - auto account_it = accounts_confirmed_info.find (account); - if (account_it != accounts_confirmed_info.cend ()) - { - confirmation_height_info.height = account_it->second.confirmed_height; - confirmation_height_info.frontier = account_it->second.iterated_frontier; - } - else - { - ledger.store.confirmation_height.get (transaction, account, confirmation_height_info); - // This block was added to the confirmation height processor but is already confirmed - if (first_iter && confirmation_height_info.height >= block->sideband ().height && current == original_block->hash ()) - { - notify_block_already_cemented_observers_callback (original_block->hash ()); - } - } - - auto block_height = block->sideband ().height; - bool already_cemented = confirmation_height_info.height >= block_height; - - // If we are not already at the bottom of the account chain (1 above cemented frontier) then find it - if (!already_cemented && block_height - confirmation_height_info.height > 1) - { - if (block_height - confirmation_height_info.height == 2) - { - // If there is 1 uncemented block in-between this block and the cemented frontier, - // we can just use the previous block to get the least unconfirmed hash. - current = block->previous (); - --block_height; - } - else if (!next_in_receive_chain.is_initialized ()) - { - current = get_least_unconfirmed_hash_from_top_level (transaction, current, account, confirmation_height_info, block_height); - } - else - { - // Use the cached successor of the last receive which saves having to do more IO in get_least_unconfirmed_hash_from_top_level - // as we already know what the next block we should process should be. - current = *hash_to_process.next; - block_height = hash_to_process.next_height; - } - } - - auto top_most_non_receive_block_hash = current; - - bool hit_receive = false; - if (!already_cemented) - { - hit_receive = iterate (transaction, block_height, current, checkpoints, top_most_non_receive_block_hash, top_level_hash, receive_source_pairs, account); - } - - // Exit early when the processor has been stopped, otherwise this function may take a - // while (and hence keep the process running) if updating a long chain. - if (stopped) - { - break; - } - - // next_in_receive_chain can be modified when writing, so need to cache it here before resetting - auto is_set = next_in_receive_chain.is_initialized (); - next_in_receive_chain = boost::none; - - // Need to also handle the case where we are hitting receives where the sends below should be confirmed - if (!hit_receive || (receive_source_pairs.size () == 1 && top_most_non_receive_block_hash != current)) - { - preparation_data preparation_data{ transaction, top_most_non_receive_block_hash, already_cemented, checkpoints, account_it, confirmation_height_info, account, block_height, current, receive_details, next_in_receive_chain }; - prepare_iterated_blocks_for_cementing (preparation_data); - - // If used the top level, don't pop off the receive source pair because it wasn't used - if (!is_set && !receive_source_pairs.empty ()) - { - receive_source_pairs.pop_back (); - } - - auto total_pending_write_block_count = std::accumulate (pending_writes.cbegin (), pending_writes.cend (), uint64_t (0), [] (uint64_t total, auto const & write_details_a) { - return total += write_details_a.top_height - write_details_a.bottom_height + 1; - }); - - auto max_batch_write_size_reached = (total_pending_write_block_count >= batch_write_size); - // When there are a lot of pending confirmation height blocks, it is more efficient to - // bulk some of them up to enable better write performance which becomes the bottleneck. - auto min_time_exceeded = (timer.since_start () >= batch_separate_pending_min_time); - auto finished_iterating = current == original_block->hash (); - auto non_awaiting_processing = awaiting_processing_size_callback () == 0; - auto should_output = finished_iterating && (non_awaiting_processing || min_time_exceeded); - auto force_write = pending_writes.size () >= pending_writes_max_size || accounts_confirmed_info.size () >= pending_writes_max_size; - - if ((max_batch_write_size_reached || should_output || force_write) && !pending_writes.empty ()) - { - // If nothing is currently using the database write lock then write the cemented pending blocks otherwise continue iterating - if (write_database_queue.process (nano::writer::confirmation_height)) - { - auto scoped_write_guard = write_database_queue.pop (); - cement_blocks (scoped_write_guard); - } - else if (force_write) - { - auto scoped_write_guard = write_database_queue.wait (nano::writer::confirmation_height); - cement_blocks (scoped_write_guard); - } - } - } - - first_iter = false; - transaction.refresh (); - } while ((!receive_source_pairs.empty () || current != original_block->hash ()) && !stopped); - - debug_assert (checkpoints.empty ()); -} - -nano::block_hash nano::confirmation_height_bounded::get_least_unconfirmed_hash_from_top_level (store::transaction const & transaction_a, nano::block_hash const & hash_a, nano::account const & account_a, nano::confirmation_height_info const & confirmation_height_info_a, uint64_t & block_height_a) -{ - nano::block_hash least_unconfirmed_hash = hash_a; - if (confirmation_height_info_a.height != 0) - { - if (block_height_a > confirmation_height_info_a.height) - { - auto block = ledger.block (transaction_a, confirmation_height_info_a.frontier); - release_assert (block != nullptr); - least_unconfirmed_hash = block->sideband ().successor; - block_height_a = block->sideband ().height + 1; - } - } - else - { - // No blocks have been confirmed, so the first block will be the open block - auto info = ledger.account_info (transaction_a, account_a); - release_assert (info); - least_unconfirmed_hash = info->open_block; - block_height_a = 1; - } - return least_unconfirmed_hash; -} - -bool nano::confirmation_height_bounded::iterate (store::read_transaction const & transaction_a, uint64_t bottom_height_a, nano::block_hash const & bottom_hash_a, boost::circular_buffer_space_optimized & checkpoints_a, nano::block_hash & top_most_non_receive_block_hash_a, nano::block_hash const & top_level_hash_a, boost::circular_buffer_space_optimized & receive_source_pairs_a, nano::account const & account_a) -{ - bool reached_target = false; - bool hit_receive = false; - auto hash = bottom_hash_a; - uint64_t num_blocks = 0; - while (!hash.is_zero () && !reached_target && !stopped) - { - // Keep iterating upwards until we either reach the desired block or the second receive. - // Once a receive is cemented, we can cement all blocks above it until the next receive, so store those details for later. - ++num_blocks; - auto block = ledger.block (transaction_a, hash); - if (block->is_receive () && ledger.block_exists (transaction_a, block->source ())) - { - hit_receive = true; - reached_target = true; - auto const & sideband (block->sideband ()); - auto next = !sideband.successor.is_zero () && sideband.successor != top_level_hash_a ? boost::optional (sideband.successor) : boost::none; - receive_source_pairs_a.push_back ({ receive_chain_details{ account_a, sideband.height, hash, top_level_hash_a, next, bottom_height_a, bottom_hash_a }, block->source () }); - // Store a checkpoint every max_items so that we can always traverse a long number of accounts to genesis - if (receive_source_pairs_a.size () % max_items == 0) - { - checkpoints_a.push_back (top_level_hash_a); - } - } - else - { - // Found a send/change/epoch block which isn't the desired top level - top_most_non_receive_block_hash_a = hash; - if (hash == top_level_hash_a) - { - reached_target = true; - } - else - { - hash = block->sideband ().successor; - } - } - - // We could be traversing a very large account so we don't want to open read transactions for too long. - if ((num_blocks > 0) && num_blocks % batch_read_size == 0) - { - transaction_a.refresh (); - } - } - - return hit_receive; -} - -// Once the path to genesis has been iterated to, we can begin to cement the lowest blocks in the accounts. This sets up -// the non-receive blocks which have been iterated for an account, and the associated receive block. -void nano::confirmation_height_bounded::prepare_iterated_blocks_for_cementing (preparation_data & preparation_data_a) -{ - if (!preparation_data_a.already_cemented) - { - // Add the non-receive blocks iterated for this account - auto block_height = (ledger.height (preparation_data_a.transaction, preparation_data_a.top_most_non_receive_block_hash)); - if (block_height > preparation_data_a.confirmation_height_info.height) - { - confirmed_info confirmed_info_l{ block_height, preparation_data_a.top_most_non_receive_block_hash }; - if (preparation_data_a.account_it != accounts_confirmed_info.cend ()) - { - preparation_data_a.account_it->second = confirmed_info_l; - } - else - { - accounts_confirmed_info.emplace (preparation_data_a.account, confirmed_info_l); - ++accounts_confirmed_info_size; - } - - preparation_data_a.checkpoints.erase (std::remove (preparation_data_a.checkpoints.begin (), preparation_data_a.checkpoints.end (), preparation_data_a.top_most_non_receive_block_hash), preparation_data_a.checkpoints.end ()); - pending_writes.emplace_back (preparation_data_a.account, preparation_data_a.bottom_height, preparation_data_a.bottom_most, block_height, preparation_data_a.top_most_non_receive_block_hash); - ++pending_writes_size; - } - } - - // Add the receive block and all non-receive blocks above that one - auto & receive_details = preparation_data_a.receive_details; - if (receive_details) - { - auto receive_confirmed_info_it = accounts_confirmed_info.find (receive_details->account); - if (receive_confirmed_info_it != accounts_confirmed_info.cend ()) - { - auto & receive_confirmed_info = receive_confirmed_info_it->second; - receive_confirmed_info.confirmed_height = receive_details->height; - receive_confirmed_info.iterated_frontier = receive_details->hash; - } - else - { - accounts_confirmed_info.emplace (std::piecewise_construct, std::forward_as_tuple (receive_details->account), std::forward_as_tuple (receive_details->height, receive_details->hash)); - ++accounts_confirmed_info_size; - } - - if (receive_details->next.is_initialized ()) - { - preparation_data_a.next_in_receive_chain = top_and_next_hash{ receive_details->top_level, receive_details->next, receive_details->height + 1 }; - } - else - { - preparation_data_a.checkpoints.erase (std::remove (preparation_data_a.checkpoints.begin (), preparation_data_a.checkpoints.end (), receive_details->hash), preparation_data_a.checkpoints.end ()); - } - - pending_writes.emplace_back (receive_details->account, receive_details->bottom_height, receive_details->bottom_most, receive_details->height, receive_details->hash); - ++pending_writes_size; - } -} - -void nano::confirmation_height_bounded::cement_blocks (nano::write_guard & scoped_write_guard_a) -{ - // Will contain all blocks that have been cemented (bounded by batch_write_size) - // and will get run through the cemented observer callback - std::vector> cemented_blocks; - auto const maximum_batch_write_time = 250; // milliseconds - auto const maximum_batch_write_time_increase_cutoff = maximum_batch_write_time - (maximum_batch_write_time / 5); - auto const amount_to_change = batch_write_size / 10; // 10% - auto const minimum_batch_write_size = 16384u; - nano::timer<> cemented_batch_timer; - auto error = false; - { - // This only writes to the confirmation_height table and is the only place to do so in a single process - auto transaction (ledger.store.tx_begin_write ({}, { nano::tables::confirmation_height })); - cemented_batch_timer.start (); - // Cement all pending entries, each entry is specific to an account and contains the least amount - // of blocks to retain consistent cementing across all account chains to genesis. - while (!error && !pending_writes.empty ()) - { - auto const & pending = pending_writes.front (); - auto const & account = pending.account; - - auto write_confirmation_height = [&account, &ledger = ledger, &transaction] (uint64_t num_blocks_cemented, uint64_t confirmation_height, nano::block_hash const & confirmed_frontier) { -#ifndef NDEBUG - // Extra debug checks - nano::confirmation_height_info confirmation_height_info; - ledger.store.confirmation_height.get (transaction, account, confirmation_height_info); - auto block = ledger.block (transaction, confirmed_frontier); - debug_assert (block != nullptr); - debug_assert (block->sideband ().height == confirmation_height_info.height + num_blocks_cemented); -#endif - ledger.store.confirmation_height.put (transaction, account, nano::confirmation_height_info{ confirmation_height, confirmed_frontier }); - ledger.cache.cemented_count += num_blocks_cemented; - ledger.stats.add (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in, num_blocks_cemented); - ledger.stats.add (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed_bounded, nano::stat::dir::in, num_blocks_cemented); - }; - - nano::confirmation_height_info confirmation_height_info; - ledger.store.confirmation_height.get (transaction, pending.account, confirmation_height_info); - - // Some blocks need to be cemented at least - if (pending.top_height > confirmation_height_info.height) - { - // The highest hash which will be cemented - nano::block_hash new_cemented_frontier; - uint64_t num_blocks_confirmed = 0; - uint64_t start_height = 0; - if (pending.bottom_height > confirmation_height_info.height) - { - new_cemented_frontier = pending.bottom_hash; - // If we are higher than the cemented frontier, we should be exactly 1 block above - debug_assert (pending.bottom_height == confirmation_height_info.height + 1); - num_blocks_confirmed = pending.top_height - pending.bottom_height + 1; - start_height = pending.bottom_height; - } - else - { - auto block = ledger.block (transaction, confirmation_height_info.frontier); - new_cemented_frontier = block->sideband ().successor; - num_blocks_confirmed = pending.top_height - confirmation_height_info.height; - start_height = confirmation_height_info.height + 1; - } - - auto total_blocks_cemented = 0; - auto block = ledger.block (transaction, new_cemented_frontier); - - // Cementing starts from the bottom of the chain and works upwards. This is because chains can have effectively - // an infinite number of send/change blocks in a row. We don't want to hold the write transaction open for too long. - for (auto num_blocks_iterated = 0; num_blocks_confirmed - num_blocks_iterated != 0; ++num_blocks_iterated) - { - if (!block) - { - logger.critical (nano::log::type::conf_processor_bounded, "Failed to write confirmation height for block {} (bounded processor)", new_cemented_frontier.to_string ()); - - // Undo any blocks about to be cemented from this account for this pending write. - cemented_blocks.erase (cemented_blocks.end () - num_blocks_iterated, cemented_blocks.end ()); - error = true; - break; - } - - auto last_iteration = (num_blocks_confirmed - num_blocks_iterated) == 1; - - cemented_blocks.emplace_back (block); - - // Flush these callbacks and continue as we write in batches (ideally maximum 250ms) to not hold write db transaction for too long. - // Include a tolerance to save having to potentially wait on the block processor if the number of blocks to cement is only a bit higher than the max. - if (cemented_blocks.size () > batch_write_size + (batch_write_size / 10)) - { - auto time_spent_cementing = cemented_batch_timer.since_start ().count (); - auto num_blocks_cemented = num_blocks_iterated - total_blocks_cemented + 1; - total_blocks_cemented += num_blocks_cemented; - write_confirmation_height (num_blocks_cemented, start_height + total_blocks_cemented - 1, new_cemented_frontier); - transaction.commit (); - - // Update the maximum amount of blocks to write next time based on the time it took to cement this batch. - if (time_spent_cementing > maximum_batch_write_time) - { - // Reduce (unless we have hit a floor) - batch_write_size = std::max (minimum_batch_write_size, batch_write_size - amount_to_change); - } - else if (time_spent_cementing < maximum_batch_write_time_increase_cutoff) - { - // Increase amount of blocks written for next batch if the time for writing this one is sufficiently lower than the max time to warrant changing - batch_write_size += amount_to_change; - } - - scoped_write_guard_a.release (); - notify_observers_callback (cemented_blocks); - cemented_blocks.clear (); - - // Only aquire transaction if there are blocks left - if (!(last_iteration && pending_writes.size () == 1)) - { - scoped_write_guard_a = write_database_queue.wait (nano::writer::confirmation_height); - transaction.renew (); - } - cemented_batch_timer.restart (); - } - - // Get the next block in the chain until we have reached the final desired one - if (!last_iteration) - { - new_cemented_frontier = block->sideband ().successor; - block = ledger.block (transaction, new_cemented_frontier); - } - else - { - // Confirm it is indeed the last one - debug_assert (new_cemented_frontier == pending.top_hash); - } - } - - if (error) - { - // There was an error writing a block, do not process any more - break; - } - - auto num_blocks_cemented = num_blocks_confirmed - total_blocks_cemented; - if (num_blocks_cemented > 0) - { - write_confirmation_height (num_blocks_cemented, pending.top_height, new_cemented_frontier); - } - } - - auto it = accounts_confirmed_info.find (pending.account); - if (it != accounts_confirmed_info.cend () && it->second.confirmed_height == pending.top_height) - { - accounts_confirmed_info.erase (pending.account); - --accounts_confirmed_info_size; - } - pending_writes.pop_front (); - --pending_writes_size; - } - } - - auto time_spent_cementing = cemented_batch_timer.since_start (); - - // Scope guard could have been released earlier (0 cemented_blocks would indicate that) - if (scoped_write_guard_a.is_owned () && !cemented_blocks.empty ()) - { - scoped_write_guard_a.release (); - notify_observers_callback (cemented_blocks); - } - - // Bail if there was an error. This indicates that there was a fatal issue with the ledger - // (the blocks probably got rolled back when they shouldn't have). - release_assert (!error); - - if (time_spent_cementing.count () > maximum_batch_write_time) - { - // Reduce (unless we have hit a floor) - batch_write_size = std::max (minimum_batch_write_size, batch_write_size - amount_to_change); - } - - debug_assert (pending_writes.empty ()); - debug_assert (pending_writes_size == 0); - timer.restart (); -} - -bool nano::confirmation_height_bounded::pending_empty () const -{ - return pending_writes.empty (); -} - -void nano::confirmation_height_bounded::clear_process_vars () -{ - accounts_confirmed_info.clear (); - accounts_confirmed_info_size = 0; -} - -nano::confirmation_height_bounded::receive_chain_details::receive_chain_details (nano::account const & account_a, uint64_t height_a, nano::block_hash const & hash_a, nano::block_hash const & top_level_a, boost::optional next_a, uint64_t bottom_height_a, nano::block_hash const & bottom_most_a) : - account (account_a), - height (height_a), - hash (hash_a), - top_level (top_level_a), - next (next_a), - bottom_height (bottom_height_a), - bottom_most (bottom_most_a) -{ -} - -nano::confirmation_height_bounded::write_details::write_details (nano::account const & account_a, uint64_t bottom_height_a, nano::block_hash const & bottom_hash_a, uint64_t top_height_a, nano::block_hash const & top_hash_a) : - account (account_a), - bottom_height (bottom_height_a), - bottom_hash (bottom_hash_a), - top_height (top_height_a), - top_hash (top_hash_a) -{ -} - -nano::confirmation_height_bounded::receive_source_pair::receive_source_pair (confirmation_height_bounded::receive_chain_details const & receive_details_a, const block_hash & source_a) : - receive_details (receive_details_a), - source_hash (source_a) -{ -} - -nano::confirmation_height_bounded::confirmed_info::confirmed_info (uint64_t confirmed_height_a, nano::block_hash const & iterated_frontier_a) : - confirmed_height (confirmed_height_a), - iterated_frontier (iterated_frontier_a) -{ -} - -std::unique_ptr nano::collect_container_info (confirmation_height_bounded & confirmation_height_bounded, std::string const & name_a) -{ - auto composite = std::make_unique (name_a); - composite->add_component (std::make_unique (container_info{ "pending_writes", confirmation_height_bounded.pending_writes_size, sizeof (decltype (confirmation_height_bounded.pending_writes)::value_type) })); - composite->add_component (std::make_unique (container_info{ "accounts_confirmed_info", confirmation_height_bounded.accounts_confirmed_info_size, sizeof (decltype (confirmation_height_bounded.accounts_confirmed_info)::value_type) })); - return composite; -} diff --git a/nano/node/confirmation_height_bounded.hpp b/nano/node/confirmation_height_bounded.hpp deleted file mode 100644 index a5052b145..000000000 --- a/nano/node/confirmation_height_bounded.hpp +++ /dev/null @@ -1,135 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include - -namespace nano -{ -class ledger; -class write_database_queue; -class write_guard; - -class confirmation_height_bounded final -{ -public: - confirmation_height_bounded (nano::ledger &, nano::write_database_queue &, std::chrono::milliseconds batch_separate_pending_min_time, nano::logger &, std::atomic & stopped, uint64_t & batch_write_size, std::function> const &)> const & cemented_callback, std::function const & already_cemented_callback, std::function const & awaiting_processing_size_query); - - bool pending_empty () const; - void clear_process_vars (); - void process (std::shared_ptr original_block); - void cement_blocks (nano::write_guard & scoped_write_guard_a); - -private: - class top_and_next_hash final - { - public: - nano::block_hash top; - boost::optional next; - uint64_t next_height; - }; - - class confirmed_info - { - public: - confirmed_info (uint64_t confirmed_height_a, nano::block_hash const & iterated_frontier); - uint64_t confirmed_height; - nano::block_hash iterated_frontier; - }; - - class write_details final - { - public: - write_details (nano::account const &, uint64_t, nano::block_hash const &, uint64_t, nano::block_hash const &); - nano::account account; - // This is the first block hash (bottom most) which is not cemented - uint64_t bottom_height; - nano::block_hash bottom_hash; - // Desired cemented frontier - uint64_t top_height; - nano::block_hash top_hash; - }; - - /** The maximum number of blocks to be read in while iterating over a long account chain */ - uint64_t const batch_read_size = 65536; - - /** The maximum number of various containers to keep the memory bounded */ - uint32_t const max_items{ 131072 }; - - // All of the atomic variables here just track the size for use in collect_container_info. - // This is so that no mutexes are needed during the algorithm itself, which would otherwise be needed - // for the sake of a rarely used RPC call for debugging purposes. As such the sizes are not being acted - // upon in any way (does not synchronize with any other data). - // This allows the load and stores to use relaxed atomic memory ordering. - std::deque pending_writes; - nano::relaxed_atomic_integral pending_writes_size{ 0 }; - uint32_t const pending_writes_max_size{ max_items }; - /* Holds confirmation height/cemented frontier in memory for accounts while iterating */ - std::unordered_map accounts_confirmed_info; - nano::relaxed_atomic_integral accounts_confirmed_info_size{ 0 }; - - class receive_chain_details final - { - public: - receive_chain_details (nano::account const &, uint64_t, nano::block_hash const &, nano::block_hash const &, boost::optional, uint64_t, nano::block_hash const &); - nano::account account; - uint64_t height; - nano::block_hash hash; - nano::block_hash top_level; - boost::optional next; - uint64_t bottom_height; - nano::block_hash bottom_most; - }; - - class preparation_data final - { - public: - store::transaction const & transaction; - nano::block_hash const & top_most_non_receive_block_hash; - bool already_cemented; - boost::circular_buffer_space_optimized & checkpoints; - decltype (accounts_confirmed_info.begin ()) account_it; - nano::confirmation_height_info const & confirmation_height_info; - nano::account const & account; - uint64_t bottom_height; - nano::block_hash const & bottom_most; - boost::optional & receive_details; - boost::optional & next_in_receive_chain; - }; - - class receive_source_pair final - { - public: - receive_source_pair (receive_chain_details const &, nano::block_hash const &); - - receive_chain_details receive_details; - nano::block_hash source_hash; - }; - - nano::timer timer; - - top_and_next_hash get_next_block (boost::optional const &, boost::circular_buffer_space_optimized const &, boost::circular_buffer_space_optimized const & receive_source_pairs, boost::optional &, nano::block const & original_block); - nano::block_hash get_least_unconfirmed_hash_from_top_level (store::transaction const &, nano::block_hash const &, nano::account const &, nano::confirmation_height_info const &, uint64_t &); - void prepare_iterated_blocks_for_cementing (preparation_data &); - bool iterate (store::read_transaction const &, uint64_t, nano::block_hash const &, boost::circular_buffer_space_optimized &, nano::block_hash &, nano::block_hash const &, boost::circular_buffer_space_optimized &, nano::account const &); - - nano::ledger & ledger; - nano::write_database_queue & write_database_queue; - std::chrono::milliseconds batch_separate_pending_min_time; - nano::logger & logger; - std::atomic & stopped; - uint64_t & batch_write_size; - std::function> const &)> notify_observers_callback; - std::function notify_block_already_cemented_observers_callback; - std::function awaiting_processing_size_callback; - - friend std::unique_ptr collect_container_info (confirmation_height_bounded &, std::string const & name_a); -}; - -std::unique_ptr collect_container_info (confirmation_height_bounded &, std::string const & name_a); -} diff --git a/nano/node/confirmation_height_processor.cpp b/nano/node/confirmation_height_processor.cpp deleted file mode 100644 index 5161980ee..000000000 --- a/nano/node/confirmation_height_processor.cpp +++ /dev/null @@ -1,225 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -nano::confirmation_height_processor::confirmation_height_processor (nano::ledger & ledger_a, nano::write_database_queue & write_database_queue_a, std::chrono::milliseconds batch_separate_pending_min_time_a, nano::logger & logger_a, boost::latch & latch, confirmation_height_mode mode_a) : - ledger (ledger_a), - write_database_queue (write_database_queue_a), - unbounded_processor ( - ledger_a, write_database_queue_a, batch_separate_pending_min_time_a, logger_a, stopped, batch_write_size, - /* cemented_callback */ [this] (auto & cemented_blocks) { this->notify_cemented (cemented_blocks); }, - /* already cemented_callback */ [this] (auto const & block_hash_a) { this->notify_already_cemented (block_hash_a); }, - /* awaiting_processing_size_query */ [this] () { return this->awaiting_processing_size (); }), - bounded_processor ( - ledger_a, write_database_queue_a, batch_separate_pending_min_time_a, logger_a, stopped, batch_write_size, - /* cemented_callback */ [this] (auto & cemented_blocks) { this->notify_cemented (cemented_blocks); }, - /* already cemented_callback */ [this] (auto const & block_hash_a) { this->notify_already_cemented (block_hash_a); }, - /* awaiting_processing_size_query */ [this] () { return this->awaiting_processing_size (); }), - thread ([this, &latch, mode_a] () { - nano::thread_role::set (nano::thread_role::name::confirmation_height_processing); - // Do not start running the processing thread until other threads have finished their operations - latch.wait (); - this->run (mode_a); - }) -{ -} - -nano::confirmation_height_processor::~confirmation_height_processor () -{ - stop (); -} - -void nano::confirmation_height_processor::stop () -{ - { - nano::lock_guard guard (mutex); - stopped = true; - } - condition.notify_one (); - if (thread.joinable ()) - { - thread.join (); - } -} - -void nano::confirmation_height_processor::run (confirmation_height_mode mode_a) -{ - nano::unique_lock lk (mutex); - while (!stopped) - { - if (!paused && !awaiting_processing.empty ()) - { - lk.unlock (); - if (bounded_processor.pending_empty () && unbounded_processor.pending_empty ()) - { - lk.lock (); - original_hashes_pending.clear (); - lk.unlock (); - } - - set_next_hash (); - - auto const num_blocks_to_use_unbounded = confirmation_height::unbounded_cutoff; - auto blocks_within_automatic_unbounded_selection = (ledger.cache.block_count < num_blocks_to_use_unbounded || ledger.cache.block_count - num_blocks_to_use_unbounded < ledger.cache.cemented_count); - - // Don't want to mix up pending writes across different processors - auto valid_unbounded = (mode_a == confirmation_height_mode::automatic && blocks_within_automatic_unbounded_selection && bounded_processor.pending_empty ()); - auto force_unbounded = (!unbounded_processor.pending_empty () || mode_a == confirmation_height_mode::unbounded); - if (force_unbounded || valid_unbounded) - { - debug_assert (bounded_processor.pending_empty ()); - unbounded_processor.process (original_block); - } - else - { - debug_assert (mode_a == confirmation_height_mode::bounded || mode_a == confirmation_height_mode::automatic); - debug_assert (unbounded_processor.pending_empty ()); - bounded_processor.process (original_block); - } - - lk.lock (); - } - else - { - auto lock_and_cleanup = [&lk, this] () { - lk.lock (); - original_block = nullptr; - original_hashes_pending.clear (); - bounded_processor.clear_process_vars (); - unbounded_processor.clear_process_vars (); - }; - - if (!paused) - { - lk.unlock (); - - // If there are blocks pending cementing, then make sure we flush out the remaining writes - if (!bounded_processor.pending_empty ()) - { - debug_assert (unbounded_processor.pending_empty ()); - { - auto scoped_write_guard = write_database_queue.wait (nano::writer::confirmation_height); - bounded_processor.cement_blocks (scoped_write_guard); - } - lock_and_cleanup (); - } - else if (!unbounded_processor.pending_empty ()) - { - debug_assert (bounded_processor.pending_empty ()); - { - auto scoped_write_guard = write_database_queue.wait (nano::writer::confirmation_height); - unbounded_processor.cement_blocks (scoped_write_guard); - } - lock_and_cleanup (); - } - else - { - lock_and_cleanup (); - // A block could have been confirmed during the re-locking - if (awaiting_processing.empty ()) - { - condition.wait (lk); - } - } - } - else - { - // Pausing is only utilised in some tests to help prevent it processing added blocks until required. - original_block = nullptr; - condition.wait (lk); - } - } - } -} - -// Pausing only affects processing new blocks, not the current one being processed. Currently only used in tests -void nano::confirmation_height_processor::pause () -{ - nano::lock_guard lk (mutex); - paused = true; -} - -void nano::confirmation_height_processor::unpause () -{ - { - nano::lock_guard lk (mutex); - paused = false; - } - condition.notify_one (); -} - -void nano::confirmation_height_processor::add (std::shared_ptr const & block_a) -{ - { - nano::lock_guard lk (mutex); - awaiting_processing.get ().emplace_back (block_a); - } - condition.notify_one (); -} - -void nano::confirmation_height_processor::set_next_hash () -{ - nano::lock_guard guard (mutex); - debug_assert (!awaiting_processing.empty ()); - original_block = awaiting_processing.get ().front ().block; - original_hashes_pending.insert (original_block->hash ()); - awaiting_processing.get ().pop_front (); -} - -void nano::confirmation_height_processor::notify_cemented (std::vector> const & cemented_blocks) -{ - for (auto const & block_callback_data : cemented_blocks) - { - cemented_observers.notify (block_callback_data); - } -} - -void nano::confirmation_height_processor::notify_already_cemented (nano::block_hash const & hash_already_cemented_a) -{ - block_already_cemented_observers.notify (hash_already_cemented_a); -} - -std::unique_ptr nano::collect_container_info (confirmation_height_processor & confirmation_height_processor_a, std::string const & name_a) -{ - auto composite = std::make_unique (name_a); - - composite->add_component (std::make_unique (container_info{ "awaiting_processing", confirmation_height_processor_a.awaiting_processing_size (), sizeof (decltype (confirmation_height_processor_a.awaiting_processing)::value_type) })); - composite->add_component (collect_container_info (confirmation_height_processor_a.bounded_processor, "bounded_processor")); - composite->add_component (collect_container_info (confirmation_height_processor_a.unbounded_processor, "unbounded_processor")); - return composite; -} - -std::size_t nano::confirmation_height_processor::awaiting_processing_size () const -{ - nano::lock_guard guard (mutex); - return awaiting_processing.size (); -} - -bool nano::confirmation_height_processor::is_processing_added_block (nano::block_hash const & hash_a) const -{ - nano::lock_guard guard (mutex); - return original_hashes_pending.count (hash_a) > 0 || awaiting_processing.get ().count (hash_a) > 0; -} - -bool nano::confirmation_height_processor::exists (nano::block_hash const & hash_a) const -{ - return is_processing_added_block (hash_a) || unbounded_processor.has_iterated_over_block (hash_a); -} - -nano::block_hash nano::confirmation_height_processor::current () const -{ - nano::lock_guard lk (mutex); - return original_block ? original_block->hash () : 0; -} - -std::reference_wrapper nano::confirmation_height_processor::block_wrapper::hash () const -{ - return block->hash (); -} diff --git a/nano/node/confirmation_height_processor.hpp b/nano/node/confirmation_height_processor.hpp deleted file mode 100644 index 2ab69fd62..000000000 --- a/nano/node/confirmation_height_processor.hpp +++ /dev/null @@ -1,113 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -namespace mi = boost::multi_index; -namespace boost -{ -class latch; -} -namespace nano -{ -class ledger; -class write_database_queue; - -class confirmation_height_processor final -{ -public: - confirmation_height_processor (nano::ledger &, nano::write_database_queue &, std::chrono::milliseconds, nano::logger &, boost::latch & initialized_latch, confirmation_height_mode = confirmation_height_mode::automatic); - ~confirmation_height_processor (); - - void pause (); - void unpause (); - void stop (); - void add (std::shared_ptr const &); - void run (confirmation_height_mode); - std::size_t awaiting_processing_size () const; - bool is_processing_added_block (nano::block_hash const & hash_a) const; - bool exists (nano::block_hash const &) const; - nano::block_hash current () const; - - nano::observer_set const &> cemented_observers; - nano::observer_set block_already_cemented_observers; - -private: - mutable nano::mutex mutex{ mutex_identifier (mutexes::confirmation_height_processor) }; - - // Hashes which have been added to the confirmation height processor, but not yet processed - struct block_wrapper - { - explicit block_wrapper (std::shared_ptr const & block_a) : - block (block_a) - { - } - - std::reference_wrapper hash () const; - - std::shared_ptr block; - }; - // clang-format off - class tag_sequence {}; - class tag_hash {}; - boost::multi_index_container>, - mi::hashed_unique, - mi::const_mem_fun, &block_wrapper::hash>>>> awaiting_processing; - // clang-format on - - // Hashes which have been added and processed, but have not been cemented - std::unordered_set original_hashes_pending; - bool paused{ false }; - - /** This is the last block popped off the confirmation height pending collection */ - std::shared_ptr original_block; - - nano::condition_variable condition; - std::atomic stopped{ false }; - - nano::ledger & ledger; - nano::write_database_queue & write_database_queue; - /** The maximum amount of blocks to write at once. This is dynamically modified by the bounded processor based on previous write performance **/ - uint64_t batch_write_size{ 16384 }; - - confirmation_height_unbounded unbounded_processor; - confirmation_height_bounded bounded_processor; - std::thread thread; - - void set_next_hash (); - void notify_cemented (std::vector> const &); - void notify_already_cemented (nano::block_hash const &); - - friend std::unique_ptr collect_container_info (confirmation_height_processor &, std::string const &); - -private: // Tests - friend class confirmation_height_pending_observer_callbacks_Test; - friend class confirmation_height_dependent_election_Test; - friend class confirmation_height_dependent_election_after_already_cemented_Test; - friend class confirmation_height_dynamic_algorithm_no_transition_while_pending_Test; - friend class confirmation_height_many_accounts_many_confirmations_Test; - friend class confirmation_height_long_chains_Test; - friend class confirmation_height_many_accounts_single_confirmation_Test; - friend class request_aggregator_cannot_vote_Test; - friend class active_transactions_pessimistic_elections_Test; -}; - -std::unique_ptr collect_container_info (confirmation_height_processor &, std::string const &); -} diff --git a/nano/node/confirmation_height_unbounded.cpp b/nano/node/confirmation_height_unbounded.cpp deleted file mode 100644 index 20e7101c1..000000000 --- a/nano/node/confirmation_height_unbounded.cpp +++ /dev/null @@ -1,498 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -nano::confirmation_height_unbounded::confirmation_height_unbounded (nano::ledger & ledger_a, nano::write_database_queue & write_database_queue_a, std::chrono::milliseconds batch_separate_pending_min_time_a, nano::logger & logger_a, std::atomic & stopped_a, uint64_t & batch_write_size_a, std::function> const &)> const & notify_observers_callback_a, std::function const & notify_block_already_cemented_observers_callback_a, std::function const & awaiting_processing_size_callback_a) : - ledger (ledger_a), - write_database_queue (write_database_queue_a), - batch_separate_pending_min_time (batch_separate_pending_min_time_a), - logger (logger_a), - stopped (stopped_a), - batch_write_size (batch_write_size_a), - notify_observers_callback (notify_observers_callback_a), - notify_block_already_cemented_observers_callback (notify_block_already_cemented_observers_callback_a), - awaiting_processing_size_callback (awaiting_processing_size_callback_a) -{ -} - -void nano::confirmation_height_unbounded::process (std::shared_ptr original_block) -{ - if (pending_empty ()) - { - clear_process_vars (); - timer.restart (); - } - std::shared_ptr receive_details; - auto current = original_block->hash (); - std::vector orig_block_callback_data; - - std::vector receive_source_pairs; - release_assert (receive_source_pairs.empty ()); - - bool first_iter = true; - auto read_transaction (ledger.store.tx_begin_read ()); - - do - { - if (!receive_source_pairs.empty ()) - { - receive_details = receive_source_pairs.back ().receive_details; - current = receive_source_pairs.back ().source_hash; - } - else - { - // If receive_details is set then this is the final iteration and we are back to the original chain. - // We need to confirm any blocks below the original hash (incl self) and the first receive block - // (if the original block is not already a receive) - if (receive_details) - { - current = original_block->hash (); - receive_details = nullptr; - } - } - - std::shared_ptr block; - if (first_iter) - { - debug_assert (current == original_block->hash ()); - // This is the original block passed so can use it directly - block = original_block; - nano::lock_guard guard (block_cache_mutex); - block_cache[original_block->hash ()] = original_block; - } - else - { - block = get_block_and_sideband (current, read_transaction); - } - if (!block) - { - logger.critical (nano::log::type::conf_processor_unbounded, "Ledger mismatch trying to set confirmation height for block {} (unbounded processor)", current.to_string ()); - } - release_assert (block); - - auto account = block->account (); - - auto block_height = block->sideband ().height; - uint64_t confirmation_height = 0; - auto account_it = confirmed_iterated_pairs.find (account); - if (account_it != confirmed_iterated_pairs.cend ()) - { - confirmation_height = account_it->second.confirmed_height; - } - else - { - nano::confirmation_height_info confirmation_height_info; - ledger.store.confirmation_height.get (read_transaction, account, confirmation_height_info); - confirmation_height = confirmation_height_info.height; - - // This block was added to the confirmation height processor but is already confirmed - if (first_iter && confirmation_height >= block_height) - { - debug_assert (current == original_block->hash ()); - notify_block_already_cemented_observers_callback (original_block->hash ()); - } - } - auto iterated_height = confirmation_height; - if (account_it != confirmed_iterated_pairs.cend () && account_it->second.iterated_height > iterated_height) - { - iterated_height = account_it->second.iterated_height; - } - - auto count_before_receive = receive_source_pairs.size (); - std::vector block_callback_datas_required; - auto already_traversed = iterated_height >= block_height; - if (!already_traversed) - { - collect_unconfirmed_receive_and_sources_for_account (block_height, iterated_height, block, current, account, read_transaction, receive_source_pairs, block_callback_datas_required, orig_block_callback_data, original_block); - } - - // Exit early when the processor has been stopped, otherwise this function may take a - // while (and hence keep the process running) if updating a long chain. - if (stopped) - { - break; - } - - // No longer need the read transaction - read_transaction.reset (); - - // If this adds no more open or receive blocks, then we can now confirm this account as well as the linked open/receive block - // Collect as pending any writes to the database and do them in bulk after a certain time. - auto confirmed_receives_pending = (count_before_receive != receive_source_pairs.size ()); - if (!confirmed_receives_pending) - { - preparation_data preparation_data{ block_height, confirmation_height, iterated_height, account_it, account, receive_details, already_traversed, current, block_callback_datas_required, orig_block_callback_data }; - prepare_iterated_blocks_for_cementing (preparation_data); - - if (!receive_source_pairs.empty ()) - { - // Pop from the end - receive_source_pairs.erase (receive_source_pairs.end () - 1); - } - } - else if (block_height > iterated_height) - { - if (account_it != confirmed_iterated_pairs.cend ()) - { - account_it->second.iterated_height = block_height; - } - else - { - confirmed_iterated_pairs.emplace (std::piecewise_construct, std::forward_as_tuple (account), std::forward_as_tuple (confirmation_height, block_height)); - ++confirmed_iterated_pairs_size; - } - } - - auto max_write_size_reached = (pending_writes.size () >= confirmation_height::unbounded_cutoff); - // When there are a lot of pending confirmation height blocks, it is more efficient to - // bulk some of them up to enable better write performance which becomes the bottleneck. - auto min_time_exceeded = (timer.since_start () >= batch_separate_pending_min_time); - auto finished_iterating = receive_source_pairs.empty (); - auto no_pending = awaiting_processing_size_callback () == 0; - auto should_output = finished_iterating && (no_pending || min_time_exceeded); - - auto total_pending_write_block_count = std::accumulate (pending_writes.cbegin (), pending_writes.cend (), uint64_t (0), [] (uint64_t total, conf_height_details const & receive_details_a) { - return total += receive_details_a.num_blocks_confirmed; - }); - auto force_write = total_pending_write_block_count > batch_write_size; - - if ((max_write_size_reached || should_output || force_write) && !pending_writes.empty ()) - { - if (write_database_queue.process (nano::writer::confirmation_height)) - { - auto scoped_write_guard = write_database_queue.pop (); - cement_blocks (scoped_write_guard); - } - else if (force_write) - { - // Unbounded processor has grown too large, force a write - auto scoped_write_guard = write_database_queue.wait (nano::writer::confirmation_height); - cement_blocks (scoped_write_guard); - } - } - - first_iter = false; - read_transaction.renew (); - } while ((!receive_source_pairs.empty () || current != original_block->hash ()) && !stopped); -} - -void nano::confirmation_height_unbounded::collect_unconfirmed_receive_and_sources_for_account (uint64_t block_height_a, uint64_t confirmation_height_a, std::shared_ptr const & block_a, nano::block_hash const & hash_a, nano::account const & account_a, store::read_transaction const & transaction_a, std::vector & receive_source_pairs_a, std::vector & block_callback_data_a, std::vector & orig_block_callback_data_a, std::shared_ptr original_block) -{ - debug_assert (block_a->hash () == hash_a); - auto hash (hash_a); - auto num_to_confirm = block_height_a - confirmation_height_a; - - // Handle any sends above a receive - auto is_original_block = (hash == original_block->hash ()); - auto hit_receive = false; - auto first_iter = true; - while ((num_to_confirm > 0) && !hash.is_zero () && !stopped) - { - std::shared_ptr block; - if (first_iter) - { - debug_assert (hash == hash_a); - block = block_a; - nano::lock_guard guard (block_cache_mutex); - block_cache[hash] = block_a; - } - else - { - block = get_block_and_sideband (hash, transaction_a); - } - - if (block) - { - if (block->is_receive () && ledger.block_exists (transaction_a, block->source ())) - { - if (!hit_receive && !block_callback_data_a.empty ()) - { - // Add the callbacks to the associated receive to retrieve later - debug_assert (!receive_source_pairs_a.empty ()); - auto & last_receive_details = receive_source_pairs_a.back ().receive_details; - last_receive_details->source_block_callback_data.assign (block_callback_data_a.begin (), block_callback_data_a.end ()); - block_callback_data_a.clear (); - } - - is_original_block = false; - hit_receive = true; - - auto block_height = confirmation_height_a + num_to_confirm; - receive_source_pairs_a.emplace_back (std::make_shared (account_a, hash, block_height, 1, std::vector{ hash }), block->source ()); - } - else if (is_original_block) - { - orig_block_callback_data_a.push_back (hash); - } - else - { - if (!hit_receive) - { - // This block is cemented via a recieve, as opposed to below a receive being cemented - block_callback_data_a.push_back (hash); - } - else - { - // We have hit a receive before, add the block to it - auto & last_receive_details = receive_source_pairs_a.back ().receive_details; - ++last_receive_details->num_blocks_confirmed; - last_receive_details->block_callback_data.push_back (hash); - - implicit_receive_cemented_mapping[hash] = std::weak_ptr (last_receive_details); - implicit_receive_cemented_mapping_size = implicit_receive_cemented_mapping.size (); - } - } - - hash = block->previous (); - } - - --num_to_confirm; - first_iter = false; - } -} - -void nano::confirmation_height_unbounded::prepare_iterated_blocks_for_cementing (preparation_data & preparation_data_a) -{ - auto receive_details = preparation_data_a.receive_details; - auto block_height = preparation_data_a.block_height; - if (block_height > preparation_data_a.confirmation_height) - { - // Check whether the previous block has been seen. If so, the rest of sends below have already been seen so don't count them - if (preparation_data_a.account_it != confirmed_iterated_pairs.cend ()) - { - preparation_data_a.account_it->second.confirmed_height = block_height; - if (block_height > preparation_data_a.iterated_height) - { - preparation_data_a.account_it->second.iterated_height = block_height; - } - } - else - { - confirmed_iterated_pairs.emplace (std::piecewise_construct, std::forward_as_tuple (preparation_data_a.account), std::forward_as_tuple (block_height, block_height)); - ++confirmed_iterated_pairs_size; - } - - auto num_blocks_confirmed = block_height - preparation_data_a.confirmation_height; - auto block_callback_data = preparation_data_a.block_callback_data; - if (block_callback_data.empty ()) - { - if (!receive_details) - { - block_callback_data = preparation_data_a.orig_block_callback_data; - } - else - { - debug_assert (receive_details); - - if (preparation_data_a.already_traversed && receive_details->source_block_callback_data.empty ()) - { - // We are confirming a block which has already been traversed and found no associated receive details for it. - auto & above_receive_details_w = implicit_receive_cemented_mapping[preparation_data_a.current]; - debug_assert (!above_receive_details_w.expired ()); - auto above_receive_details = above_receive_details_w.lock (); - - auto num_blocks_already_confirmed = above_receive_details->num_blocks_confirmed - (above_receive_details->height - preparation_data_a.confirmation_height); - - auto end_it = above_receive_details->block_callback_data.begin () + above_receive_details->block_callback_data.size () - (num_blocks_already_confirmed); - auto start_it = end_it - num_blocks_confirmed; - - block_callback_data.assign (start_it, end_it); - } - else - { - block_callback_data = receive_details->source_block_callback_data; - } - - auto num_to_remove = block_callback_data.size () - num_blocks_confirmed; - block_callback_data.erase (std::next (block_callback_data.rbegin (), num_to_remove).base (), block_callback_data.end ()); - receive_details->source_block_callback_data.clear (); - } - } - - pending_writes.emplace_back (preparation_data_a.account, preparation_data_a.current, block_height, num_blocks_confirmed, block_callback_data); - ++pending_writes_size; - } - - if (receive_details) - { - // Check whether the previous block has been seen. If so, the rest of sends below have already been seen so don't count them - auto const & receive_account = receive_details->account; - auto receive_account_it = confirmed_iterated_pairs.find (receive_account); - if (receive_account_it != confirmed_iterated_pairs.cend ()) - { - // Get current height - auto current_height = receive_account_it->second.confirmed_height; - receive_account_it->second.confirmed_height = receive_details->height; - auto const orig_num_blocks_confirmed = receive_details->num_blocks_confirmed; - receive_details->num_blocks_confirmed = receive_details->height - current_height; - - // Get the difference and remove the callbacks - auto block_callbacks_to_remove = orig_num_blocks_confirmed - receive_details->num_blocks_confirmed; - receive_details->block_callback_data.erase (std::next (receive_details->block_callback_data.rbegin (), block_callbacks_to_remove).base (), receive_details->block_callback_data.end ()); - debug_assert (receive_details->block_callback_data.size () == receive_details->num_blocks_confirmed); - } - else - { - confirmed_iterated_pairs.emplace (std::piecewise_construct, std::forward_as_tuple (receive_account), std::forward_as_tuple (receive_details->height, receive_details->height)); - ++confirmed_iterated_pairs_size; - } - - pending_writes.push_back (*receive_details); - ++pending_writes_size; - } -} - -void nano::confirmation_height_unbounded::cement_blocks (nano::write_guard & scoped_write_guard_a) -{ - nano::timer cemented_batch_timer; - std::vector> cemented_blocks; - auto error = false; - { - auto transaction (ledger.store.tx_begin_write ({}, { nano::tables::confirmation_height })); - cemented_batch_timer.start (); - while (!pending_writes.empty ()) - { - auto & pending = pending_writes.front (); - nano::confirmation_height_info confirmation_height_info; - ledger.store.confirmation_height.get (transaction, pending.account, confirmation_height_info); - auto confirmation_height = confirmation_height_info.height; - if (pending.height > confirmation_height) - { - auto block = ledger.block (transaction, pending.hash); - debug_assert (ledger.pruning || block != nullptr); - debug_assert (ledger.pruning || block->sideband ().height == pending.height); - - if (!block) - { - if (ledger.pruning && ledger.store.pruned.exists (transaction, pending.hash)) - { - pending_writes.erase (pending_writes.begin ()); - --pending_writes_size; - continue; - } - else - { - logger.critical (nano::log::type::conf_processor_unbounded, "Failed to write confirmation height for block {} (unbounded processor)", pending.hash.to_string ()); - - error = true; - break; - } - } - ledger.stats.add (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in, pending.height - confirmation_height); - ledger.stats.add (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed_unbounded, nano::stat::dir::in, pending.height - confirmation_height); - debug_assert (pending.num_blocks_confirmed == pending.height - confirmation_height); - confirmation_height = pending.height; - ledger.cache.cemented_count += pending.num_blocks_confirmed; - ledger.store.confirmation_height.put (transaction, pending.account, { confirmation_height, pending.hash }); - - // Reverse it so that the callbacks start from the lowest newly cemented block and move upwards - std::reverse (pending.block_callback_data.begin (), pending.block_callback_data.end ()); - - nano::lock_guard guard (block_cache_mutex); - std::transform (pending.block_callback_data.begin (), pending.block_callback_data.end (), std::back_inserter (cemented_blocks), [&block_cache = block_cache] (auto const & hash_a) { - debug_assert (block_cache.count (hash_a) == 1); - return block_cache.at (hash_a); - }); - } - pending_writes.erase (pending_writes.begin ()); - --pending_writes_size; - } - } - - auto time_spent_cementing = cemented_batch_timer.since_start ().count (); - - scoped_write_guard_a.release (); - notify_observers_callback (cemented_blocks); - release_assert (!error); - - debug_assert (pending_writes.empty ()); - debug_assert (pending_writes_size == 0); - timer.restart (); -} - -std::shared_ptr nano::confirmation_height_unbounded::get_block_and_sideband (nano::block_hash const & hash_a, store::transaction const & transaction_a) -{ - nano::lock_guard guard (block_cache_mutex); - auto block_cache_it = block_cache.find (hash_a); - if (block_cache_it != block_cache.cend ()) - { - return block_cache_it->second; - } - else - { - auto block = ledger.block (transaction_a, hash_a); - block_cache.emplace (hash_a, block); - return block; - } -} - -bool nano::confirmation_height_unbounded::pending_empty () const -{ - return pending_writes.empty (); -} - -void nano::confirmation_height_unbounded::clear_process_vars () -{ - // Separate blocks which are pending confirmation height can be batched by a minimum processing time (to improve lmdb disk write performance), - // so make sure the slate is clean when a new batch is starting. - confirmed_iterated_pairs.clear (); - confirmed_iterated_pairs_size = 0; - implicit_receive_cemented_mapping.clear (); - implicit_receive_cemented_mapping_size = 0; - { - nano::lock_guard guard (block_cache_mutex); - block_cache.clear (); - } -} - -bool nano::confirmation_height_unbounded::has_iterated_over_block (nano::block_hash const & hash_a) const -{ - nano::lock_guard guard (block_cache_mutex); - return block_cache.count (hash_a) == 1; -} - -uint64_t nano::confirmation_height_unbounded::block_cache_size () const -{ - nano::lock_guard guard (block_cache_mutex); - return block_cache.size (); -} - -nano::confirmation_height_unbounded::conf_height_details::conf_height_details (nano::account const & account_a, nano::block_hash const & hash_a, uint64_t height_a, uint64_t num_blocks_confirmed_a, std::vector const & block_callback_data_a) : - account (account_a), - hash (hash_a), - height (height_a), - num_blocks_confirmed (num_blocks_confirmed_a), - block_callback_data (block_callback_data_a) -{ -} - -nano::confirmation_height_unbounded::receive_source_pair::receive_source_pair (std::shared_ptr const & receive_details_a, const block_hash & source_a) : - receive_details (receive_details_a), - source_hash (source_a) -{ -} - -nano::confirmation_height_unbounded::confirmed_iterated_pair::confirmed_iterated_pair (uint64_t confirmed_height_a, uint64_t iterated_height_a) : - confirmed_height (confirmed_height_a), - iterated_height (iterated_height_a) -{ -} - -std::unique_ptr nano::collect_container_info (confirmation_height_unbounded & confirmation_height_unbounded, std::string const & name_a) -{ - auto composite = std::make_unique (name_a); - composite->add_component (std::make_unique (container_info{ "confirmed_iterated_pairs", confirmation_height_unbounded.confirmed_iterated_pairs_size, sizeof (decltype (confirmation_height_unbounded.confirmed_iterated_pairs)::value_type) })); - composite->add_component (std::make_unique (container_info{ "pending_writes", confirmation_height_unbounded.pending_writes_size, sizeof (decltype (confirmation_height_unbounded.pending_writes)::value_type) })); - composite->add_component (std::make_unique (container_info{ "implicit_receive_cemented_mapping", confirmation_height_unbounded.implicit_receive_cemented_mapping_size, sizeof (decltype (confirmation_height_unbounded.implicit_receive_cemented_mapping)::value_type) })); - composite->add_component (std::make_unique (container_info{ "block_cache", confirmation_height_unbounded.block_cache_size (), sizeof (decltype (confirmation_height_unbounded.block_cache)::value_type) })); - return composite; -} diff --git a/nano/node/confirmation_height_unbounded.hpp b/nano/node/confirmation_height_unbounded.hpp deleted file mode 100644 index ea3e7e52a..000000000 --- a/nano/node/confirmation_height_unbounded.hpp +++ /dev/null @@ -1,114 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace nano -{ -class ledger; -class write_database_queue; -class write_guard; - -class confirmation_height_unbounded final -{ -public: - confirmation_height_unbounded (nano::ledger &, nano::write_database_queue &, std::chrono::milliseconds batch_separate_pending_min_time, nano::logger &, std::atomic & stopped, uint64_t & batch_write_size, std::function> const &)> const & cemented_callback, std::function const & already_cemented_callback, std::function const & awaiting_processing_size_query); - - bool pending_empty () const; - void clear_process_vars (); - void process (std::shared_ptr original_block); - void cement_blocks (nano::write_guard &); - bool has_iterated_over_block (nano::block_hash const &) const; - -private: - class confirmed_iterated_pair - { - public: - confirmed_iterated_pair (uint64_t confirmed_height_a, uint64_t iterated_height_a); - uint64_t confirmed_height; - uint64_t iterated_height; - }; - - class conf_height_details final - { - public: - conf_height_details (nano::account const &, nano::block_hash const &, uint64_t, uint64_t, std::vector const &); - - nano::account account; - nano::block_hash hash; - uint64_t height; - uint64_t num_blocks_confirmed; - std::vector block_callback_data; - std::vector source_block_callback_data; - }; - - class receive_source_pair final - { - public: - receive_source_pair (std::shared_ptr const &, nano::block_hash const &); - - std::shared_ptr receive_details; - nano::block_hash source_hash; - }; - - // All of the atomic variables here just track the size for use in collect_container_info. - // This is so that no mutexes are needed during the algorithm itself, which would otherwise be needed - // for the sake of a rarely used RPC call for debugging purposes. As such the sizes are not being acted - // upon in any way (does not synchronize with any other data). - // This allows the load and stores to use relaxed atomic memory ordering. - std::unordered_map confirmed_iterated_pairs; - nano::relaxed_atomic_integral confirmed_iterated_pairs_size{ 0 }; - std::shared_ptr get_block_and_sideband (nano::block_hash const &, store::transaction const &); - std::deque pending_writes; - nano::relaxed_atomic_integral pending_writes_size{ 0 }; - std::unordered_map> implicit_receive_cemented_mapping; - nano::relaxed_atomic_integral implicit_receive_cemented_mapping_size{ 0 }; - - mutable nano::mutex block_cache_mutex; - std::unordered_map> block_cache; - uint64_t block_cache_size () const; - - nano::timer timer; - - class preparation_data final - { - public: - uint64_t block_height; - uint64_t confirmation_height; - uint64_t iterated_height; - decltype (confirmed_iterated_pairs.begin ()) account_it; - nano::account const & account; - std::shared_ptr receive_details; - bool already_traversed; - nano::block_hash const & current; - std::vector const & block_callback_data; - std::vector const & orig_block_callback_data; - }; - - void collect_unconfirmed_receive_and_sources_for_account (uint64_t, uint64_t, std::shared_ptr const &, nano::block_hash const &, nano::account const &, store::read_transaction const &, std::vector &, std::vector &, std::vector &, std::shared_ptr original_block); - void prepare_iterated_blocks_for_cementing (preparation_data &); - - nano::ledger & ledger; - nano::write_database_queue & write_database_queue; - std::chrono::milliseconds batch_separate_pending_min_time; - nano::logger & logger; - std::atomic & stopped; - uint64_t & batch_write_size; - - std::function> const &)> notify_observers_callback; - std::function notify_block_already_cemented_observers_callback; - std::function awaiting_processing_size_callback; - - friend class confirmation_height_dynamic_algorithm_no_transition_while_pending_Test; - friend std::unique_ptr collect_container_info (confirmation_height_unbounded &, std::string const & name_a); -}; - -std::unique_ptr collect_container_info (confirmation_height_unbounded &, std::string const & name_a); -} diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index fe217b759..ccec370e1 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -1206,7 +1206,7 @@ void nano::json_handler::block_confirm () if (!node.ledger.block_confirmed (transaction, hash)) { // Start new confirmation for unconfirmed (or not being confirmed) block - if (!node.confirmation_height_processor.exists (hash)) + if (!node.confirming_set.exists (hash)) { node.start_election (std::move (block_l)); } diff --git a/nano/node/node.cpp b/nano/node/node.cpp index a032e60bf..da3a2f0e0 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -466,7 +466,7 @@ nano::node::node (std::shared_ptr io_ctx_a, std::filesy std::exit (1); } } - confirmation_height_processor.cemented_observers.add ([this] (auto const & block) { + confirming_set.cemented_observers.add ([this] (auto const & block) { if (block->is_send ()) { auto transaction = store.tx_begin_read (); @@ -570,7 +570,7 @@ std::unique_ptr nano::collect_container_info (no 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 (node.confirmation_height_processor.collect_container_info ("confirmation_queue")); + composite->add_component (node.confirming_set.collect_container_info ("confirming_set")); composite->add_component (collect_container_info (node.distributed_work, "distributed_work")); composite->add_component (collect_container_info (node.aggregator, "request_aggregator")); composite->add_component (node.scheduler.collect_container_info ("election_scheduler")); @@ -681,7 +681,7 @@ void nano::node::start () active.start (); generator.start (); final_generator.start (); - confirmation_height_processor.start (); + confirming_set.start (); scheduler.start (); backlog.start (); bootstrap_server.start (); @@ -722,7 +722,7 @@ void nano::node::stop () active.stop (); generator.stop (); final_generator.stop (); - confirmation_height_processor.stop (); + confirming_set.stop (); telemetry.stop (); websocket.stop (); bootstrap_server.stop (); @@ -1188,7 +1188,7 @@ bool nano::node::block_confirmed (nano::block_hash const & hash_a) bool nano::node::block_confirmed_or_being_confirmed (nano::store::transaction const & transaction, nano::block_hash const & hash_a) { - return confirmation_height_processor.exists (hash_a) || ledger.block_confirmed (transaction, hash_a); + return confirming_set.exists (hash_a) || ledger.block_confirmed (transaction, hash_a); } bool nano::node::block_confirmed_or_being_confirmed (nano::block_hash const & hash_a) @@ -1260,7 +1260,7 @@ void nano::node::process_confirmed (nano::election_status const & status_a, uint { logger.trace (nano::log::type::node, nano::log::detail::process_confirmed, nano::log::arg{ "block", block_l }); - confirmation_height_processor.add (block_l->hash ()); + confirming_set.add (block_l->hash ()); } else if (iteration_a < num_iters) { diff --git a/nano/node/node.hpp b/nano/node/node.hpp index 8c1a0476b..53ba1f69a 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -168,8 +168,8 @@ public: nano::node_observers observers; nano::port_mapping port_mapping; nano::block_processor block_processor; - std::unique_ptr confirmation_height_processor_impl; - nano::confirming_set & confirmation_height_processor; + std::unique_ptr confirming_set_impl; + nano::confirming_set & confirming_set; std::unique_ptr active_impl; nano::active_transactions & active; nano::online_reps online_reps; diff --git a/nano/node/wallet.cpp b/nano/node/wallet.cpp index a322b0037..696d7b4b6 100644 --- a/nano/node/wallet.cpp +++ b/nano/node/wallet.cpp @@ -1198,7 +1198,7 @@ bool nano::wallet::search_receivable (store::transaction const & wallet_transact // Receive confirmed block receive_async (hash, representative, amount, account, [] (std::shared_ptr const &) {}); } - else if (!wallets.node.confirmation_height_processor.exists (hash)) + else if (!wallets.node.confirming_set.exists (hash)) { auto block = wallets.node.ledger.block (block_transaction, hash); if (block) diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 66c10fac8..284bea023 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -5820,7 +5820,7 @@ TEST (rpc, block_confirmed) ASSERT_TRUE (nano::test::start_elections (system, *node, { send }, true)); // Wait until the confirmation height has been set - ASSERT_TIMELY (5s, node->ledger.block_confirmed (node->store.tx_begin_read (), send->hash ()) && !node->confirmation_height_processor.exists (send->hash ())); + ASSERT_TIMELY (5s, node->ledger.block_confirmed (node->store.tx_begin_read (), send->hash ()) && !node->confirming_set.exists (send->hash ())); // Requesting confirmation for this should now succeed request.put ("hash", send->hash ().to_string ()); diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index bc6f50a2b..cca99a878 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -341,12 +341,5 @@ public: nano::bootstrap_constants bootstrap; }; -enum class confirmation_height_mode -{ - automatic, - unbounded, - bounded -}; - nano::wallet_id random_wallet_id (); } diff --git a/nano/slow_test/node.cpp b/nano/slow_test/node.cpp index 2c1ff3c9e..f9f3d57cd 100644 --- a/nano/slow_test/node.cpp +++ b/nano/slow_test/node.cpp @@ -975,10 +975,10 @@ TEST (confirmation_height, dynamic_algorithm) } } - node->confirmation_height_processor.add (state_blocks.front ()->hash ()); + node->confirming_set.add (state_blocks.front ()->hash ()); ASSERT_TIMELY_EQ (20s, node->ledger.cache.cemented_count, 2); - node->confirmation_height_processor.add (latest_genesis->hash ()); + node->confirming_set.add (latest_genesis->hash ()); ASSERT_TIMELY_EQ (20s, node->ledger.cache.cemented_count, num_blocks + 1); @@ -1144,7 +1144,7 @@ TEST (confirmation_height, many_accounts_send_receive_self_no_elections) nano::block_hash block_hash_being_processed{ 0 }; nano::write_database_queue write_queue{ false }; - nano::confirming_set confirmation_height_processor{ ledger, write_queue }; + nano::confirming_set confirming_set{ ledger, write_queue }; auto const num_accounts = 100000; @@ -1189,7 +1189,7 @@ TEST (confirmation_height, many_accounts_send_receive_self_no_elections) for (auto & open_block : open_blocks) { - confirmation_height_processor.add (open_block->hash ()); + confirming_set.add (open_block->hash ()); } system.deadline_set (1000s); @@ -1240,8 +1240,8 @@ TEST (confirmation_height, many_accounts_send_receive_self_no_elections) // Now send and receive to self for (int i = 0; i < open_blocks.size (); ++i) { - confirmation_height_processor.add (send_blocks[i]->hash ()); - confirmation_height_processor.add (receive_blocks[i]->hash ()); + confirming_set.add (send_blocks[i]->hash ()); + confirming_set.add (receive_blocks[i]->hash ()); } system.deadline_set (1000s); @@ -1251,7 +1251,7 @@ TEST (confirmation_height, many_accounts_send_receive_self_no_elections) ASSERT_NO_ERROR (system.poll ()); } - while (confirmation_height_processor.size () > 0) + while (confirming_set.size () > 0) { ASSERT_NO_ERROR (system.poll ()); } @@ -2078,7 +2078,7 @@ TEST (node, wallet_create_block_confirm_conflicts) election->force_confirm (); } - ASSERT_TIMELY (120s, node->ledger.block_confirmed (node->store.tx_begin_read (), latest) && node->confirmation_height_processor.size () == 0); + ASSERT_TIMELY (120s, node->ledger.block_confirmed (node->store.tx_begin_read (), latest) && node->confirming_set.size () == 0); done = true; t.join (); } From 9b0401d3d443ae1f4681520a6e619b9f0dd7c853 Mon Sep 17 00:00:00 2001 From: clemahieu Date: Mon, 25 Mar 2024 10:36:04 +0000 Subject: [PATCH 108/128] Improve write_database_queue::wait description and add [[nodiscard]] (#4522) --- nano/node/write_database_queue.hpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nano/node/write_database_queue.hpp b/nano/node/write_database_queue.hpp index 4443eca6c..d6c6883ea 100644 --- a/nano/node/write_database_queue.hpp +++ b/nano/node/write_database_queue.hpp @@ -34,12 +34,16 @@ private: bool owns{ true }; }; +/** + * Allocates database write access in a fair maner rather than directly waiting for mutex aquisition + * Users should wait() for access to database write transaction and hold the write_guard until complete + */ class write_database_queue final { public: write_database_queue (bool use_noops_a); - /** Blocks until we are at the head of the queue */ - write_guard wait (nano::writer writer); + /** Blocks until we are at the head of the queue and blocks other waiters until write_guard goes out of scope */ + [[nodiscard ("write_guard blocks other waiters")]] write_guard wait (nano::writer writer); /** Returns true if this writer is now at the front of the queue */ bool process (nano::writer writer); From 43dae2f543d1f98be3d1301c53fc835a2ad00e0a Mon Sep 17 00:00:00 2001 From: clemahieu Date: Mon, 25 Mar 2024 15:42:16 +0000 Subject: [PATCH 109/128] Remove unused frontiers table (#4425) --- nano/core_test/block_store.cpp | 36 +++++++++++++++++++++ nano/core_test/ledger.cpp | 1 - nano/node/blockprocessor.cpp | 2 +- nano/node/node.cpp | 4 +-- nano/secure/ledger.cpp | 36 --------------------- nano/store/CMakeLists.txt | 6 ---- nano/store/component.cpp | 5 +-- nano/store/component.hpp | 5 +-- nano/store/frontier.cpp | 1 - nano/store/frontier.hpp | 29 ----------------- nano/store/lmdb/frontier.cpp | 57 --------------------------------- nano/store/lmdb/frontier.hpp | 34 -------------------- nano/store/lmdb/lmdb.cpp | 19 ++++++++--- nano/store/lmdb/lmdb.hpp | 4 +-- nano/store/rocksdb/frontier.cpp | 57 --------------------------------- nano/store/rocksdb/frontier.hpp | 27 ---------------- nano/store/rocksdb/rocksdb.cpp | 35 ++++++++++++++++---- nano/store/rocksdb/rocksdb.hpp | 4 +-- nano/store/tables.hpp | 1 - nano/test_common/testutil.cpp | 2 +- 20 files changed, 87 insertions(+), 278 deletions(-) delete mode 100644 nano/store/frontier.cpp delete mode 100644 nano/store/frontier.hpp delete mode 100644 nano/store/lmdb/frontier.cpp delete mode 100644 nano/store/lmdb/frontier.hpp delete mode 100644 nano/store/rocksdb/frontier.cpp delete mode 100644 nano/store/rocksdb/frontier.hpp diff --git a/nano/core_test/block_store.cpp b/nano/core_test/block_store.cpp index 385829766..42106adcb 100644 --- a/nano/core_test/block_store.cpp +++ b/nano/core_test/block_store.cpp @@ -1397,6 +1397,42 @@ TEST (mdb_block_store, upgrade_v21_v22) // Testing the upgrade code worked check_correct_state (); } + +TEST (mdb_block_store, upgrade_v23_v24) +{ + if (nano::rocksdb_config::using_rocksdb_in_tests ()) + { + // Direct lmdb operations are used to simulate the old ledger format so this test will not work on RocksDB + GTEST_SKIP (); + } + + auto path (nano::unique_path () / "data.ldb"); + nano::logger logger; + nano::stats stats; + auto const check_correct_state = [&] () { + nano::store::lmdb::component store (logger, path, nano::dev::constants); + auto transaction (store.tx_begin_write ()); + ASSERT_EQ (store.version.get (transaction), store.version_current); + MDB_dbi frontiers_handle{ 0 }; + ASSERT_EQ (MDB_NOTFOUND, mdb_dbi_open (store.env.tx (transaction), "frontiers", 0, &frontiers_handle)); + }; + + // Testing current version doesn't contain the frontiers table + check_correct_state (); + + // Setting the database to its 23st version state + { + nano::store::lmdb::component store (logger, path, nano::dev::constants); + auto transaction (store.tx_begin_write ()); + store.version.put (transaction, 23); + MDB_dbi frontiers_handle{ 0 }; + ASSERT_FALSE (mdb_dbi_open (store.env.tx (transaction), "frontiers", MDB_CREATE, &frontiers_handle)); + ASSERT_EQ (store.version.get (transaction), 23); + } + + // Testing the upgrade code worked + check_correct_state (); +} } namespace nano::store::rocksdb diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 8d8e0c0e4..af04a99be 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -5502,7 +5502,6 @@ TEST (ledger, migrate_lmdb_to_rocksdb) store.confirmation_height.put (transaction, nano::dev::genesis_key.pub, { 2, send->hash () }); store.online_weight.put (transaction, 100, nano::amount (2)); - store.frontier.put (transaction, nano::block_hash (2), nano::account (5)); store.peer.put (transaction, endpoint_key); store.pending.put (transaction, nano::pending_key (nano::dev::genesis_key.pub, send->hash ()), nano::pending_info (nano::dev::genesis_key.pub, 100, nano::epoch::epoch_0)); diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index 3a7471d8d..5e1e4b8cd 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -284,7 +284,7 @@ auto nano::block_processor::process_batch (nano::unique_lock & lock processed_batch_t processed; auto scoped_write_guard = write_database_queue.wait (nano::writer::process_batch); - auto transaction (node.store.tx_begin_write ({ tables::accounts, tables::blocks, tables::frontiers, tables::pending, tables::rep_weights })); + auto transaction (node.store.tx_begin_write ({ tables::accounts, tables::blocks, tables::pending, tables::rep_weights })); nano::timer timer_l; lock_a.lock (); diff --git a/nano/node/node.cpp b/nano/node/node.cpp index da3a2f0e0..c21f9818d 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -385,7 +385,7 @@ nano::node::node (std::shared_ptr io_ctx_a, std::filesy if (!is_initialized && !flags.read_only) { - auto const transaction (store.tx_begin_write ({ tables::accounts, tables::blocks, tables::confirmation_height, tables::frontiers, tables::rep_weights })); + auto const transaction (store.tx_begin_write ({ tables::accounts, tables::blocks, tables::confirmation_height, tables::rep_weights })); // Store was empty meaning we just created it, add the genesis block store.initialize (transaction, ledger.cache, ledger.constants); } @@ -596,7 +596,7 @@ void nano::node::process_active (std::shared_ptr const & incoming) nano::block_status nano::node::process (std::shared_ptr block) { - auto const transaction = store.tx_begin_write ({ tables::accounts, tables::blocks, tables::frontiers, tables::pending, tables::rep_weights }); + auto const transaction = store.tx_begin_write ({ tables::accounts, tables::blocks, tables::pending, tables::rep_weights }); return process (transaction, block); } diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index f245400ee..1f2f373a0 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -59,8 +58,6 @@ public: nano::account_info new_info (block_a.hashables.previous, info->representative, info->open_block, ledger.balance (transaction, block_a.hashables.previous).value (), nano::seconds_since_epoch (), info->block_count - 1, nano::epoch::epoch_0); ledger.update_account (transaction, pending.value ().source, *info, new_info); ledger.store.block.del (transaction, hash); - ledger.store.frontier.del (transaction, hash); - ledger.store.frontier.put (transaction, block_a.hashables.previous, pending.value ().source); ledger.store.block.successor_clear (transaction, block_a.hashables.previous); ledger.stats.inc (nano::stat::type::rollback, nano::stat::detail::send); } @@ -79,8 +76,6 @@ public: ledger.update_account (transaction, destination_account, *info, new_info); ledger.store.block.del (transaction, hash); ledger.store.pending.put (transaction, nano::pending_key (destination_account, block_a.hashables.source), { source_account.value_or (0), amount, nano::epoch::epoch_0 }); - ledger.store.frontier.del (transaction, hash); - ledger.store.frontier.put (transaction, block_a.hashables.previous, destination_account); ledger.store.block.successor_clear (transaction, block_a.hashables.previous); ledger.stats.inc (nano::stat::type::rollback, nano::stat::detail::receive); } @@ -95,7 +90,6 @@ public: ledger.update_account (transaction, destination_account, new_info, new_info); ledger.store.block.del (transaction, hash); ledger.store.pending.put (transaction, nano::pending_key (destination_account, block_a.hashables.source), { source_account.value_or (0), amount, nano::epoch::epoch_0 }); - ledger.store.frontier.del (transaction, hash); ledger.stats.inc (nano::stat::type::rollback, nano::stat::detail::open); } void change_block (nano::change_block const & block_a) override @@ -113,8 +107,6 @@ public: ledger.store.block.del (transaction, hash); nano::account_info new_info (block_a.hashables.previous, representative, info->open_block, info->balance, nano::seconds_since_epoch (), info->block_count - 1, nano::epoch::epoch_0); ledger.update_account (transaction, account, *info, new_info); - ledger.store.frontier.del (transaction, hash); - ledger.store.frontier.put (transaction, block_a.hashables.previous, account); ledger.store.block.successor_clear (transaction, block_a.hashables.previous); ledger.stats.inc (nano::stat::type::rollback, nano::stat::detail::change); } @@ -174,10 +166,6 @@ public: if (previous != nullptr) { ledger.store.block.successor_clear (transaction, block_a.hashables.previous); - if (previous->type () < nano::block_type::state) - { - ledger.store.frontier.put (transaction, block_a.hashables.previous, block_a.hashables.account); - } } else { @@ -371,10 +359,6 @@ void ledger_processor::state_block_impl (nano::state_block & block_a) nano::account_info new_info (hash, block_a.hashables.representative, info.open_block.is_zero () ? hash : info.open_block, block_a.hashables.balance, nano::seconds_since_epoch (), info.block_count + 1, epoch); ledger.update_account (transaction, block_a.hashables.account, info, new_info); - if (!ledger.store.frontier.get (transaction, info.head).is_zero ()) - { - ledger.store.frontier.del (transaction, info.head); - } } } } @@ -441,10 +425,6 @@ void ledger_processor::epoch_block_impl (nano::state_block & block_a) ledger.store.block.put (transaction, hash, block_a); nano::account_info new_info (hash, block_a.hashables.representative, info.open_block.is_zero () ? hash : info.open_block, info.balance, nano::seconds_since_epoch (), info.block_count + 1, epoch); ledger.update_account (transaction, block_a.hashables.account, info, new_info); - if (!ledger.store.frontier.get (transaction, info.head).is_zero ()) - { - ledger.store.frontier.del (transaction, info.head); - } } } } @@ -489,8 +469,6 @@ void ledger_processor::change_block (nano::change_block & block_a) ledger.cache.rep_weights.representation_add_dual (transaction, block_a.hashables.representative, balance.number (), info->representative, 0 - balance.number ()); nano::account_info new_info (hash, block_a.hashables.representative, info->open_block, info->balance, nano::seconds_since_epoch (), info->block_count + 1, nano::epoch::epoch_0); ledger.update_account (transaction, account, *info, new_info); - ledger.store.frontier.del (transaction, block_a.hashables.previous); - ledger.store.frontier.put (transaction, hash, account); ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::change); } } @@ -539,8 +517,6 @@ void ledger_processor::send_block (nano::send_block & block_a) nano::account_info new_info (hash, info->representative, info->open_block, block_a.hashables.balance, nano::seconds_since_epoch (), info->block_count + 1, nano::epoch::epoch_0); ledger.update_account (transaction, account, *info, new_info); ledger.store.pending.put (transaction, nano::pending_key (block_a.hashables.destination, hash), { account, amount, nano::epoch::epoch_0 }); - ledger.store.frontier.del (transaction, block_a.hashables.previous); - ledger.store.frontier.put (transaction, hash, account); ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::send); } } @@ -607,8 +583,6 @@ void ledger_processor::receive_block (nano::receive_block & block_a) nano::account_info new_info (hash, info->representative, info->open_block, new_balance, nano::seconds_since_epoch (), info->block_count + 1, nano::epoch::epoch_0); ledger.update_account (transaction, account, *info, new_info); ledger.cache.rep_weights.representation_add (transaction, info->representative, pending.value ().amount.number ()); - ledger.store.frontier.del (transaction, block_a.hashables.previous); - ledger.store.frontier.put (transaction, hash, account); ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::receive); } } @@ -669,7 +643,6 @@ void ledger_processor::open_block (nano::open_block & block_a) nano::account_info new_info (hash, block_a.representative_field ().value (), hash, pending.value ().amount.number (), nano::seconds_since_epoch (), 1, nano::epoch::epoch_0); ledger.update_account (transaction, block_a.hashables.account, info, new_info); ledger.cache.rep_weights.representation_add (transaction, block_a.representative_field ().value (), pending.value ().amount.number ()); - ledger.store.frontier.put (transaction, hash, block_a.hashables.account); ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::open); } } @@ -1498,15 +1471,6 @@ bool nano::ledger::migrate_lmdb_to_rocksdb (std::filesystem::path const & data_p } }); - store.frontier.for_each_par ( - [&rocksdb_store] (store::read_transaction const & /*unused*/, auto i, auto n) { - for (; i != n; ++i) - { - auto rocksdb_transaction (rocksdb_store->tx_begin_write ({}, { nano::tables::frontiers })); - rocksdb_store->frontier.put (rocksdb_transaction, i->first, i->second); - } - }); - store.pruned.for_each_par ( [&rocksdb_store] (store::read_transaction const & /*unused*/, auto i, auto n) { for (; i != n; ++i) diff --git a/nano/store/CMakeLists.txt b/nano/store/CMakeLists.txt index 9ea6c5c73..794f6c053 100644 --- a/nano/store/CMakeLists.txt +++ b/nano/store/CMakeLists.txt @@ -9,13 +9,11 @@ add_library( iterator.hpp iterator_impl.hpp final.hpp - frontier.hpp lmdb/account.hpp lmdb/block.hpp lmdb/confirmation_height.hpp lmdb/db_val.hpp lmdb/final_vote.hpp - lmdb/frontier.hpp lmdb/iterator.hpp lmdb/lmdb.hpp lmdb/lmdb_env.hpp @@ -38,7 +36,6 @@ add_library( rocksdb/confirmation_height.hpp rocksdb/db_val.hpp rocksdb/final_vote.hpp - rocksdb/frontier.hpp rocksdb/iterator.hpp rocksdb/online_weight.hpp rocksdb/peer.hpp @@ -61,13 +58,11 @@ add_library( iterator.cpp iterator_impl.cpp final.cpp - frontier.cpp lmdb/account.cpp lmdb/block.cpp lmdb/confirmation_height.cpp lmdb/db_val.cpp lmdb/final_vote.cpp - lmdb/frontier.cpp lmdb/lmdb.cpp lmdb/lmdb_env.cpp lmdb/transaction.cpp @@ -87,7 +82,6 @@ add_library( rocksdb/confirmation_height.cpp rocksdb/db_val.cpp rocksdb/final_vote.cpp - rocksdb/frontier.cpp rocksdb/online_weight.cpp rocksdb/peer.cpp rocksdb/pending.cpp diff --git a/nano/store/component.cpp b/nano/store/component.cpp index 28fc4933c..5d8c8bec2 100644 --- a/nano/store/component.cpp +++ b/nano/store/component.cpp @@ -5,12 +5,10 @@ #include #include #include -#include #include -nano::store::component::component (nano::store::block & block_store_a, nano::store::frontier & frontier_store_a, nano::store::account & account_store_a, nano::store::pending & pending_store_a, nano::store::online_weight & online_weight_store_a, nano::store::pruned & pruned_store_a, nano::store::peer & peer_store_a, nano::store::confirmation_height & confirmation_height_store_a, nano::store::final_vote & final_vote_store_a, nano::store::version & version_store_a, nano::store::rep_weight & rep_weight_a) : +nano::store::component::component (nano::store::block & block_store_a, nano::store::account & account_store_a, nano::store::pending & pending_store_a, nano::store::online_weight & online_weight_store_a, nano::store::pruned & pruned_store_a, nano::store::peer & peer_store_a, nano::store::confirmation_height & confirmation_height_store_a, nano::store::final_vote & final_vote_store_a, nano::store::version & version_store_a, nano::store::rep_weight & rep_weight_a) : block (block_store_a), - frontier (frontier_store_a), account (account_store_a), pending (pending_store_a), online_weight (online_weight_store_a), @@ -40,5 +38,4 @@ void nano::store::component::initialize (store::write_transaction const & transa ++ledger_cache_a.account_count; rep_weight.put (transaction_a, constants.genesis->account (), std::numeric_limits::max ()); ledger_cache_a.rep_weights.representation_put (constants.genesis->account (), std::numeric_limits::max ()); - frontier.put (transaction_a, hash_l, constants.genesis->account ()); } diff --git a/nano/store/component.hpp b/nano/store/component.hpp index ac5103e92..9ba94029a 100644 --- a/nano/store/component.hpp +++ b/nano/store/component.hpp @@ -21,7 +21,6 @@ namespace store class block; class confirmation_height; class final_vote; - class frontier; class online_weight; class peer; class pending; @@ -45,7 +44,6 @@ namespace store // clang-format off explicit component ( nano::store::block &, - nano::store::frontier &, nano::store::account &, nano::store::pending &, nano::store::online_weight&, @@ -67,12 +65,11 @@ namespace store virtual std::string error_string (int status) const = 0; store::block & block; - store::frontier & frontier; store::account & account; store::pending & pending; store::rep_weight & rep_weight; static int constexpr version_minimum{ 21 }; - static int constexpr version_current{ 23 }; + static int constexpr version_current{ 24 }; public: store::online_weight & online_weight; diff --git a/nano/store/frontier.cpp b/nano/store/frontier.cpp deleted file mode 100644 index 2d12551d5..000000000 --- a/nano/store/frontier.cpp +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/nano/store/frontier.hpp b/nano/store/frontier.hpp deleted file mode 100644 index b4d504d41..000000000 --- a/nano/store/frontier.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -namespace nano -{ -class block_hash; -} -namespace nano::store -{ -/** - * Manages frontier storage and iteration - */ -class frontier -{ -public: - virtual void put (store::write_transaction const &, nano::block_hash const &, nano::account const &) = 0; - virtual nano::account get (store::transaction const &, nano::block_hash const &) const = 0; - virtual void del (store::write_transaction const &, nano::block_hash const &) = 0; - virtual iterator begin (store::transaction const &) const = 0; - virtual iterator begin (store::transaction const &, nano::block_hash const &) const = 0; - virtual iterator end () const = 0; - virtual void for_each_par (std::function, store::iterator)> const & action_a) const = 0; -}; -} // namespace nano::store diff --git a/nano/store/lmdb/frontier.cpp b/nano/store/lmdb/frontier.cpp deleted file mode 100644 index 8b694a3ef..000000000 --- a/nano/store/lmdb/frontier.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include -#include -#include - -nano::store::lmdb::frontier::frontier (nano::store::lmdb::component & store) : - store{ store } -{ -} - -void nano::store::lmdb::frontier::put (store::write_transaction const & transaction, nano::block_hash const & hash, nano::account const & account) -{ - auto status = store.put (transaction, tables::frontiers, hash, account); - store.release_assert_success (status); -} - -nano::account nano::store::lmdb::frontier::get (store::transaction const & transaction, nano::block_hash const & hash) const -{ - store::db_val value; - auto status = store.get (transaction, tables::frontiers, hash, value); - release_assert (store.success (status) || store.not_found (status)); - nano::account result{}; - if (store.success (status)) - { - result = static_cast (value); - } - return result; -} - -void nano::store::lmdb::frontier::del (store::write_transaction const & transaction, nano::block_hash const & hash) -{ - auto status = store.del (transaction, tables::frontiers, hash); - store.release_assert_success (status); -} - -nano::store::iterator nano::store::lmdb::frontier::begin (store::transaction const & transaction) const -{ - return store.make_iterator (transaction, tables::frontiers); -} - -nano::store::iterator nano::store::lmdb::frontier::begin (store::transaction const & transaction, nano::block_hash const & hash) const -{ - return store.make_iterator (transaction, tables::frontiers, store::db_val (hash)); -} - -nano::store::iterator nano::store::lmdb::frontier::end () const -{ - return store::iterator (nullptr); -} - -void nano::store::lmdb::frontier::for_each_par (std::function, store::iterator)> const & action_a) const -{ - parallel_traversal ( - [&action_a, this] (nano::uint256_t const & start, nano::uint256_t const & end, bool const is_last) { - auto transaction (this->store.tx_begin_read ()); - action_a (transaction, this->begin (transaction, start), !is_last ? this->begin (transaction, end) : this->end ()); - }); -} diff --git a/nano/store/lmdb/frontier.hpp b/nano/store/lmdb/frontier.hpp deleted file mode 100644 index 95205d4cd..000000000 --- a/nano/store/lmdb/frontier.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include - -#include - -namespace nano::store::lmdb -{ -class component; -} -namespace nano::store::lmdb -{ -class frontier : public nano::store::frontier -{ -private: - nano::store::lmdb::component & store; - -public: - frontier (nano::store::lmdb::component & store); - void put (store::write_transaction const &, nano::block_hash const &, nano::account const &) override; - nano::account get (store::transaction const &, nano::block_hash const &) const override; - void del (store::write_transaction const &, nano::block_hash const &) override; - store::iterator begin (store::transaction const &) const override; - store::iterator begin (store::transaction const &, nano::block_hash const &) const override; - store::iterator end () const override; - void for_each_par (std::function, store::iterator)> const & action_a) const override; - - /** - * Maps head block to owning account - * nano::block_hash -> nano::account - */ - MDB_dbi frontiers_handle{ 0 }; -}; -} // namespace nano::store::lmdb diff --git a/nano/store/lmdb/lmdb.cpp b/nano/store/lmdb/lmdb.cpp index 179c14b40..122bf55a9 100644 --- a/nano/store/lmdb/lmdb.cpp +++ b/nano/store/lmdb/lmdb.cpp @@ -18,7 +18,6 @@ nano::store::lmdb::component::component (nano::logger & logger_a, std::filesyste // clang-format off nano::store::component{ block_store, - frontier_store, account_store, pending_store, online_weight_store, @@ -31,7 +30,6 @@ nano::store::lmdb::component::component (nano::logger & logger_a, std::filesyste }, // clang-format on block_store{ *this }, - frontier_store{ *this }, account_store{ *this }, pending_store{ *this }, online_weight_store{ *this }, @@ -196,7 +194,6 @@ nano::store::lmdb::txn_callbacks nano::store::lmdb::component::create_txn_callba void nano::store::lmdb::component::open_databases (bool & error_a, store::transaction const & transaction_a, unsigned flags) { - error_a |= mdb_dbi_open (env.tx (transaction_a), "frontiers", flags, &frontier_store.frontiers_handle) != 0; error_a |= mdb_dbi_open (env.tx (transaction_a), "online_weight", flags, &online_weight_store.online_weight_handle) != 0; error_a |= mdb_dbi_open (env.tx (transaction_a), "meta", flags, &version_store.meta_handle) != 0; error_a |= mdb_dbi_open (env.tx (transaction_a), "peers", flags, &peer_store.peers_handle) != 0; @@ -229,6 +226,9 @@ bool nano::store::lmdb::component::do_upgrades (store::write_transaction & trans upgrade_v22_to_v23 (transaction_a); [[fallthrough]]; case 23: + upgrade_v23_to_v24 (transaction_a); + [[fallthrough]]; + case 24: break; default: logger.critical (nano::log::type::lmdb, "The version of the ledger ({}) is too high for this node", version_l); @@ -283,6 +283,17 @@ void nano::store::lmdb::component::upgrade_v22_to_v23 (store::write_transaction logger.info (nano::log::type::lmdb, "Upgrading database from v22 to v23 completed"); } +void nano::store::lmdb::component::upgrade_v23_to_v24 (store::write_transaction const & transaction_a) +{ + logger.info (nano::log::type::lmdb, "Upgrading database from v23 to v24..."); + + MDB_dbi frontiers_handle{ 0 }; + release_assert (!mdb_dbi_open (env.tx (transaction_a), "frontiers", MDB_CREATE, &frontiers_handle)); + release_assert (!mdb_drop (env.tx (transaction_a), frontiers_handle, 1)); // del = 1, to delete it from the environment and close the DB handle. + version.put (transaction_a, 24); + logger.info (nano::log::type::lmdb, "Upgrading database from v23 to v24 completed"); +} + /** Takes a filepath, appends '_backup_' to the end (but before any extension) and saves that file in the same directory */ void nano::store::lmdb::component::create_backup_file (nano::store::lmdb::env & env_a, std::filesystem::path const & filepath_a, nano::logger & logger) { @@ -360,8 +371,6 @@ MDB_dbi nano::store::lmdb::component::table_to_dbi (tables table_a) const { switch (table_a) { - case tables::frontiers: - return frontier_store.frontiers_handle; case tables::accounts: return account_store.accounts_handle; case tables::blocks: diff --git a/nano/store/lmdb/lmdb.hpp b/nano/store/lmdb/lmdb.hpp index c76381886..8cf9d94bc 100644 --- a/nano/store/lmdb/lmdb.hpp +++ b/nano/store/lmdb/lmdb.hpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -45,7 +44,6 @@ private: nano::store::lmdb::block block_store; nano::store::lmdb::confirmation_height confirmation_height_store; nano::store::lmdb::final_vote final_vote_store; - nano::store::lmdb::frontier frontier_store; nano::store::lmdb::online_weight online_weight_store; nano::store::lmdb::peer peer_store; nano::store::lmdb::pending pending_store; @@ -57,7 +55,6 @@ private: friend class nano::store::lmdb::block; friend class nano::store::lmdb::confirmation_height; friend class nano::store::lmdb::final_vote; - friend class nano::store::lmdb::frontier; friend class nano::store::lmdb::online_weight; friend class nano::store::lmdb::peer; friend class nano::store::lmdb::pending; @@ -117,6 +114,7 @@ private: bool do_upgrades (store::write_transaction &, nano::ledger_constants & constants, bool &); void upgrade_v21_to_v22 (store::write_transaction const &); void upgrade_v22_to_v23 (store::write_transaction const &); + void upgrade_v23_to_v24 (store::write_transaction const &); void open_databases (bool &, store::transaction const &, unsigned); diff --git a/nano/store/rocksdb/frontier.cpp b/nano/store/rocksdb/frontier.cpp deleted file mode 100644 index 6724b9336..000000000 --- a/nano/store/rocksdb/frontier.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include -#include -#include - -nano::store::rocksdb::frontier::frontier (nano::store::rocksdb::component & store) : - store{ store } -{ -} - -void nano::store::rocksdb::frontier::put (store::write_transaction const & transaction, nano::block_hash const & block, nano::account const & account) -{ - auto status = store.put (transaction, tables::frontiers, block, account); - store.release_assert_success (status); -} - -nano::account nano::store::rocksdb::frontier::get (store::transaction const & transaction, nano::block_hash const & hash) const -{ - db_val value; - auto status = store.get (transaction, tables::frontiers, hash, value); - release_assert (store.success (status) || store.not_found (status)); - nano::account result{}; - if (store.success (status)) - { - result = static_cast (value); - } - return result; -} - -void nano::store::rocksdb::frontier::del (store::write_transaction const & transaction, nano::block_hash const & hash) -{ - auto status = store.del (transaction, tables::frontiers, hash); - store.release_assert_success (status); -} - -nano::store::iterator nano::store::rocksdb::frontier::begin (store::transaction const & transaction) const -{ - return store.make_iterator (transaction, tables::frontiers); -} - -nano::store::iterator nano::store::rocksdb::frontier::begin (store::transaction const & transaction, nano::block_hash const & hash) const -{ - return store.make_iterator (transaction, tables::frontiers, hash); -} - -nano::store::iterator nano::store::rocksdb::frontier::end () const -{ - return nano::store::iterator (nullptr); -} - -void nano::store::rocksdb::frontier::for_each_par (std::function, nano::store::iterator)> const & action_a) const -{ - parallel_traversal ( - [&action_a, this] (nano::uint256_t const & start, nano::uint256_t const & end, bool const is_last) { - auto transaction (this->store.tx_begin_read ()); - action_a (transaction, this->begin (transaction, start), !is_last ? this->begin (transaction, end) : this->end ()); - }); -} diff --git a/nano/store/rocksdb/frontier.hpp b/nano/store/rocksdb/frontier.hpp deleted file mode 100644 index edc0e15b4..000000000 --- a/nano/store/rocksdb/frontier.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include - -namespace nano::store::rocksdb -{ -class component; -} -namespace nano::store::rocksdb -{ -class frontier : public nano::store::frontier -{ -public: - frontier (nano::store::rocksdb::component & store); - void put (store::write_transaction const &, nano::block_hash const &, nano::account const &) override; - nano::account get (store::transaction const &, nano::block_hash const &) const override; - void del (store::write_transaction const &, nano::block_hash const &) override; - store::iterator begin (store::transaction const &) const override; - store::iterator begin (store::transaction const &, nano::block_hash const &) const override; - store::iterator end () const override; - void for_each_par (std::function, store::iterator)> const & action_a) const override; - -private: - nano::store::rocksdb::component & store; -}; -} // namespace nano::store::rocksdb diff --git a/nano/store/rocksdb/rocksdb.cpp b/nano/store/rocksdb/rocksdb.cpp index f13cdb76b..d9fb4e316 100644 --- a/nano/store/rocksdb/rocksdb.cpp +++ b/nano/store/rocksdb/rocksdb.cpp @@ -39,7 +39,6 @@ nano::store::rocksdb::component::component (nano::logger & logger_a, std::filesy // clang-format off nano::store::component{ block_store, - frontier_store, account_store, pending_store, online_weight_store, @@ -52,7 +51,6 @@ nano::store::rocksdb::component::component (nano::logger & logger_a, std::filesy }, // clang-format on block_store{ *this }, - frontier_store{ *this }, account_store{ *this }, pending_store{ *this }, online_weight_store{ *this }, @@ -164,7 +162,6 @@ nano::store::rocksdb::component::component (nano::logger & logger_a, std::filesy std::unordered_map nano::store::rocksdb::component::create_cf_name_table_map () const { std::unordered_map map{ { ::rocksdb::kDefaultColumnFamilyName.c_str (), tables::default_unused }, - { "frontiers", tables::frontiers }, { "accounts", tables::accounts }, { "blocks", tables::blocks }, { "pending", tables::pending }, @@ -248,6 +245,9 @@ bool nano::store::rocksdb::component::do_upgrades (store::write_transaction cons upgrade_v22_to_v23 (transaction_a); [[fallthrough]]; case 23: + upgrade_v23_to_v24 (transaction_a); + [[fallthrough]]; + case 24: break; default: logger.critical (nano::log::type::rocksdb, "The version of the ledger ({}) is too high for this node", version_l); @@ -316,6 +316,31 @@ void nano::store::rocksdb::component::upgrade_v22_to_v23 (store::write_transacti logger.info (nano::log::type::rocksdb, "Upgrading database from v22 to v23 completed"); } +void nano::store::rocksdb::component::upgrade_v23_to_v24 (store::write_transaction const & transaction_a) +{ + logger.info (nano::log::type::rocksdb, "Upgrading database from v23 to v24..."); + + if (column_family_exists ("frontiers")) + { + auto const unchecked_handle = get_column_family ("frontiers"); + db->DropColumnFamily (unchecked_handle); + db->DestroyColumnFamilyHandle (unchecked_handle); + std::erase_if (handles, [unchecked_handle] (auto & handle) { + if (handle.get () == unchecked_handle) + { + // The handle resource is deleted by RocksDB. + [[maybe_unused]] auto ptr = handle.release (); + return true; + } + return false; + }); + logger.debug (nano::log::type::rocksdb, "Finished removing frontiers table"); + } + + version.put (transaction_a, 24); + logger.info (nano::log::type::rocksdb, "Upgrading database from v23 to v24 completed"); +} + void nano::store::rocksdb::component::generate_tombstone_map () { tombstone_map.emplace (std::piecewise_construct, std::forward_as_tuple (nano::tables::blocks), std::forward_as_tuple (0, 25000)); @@ -531,8 +556,6 @@ rocksdb::ColumnFamilyHandle * nano::store::rocksdb::component::table_to_column_f { switch (table_a) { - case tables::frontiers: - return get_column_family ("frontiers"); case tables::accounts: return get_column_family ("accounts"); case tables::blocks: @@ -904,7 +927,7 @@ void nano::store::rocksdb::component::on_flush (::rocksdb::FlushJobInfo const & std::vector nano::store::rocksdb::component::all_tables () const { - return std::vector{ tables::accounts, tables::blocks, tables::confirmation_height, tables::final_votes, tables::frontiers, tables::meta, tables::online_weight, tables::peers, tables::pending, tables::pruned, tables::vote, tables::rep_weights }; + return std::vector{ tables::accounts, tables::blocks, tables::confirmation_height, tables::final_votes, tables::meta, tables::online_weight, tables::peers, tables::pending, tables::pruned, tables::vote, tables::rep_weights }; } bool nano::store::rocksdb::component::copy_db (std::filesystem::path const & destination_path) diff --git a/nano/store/rocksdb/rocksdb.hpp b/nano/store/rocksdb/rocksdb.hpp index f6c2d683b..8a71cf2b1 100644 --- a/nano/store/rocksdb/rocksdb.hpp +++ b/nano/store/rocksdb/rocksdb.hpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -46,7 +45,6 @@ private: nano::store::rocksdb::block block_store; nano::store::rocksdb::confirmation_height confirmation_height_store; nano::store::rocksdb::final_vote final_vote_store; - nano::store::rocksdb::frontier frontier_store; nano::store::rocksdb::online_weight online_weight_store; nano::store::rocksdb::peer peer_store; nano::store::rocksdb::pending pending_store; @@ -59,7 +57,6 @@ public: friend class nano::store::rocksdb::block; friend class nano::store::rocksdb::confirmation_height; friend class nano::store::rocksdb::final_vote; - friend class nano::store::rocksdb::frontier; friend class nano::store::rocksdb::online_weight; friend class nano::store::rocksdb::peer; friend class nano::store::rocksdb::pending; @@ -155,6 +152,7 @@ private: bool do_upgrades (store::write_transaction const &); void upgrade_v21_to_v22 (store::write_transaction const &); void upgrade_v22_to_v23 (store::write_transaction const &); + void upgrade_v23_to_v24 (store::write_transaction const &); void construct_column_family_mutexes (); ::rocksdb::Options get_db_options (); diff --git a/nano/store/tables.hpp b/nano/store/tables.hpp index cf53a0f91..13b073b76 100644 --- a/nano/store/tables.hpp +++ b/nano/store/tables.hpp @@ -12,7 +12,6 @@ enum class tables confirmation_height, default_unused, // RocksDB only final_votes, - frontiers, meta, online_weight, peers, diff --git a/nano/test_common/testutil.cpp b/nano/test_common/testutil.cpp index 62f15f82a..75f26546f 100644 --- a/nano/test_common/testutil.cpp +++ b/nano/test_common/testutil.cpp @@ -65,7 +65,7 @@ nano::account nano::test::random_account () bool nano::test::process (nano::node & node, std::vector> blocks) { - auto const transaction = node.store.tx_begin_write ({ tables::accounts, tables::blocks, tables::frontiers, tables::pending, tables::rep_weights }); + auto const transaction = node.store.tx_begin_write ({ tables::accounts, tables::blocks, tables::pending, tables::rep_weights }); for (auto & block : blocks) { auto result = node.process (transaction, block); From f942d9051540ffe5dedb6f4aff33b5086d809dfb Mon Sep 17 00:00:00 2001 From: clemahieu Date: Tue, 26 Mar 2024 11:58:16 +0000 Subject: [PATCH 110/128] Add missing lib/logger.hpp include. (#4524) --- nano/test_common/ledger.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/nano/test_common/ledger.hpp b/nano/test_common/ledger.hpp index e968f4d17..f996a9a4b 100644 --- a/nano/test_common/ledger.hpp +++ b/nano/test_common/ledger.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include From a974d2ac50f3c3c362212cdb8cbb017bc7a2db48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Fri, 22 Mar 2024 18:34:43 +0100 Subject: [PATCH 111/128] System io guard --- nano/test_common/system.cpp | 22 ++++++++++++---------- nano/test_common/system.hpp | 1 + 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/nano/test_common/system.cpp b/nano/test_common/system.cpp index 1e0f938f7..6d14598a5 100644 --- a/nano/test_common/system.cpp +++ b/nano/test_common/system.cpp @@ -32,7 +32,8 @@ std::string nano::error_system_messages::message (int ev) const */ nano::test::system::system () : - io_ctx{ std::make_shared () } + io_ctx{ std::make_shared () }, + io_guard{ boost::asio::make_work_guard (*io_ctx) } { auto scale_str = std::getenv ("DEADLINE_SCALE_FACTOR"); if (scale_str) @@ -70,6 +71,16 @@ nano::test::system::~system () #endif } +void nano::test::system::stop () +{ + io_guard.reset (); + for (auto & node : nodes) + { + node->stop (); + } + work.stop (); +} + nano::node & nano::test::system::node (std::size_t index) const { debug_assert (index < nodes.size ()); @@ -574,15 +585,6 @@ void nano::test::system::generate_mass_activity (uint32_t count_a, nano::node & } } -void nano::test::system::stop () -{ - for (auto i : nodes) - { - i->stop (); - } - work.stop (); -} - nano::node_config nano::test::system::default_config () { nano::node_config config{ get_available_port () }; diff --git a/nano/test_common/system.hpp b/nano/test_common/system.hpp index 00808006a..dffa0a47d 100644 --- a/nano/test_common/system.hpp +++ b/nano/test_common/system.hpp @@ -75,6 +75,7 @@ namespace test public: std::shared_ptr io_ctx; + boost::asio::executor_work_guard io_guard; std::vector> nodes; nano::stats stats; nano::logger logger{ "tests" }; From b5ae8b9f537621a9ac17832a74c9fcb2cdcfe03a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Tue, 26 Mar 2024 11:26:41 +0100 Subject: [PATCH 112/128] Keep io context running when stopping tests --- nano/core_test/bootstrap.cpp | 24 ------------------------ nano/core_test/network.cpp | 1 - nano/test_common/system.cpp | 21 +++++++++++++++++---- 3 files changed, 17 insertions(+), 29 deletions(-) diff --git a/nano/core_test/bootstrap.cpp b/nano/core_test/bootstrap.cpp index b78b65d5d..4442b7ede 100644 --- a/nano/core_test/bootstrap.cpp +++ b/nano/core_test/bootstrap.cpp @@ -296,7 +296,6 @@ TEST (bootstrap_processor, process_none) node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint (), false); ASSERT_TIMELY (5s, done); - node1->stop (); } // Bootstrap can pull one basic block @@ -320,7 +319,6 @@ TEST (bootstrap_processor, process_one) ASSERT_NE (node0->latest (nano::dev::genesis_key.pub), node1->latest (nano::dev::genesis_key.pub)); node1->bootstrap_initiator.bootstrap (node0->network.endpoint (), false); ASSERT_TIMELY_EQ (10s, node1->latest (nano::dev::genesis_key.pub), node0->latest (nano::dev::genesis_key.pub)); - node1->stop (); } TEST (bootstrap_processor, process_two) @@ -341,7 +339,6 @@ TEST (bootstrap_processor, process_two) ASSERT_NE (node1->latest (nano::dev::genesis_key.pub), node0->latest (nano::dev::genesis_key.pub)); // nodes should be out of sync here node1->bootstrap_initiator.bootstrap (node0->network.endpoint (), false); // bootstrap triggered ASSERT_TIMELY_EQ (5s, node1->latest (nano::dev::genesis_key.pub), node0->latest (nano::dev::genesis_key.pub)); // nodes should sync up - node1->stop (); } // Bootstrap can pull universal blocks @@ -387,7 +384,6 @@ TEST (bootstrap_processor, process_state) ASSERT_NE (node1->latest (nano::dev::genesis_key.pub), block2->hash ()); node1->bootstrap_initiator.bootstrap (node0->network.endpoint (), false); ASSERT_TIMELY_EQ (5s, node1->latest (nano::dev::genesis_key.pub), block2->hash ()); - node1->stop (); } TEST (bootstrap_processor, process_new) @@ -426,7 +422,6 @@ TEST (bootstrap_processor, process_new) auto node3 = system.make_disconnected_node (); node3->bootstrap_initiator.bootstrap (node1->network.endpoint (), false); ASSERT_TIMELY_EQ (5s, node3->balance (key2.pub), amount); - node3->stop (); } TEST (bootstrap_processor, pull_diamond) @@ -478,7 +473,6 @@ TEST (bootstrap_processor, pull_diamond) auto node1 = system.make_disconnected_node (); node1->bootstrap_initiator.bootstrap (node0->network.endpoint (), false); ASSERT_TIMELY_EQ (5s, node1->balance (nano::dev::genesis_key.pub), 100); - node1->stop (); } TEST (bootstrap_processor, DISABLED_pull_requeue_network_error) @@ -517,7 +511,6 @@ TEST (bootstrap_processor, DISABLED_pull_requeue_network_error) ++attempt->pulling; node1->bootstrap_initiator.connections->pulls.emplace_back (nano::dev::genesis_key.pub, send1->hash (), nano::dev::genesis->hash (), attempt->incremental_id); node1->bootstrap_initiator.connections->request_pull (lock); - node2->stop (); } ASSERT_TIMELY (5s, attempt == nullptr || attempt->requeued_pulls == 1); ASSERT_EQ (0, node1->stats.count (nano::stat::type::bootstrap, nano::stat::detail::bulk_pull_failed_account, nano::stat::dir::in)); // Requeue is not increasing failed attempts @@ -586,7 +579,6 @@ TEST (bootstrap_processor, push_diamond) auto node2 = system.add_node (config, flags); node1->bootstrap_initiator.bootstrap (node2->network.endpoint (), false); ASSERT_TIMELY_EQ (5s, node2->balance (nano::dev::genesis_key.pub), 100); - node1->stop (); } TEST (bootstrap_processor, push_diamond_pruning) @@ -677,7 +669,6 @@ TEST (bootstrap_processor, push_diamond_pruning) node1->bootstrap_initiator.bootstrap (node0->network.endpoint (), false); ASSERT_TIMELY_EQ (5s, node0->balance (nano::dev::genesis_key.pub), 100); ASSERT_TIMELY_EQ (5s, node1->balance (nano::dev::genesis_key.pub), 100); - node1->stop (); } TEST (bootstrap_processor, push_one) @@ -700,7 +691,6 @@ TEST (bootstrap_processor, push_one) node1->bootstrap_initiator.bootstrap (node0->network.endpoint (), false); ASSERT_TIMELY_EQ (5s, node0->balance (nano::dev::genesis_key.pub), genesis_balance - 100); - node1->stop (); } TEST (bootstrap_processor, lazy_hash) @@ -775,7 +765,6 @@ TEST (bootstrap_processor, lazy_hash) } // Check processed blocks ASSERT_TIMELY (10s, node1->balance (key2.pub) != 0); - node1->stop (); } TEST (bootstrap_processor, lazy_hash_bootstrap_id) @@ -850,7 +839,6 @@ TEST (bootstrap_processor, lazy_hash_bootstrap_id) } // Check processed blocks ASSERT_TIMELY (10s, node1->balance (key2.pub) != 0); - node1->stop (); } TEST (bootstrap_processor, lazy_hash_pruning) @@ -1003,7 +991,6 @@ TEST (bootstrap_processor, lazy_hash_pruning) ASSERT_TIMELY_EQ (5s, node1->ledger.cache.block_count, 9); ASSERT_TIMELY (5s, node1->balance (key2.pub) != 0); ASSERT_TIMELY (5s, !node1->bootstrap_initiator.in_progress ()); - node1->stop (); } TEST (bootstrap_processor, lazy_max_pull_count) @@ -1105,7 +1092,6 @@ TEST (bootstrap_processor, lazy_max_pull_count) node1->bootstrap_initiator.bootstrap_lazy (change3->hash ()); // Check processed blocks ASSERT_TIMELY (10s, node1->block (change3->hash ())); - node1->stop (); } TEST (bootstrap_processor, lazy_unclear_state_link) @@ -1174,7 +1160,6 @@ TEST (bootstrap_processor, lazy_unclear_state_link) node2->bootstrap_initiator.bootstrap_lazy (receive->hash ()); ASSERT_TIMELY (5s, nano::test::exists (*node2, { send1, send2, open, receive })); ASSERT_EQ (0, node2->stats.count (nano::stat::type::bootstrap, nano::stat::detail::bulk_pull_failed_account, nano::stat::dir::in)); - node2->stop (); } TEST (bootstrap_processor, lazy_unclear_state_link_not_existing) @@ -1233,7 +1218,6 @@ TEST (bootstrap_processor, lazy_unclear_state_link_not_existing) ASSERT_TIMELY (15s, !node2->bootstrap_initiator.in_progress ()); ASSERT_TIMELY (15s, nano::test::block_or_pruned_all_exists (*node2, { send1, open, send2 })); ASSERT_EQ (1, node2->stats.count (nano::stat::type::bootstrap, nano::stat::detail::bulk_pull_failed_account, nano::stat::dir::in)); - node2->stop (); } TEST (bootstrap_processor, lazy_destinations) @@ -1312,7 +1296,6 @@ TEST (bootstrap_processor, lazy_destinations) ASSERT_TIMELY (5s, node2->ledger.block_or_pruned_exists (send2->hash ())); ASSERT_FALSE (node2->ledger.block_or_pruned_exists (open->hash ())); ASSERT_FALSE (node2->ledger.block_or_pruned_exists (state_open->hash ())); - node2->stop (); } TEST (bootstrap_processor, lazy_pruning_missing_block) @@ -1421,7 +1404,6 @@ TEST (bootstrap_processor, lazy_pruning_missing_block) ASSERT_TIMELY_EQ (5s, 3, node2->ledger.cache.block_count); ASSERT_TIMELY (5s, nano::test::exists (*node2, { send1, send2 })); ASSERT_TRUE (nano::test::block_or_pruned_none_exists (*node2, { open, state_open })); - node2->stop (); } TEST (bootstrap_processor, lazy_cancel) @@ -1456,7 +1438,6 @@ TEST (bootstrap_processor, lazy_cancel) } // Cancel failing lazy bootstrap ASSERT_TIMELY (10s, !node1->bootstrap_initiator.in_progress ()); - node1->stop (); } TEST (bootstrap_processor, wallet_lazy_frontier) @@ -1537,7 +1518,6 @@ TEST (bootstrap_processor, wallet_lazy_frontier) } // Check processed blocks ASSERT_TIMELY (10s, node1->ledger.block_or_pruned_exists (receive2->hash ())); - node1->stop (); } TEST (bootstrap_processor, wallet_lazy_pending) @@ -1684,7 +1664,6 @@ TEST (bootstrap_processor, multiple_attempts) ASSERT_TIMELY (10s, node2->balance (key2.pub) != 0); // Check attempts finish ASSERT_TIMELY_EQ (5s, node2->bootstrap_initiator.attempts.size (), 0); - node2->stop (); } TEST (frontier_req_response, DISABLED_destruction) @@ -1996,7 +1975,6 @@ TEST (bulk, genesis) node2->bootstrap_initiator.bootstrap (node1->network.endpoint (), false); ASSERT_TIMELY_EQ (10s, node2->latest (nano::dev::genesis_key.pub), node1->latest (nano::dev::genesis_key.pub)); ASSERT_EQ (node2->latest (nano::dev::genesis_key.pub), node1->latest (nano::dev::genesis_key.pub)); - node2->stop (); } TEST (bulk, offline_send) @@ -2036,7 +2014,6 @@ TEST (bulk, offline_send) ASSERT_TIMELY_EQ (5s, node2->balance (nano::dev::genesis_key.pub), std::numeric_limits::max () - amount); // Receiving send block ASSERT_TIMELY_EQ (5s, node2->balance (key2.pub), amount); - node2->stop (); } TEST (bulk, genesis_pruning) @@ -2115,7 +2092,6 @@ TEST (bulk, genesis_pruning) ASSERT_TIMELY_EQ (5s, node2->bootstrap_initiator.connections->connections_count, 0); node2->bootstrap_initiator.bootstrap (node1->network.endpoint (), false); ASSERT_TIMELY_EQ (5s, node2->latest (nano::dev::genesis_key.pub), node1->latest (nano::dev::genesis_key.pub)); - node2->stop (); } TEST (bulk_pull_account, basics) diff --git a/nano/core_test/network.cpp b/nano/core_test/network.cpp index d5a97da73..8b9a372ef 100644 --- a/nano/core_test/network.cpp +++ b/nano/core_test/network.cpp @@ -105,7 +105,6 @@ TEST (network, send_node_id_handshake_tcp) auto list2 (node1->network.list (1)); ASSERT_EQ (nano::transport::transport_type::tcp, list2[0]->get_type ()); ASSERT_EQ (node0->get_node_id (), list2[0]->get_node_id ()); - node1->stop (); } TEST (network, last_contacted) diff --git a/nano/test_common/system.cpp b/nano/test_common/system.cpp index 6d14598a5..46c72dd10 100644 --- a/nano/test_common/system.cpp +++ b/nano/test_common/system.cpp @@ -73,11 +73,23 @@ nano::test::system::~system () void nano::test::system::stop () { + logger.debug (nano::log::type::system, "Stopping..."); + + // Keep io_context running while stopping + auto stopped = std::async (std::launch::async, [&] { + for (auto & node : nodes) + { + node->stop (); + } + }); + + auto ec = poll_until_true (10s, [&] { + auto status = stopped.wait_for (0s); + return status == std::future_status::ready; + }); + debug_assert (!ec); + io_guard.reset (); - for (auto & node : nodes) - { - node->stop (); - } work.stop (); } @@ -195,6 +207,7 @@ std::shared_ptr nano::test::system::make_disconnected_node (std::opt return nullptr; } node->start (); + nodes.push_back (node); return node; } From 08b32db3d924dbd08676fa91b8fc8c31a418ae49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Tue, 26 Mar 2024 17:20:29 +0100 Subject: [PATCH 113/128] Do not directly stop nodes in tests --- nano/core_test/network.cpp | 2 +- nano/core_test/node.cpp | 20 ++++++-------------- nano/core_test/rep_crawler.cpp | 4 ++-- nano/core_test/socket.cpp | 10 ---------- nano/core_test/telemetry.cpp | 4 +--- nano/test_common/system.cpp | 18 ++++++++++++++++++ nano/test_common/system.hpp | 5 ++++- 7 files changed, 32 insertions(+), 31 deletions(-) diff --git a/nano/core_test/network.cpp b/nano/core_test/network.cpp index 8b9a372ef..73bcd7d94 100644 --- a/nano/core_test/network.cpp +++ b/nano/core_test/network.cpp @@ -569,7 +569,7 @@ TEST (network, ipv6_bind_send_ipv4) TEST (network, endpoint_bad_fd) { nano::test::system system (1); - system.nodes[0]->stop (); + system.stop_node (*system.nodes[0]); auto endpoint (system.nodes[0]->network.endpoint ()); ASSERT_TRUE (endpoint.address ().is_loopback ()); // The endpoint is invalidated asynchronously diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index f9bc35ec5..d396a2cba 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -40,8 +40,6 @@ TEST (node, stop) { nano::test::system system (1); ASSERT_NE (system.nodes[0]->wallets.items.end (), system.nodes[0]->wallets.items.begin ()); - system.nodes[0]->stop (); - system.io_ctx->run (); ASSERT_TRUE (true); } @@ -77,9 +75,10 @@ TEST (node, block_store_path_failure) auto path (nano::unique_path ()); nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; auto node (std::make_shared (io_ctx, system.get_available_port (), path, pool)); + system.register_node (node); ASSERT_TRUE (node->wallets.items.empty ()); - node->stop (); } + #if defined(__clang__) && defined(__linux__) && CI // Disable test due to instability with clang and actions TEST (node_DeathTest, DISABLED_readonly_block_store_not_exist) @@ -102,16 +101,13 @@ TEST (node_DeathTest, readonly_block_store_not_exist) TEST (node, password_fanout) { nano::test::system system; - auto io_ctx = std::make_shared (); - auto path (nano::unique_path ()); nano::node_config config; config.peering_port = system.get_available_port (); nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; config.password_fanout = 10; - nano::node node (io_ctx, path, config, pool); + auto & node = *system.add_node (config); auto wallet (node.wallets.create (100)); ASSERT_EQ (10, wallet->store.password.values.size ()); - node.stop (); } TEST (node, balance) @@ -284,8 +280,6 @@ TEST (node, auto_bootstrap) ASSERT_TIMELY_EQ (5s, node1->ledger.cache.block_count, 3); // Confirmation for all blocks ASSERT_TIMELY_EQ (5s, node1->ledger.cache.cemented_count, 3); - - node1->stop (); } TEST (node, auto_bootstrap_reverse) @@ -329,8 +323,6 @@ TEST (node, auto_bootstrap_age) ASSERT_TIMELY (10s, node0->stats.count (nano::stat::type::bootstrap, nano::stat::detail::initiate_legacy_age, nano::stat::dir::out) >= 3); // More attempts with frontiers age ASSERT_GE (node0->stats.count (nano::stat::type::bootstrap, nano::stat::detail::initiate_legacy_age, nano::stat::dir::out), node0->stats.count (nano::stat::type::bootstrap, nano::stat::detail::initiate, nano::stat::dir::out)); - - node1->stop (); } TEST (node, merge_peers) @@ -2843,7 +2835,7 @@ TEST (node, peers) ASSERT_TRUE (store.peer.exists (store.tx_begin_read (), endpoint_key)); // Stop the peer node and check that it is removed from the store - node1->stop (); + system.stop_node (*node1); // TODO: In `tcp_channels::store_all` we skip store operation when there are no peers present, // so the best we can do here is check if network is empty @@ -2873,7 +2865,7 @@ TEST (node, peer_cache_restart) auto list (node2->network.list (2)); ASSERT_EQ (node1->network.endpoint (), list[0]->get_endpoint ()); ASSERT_EQ (1, node2->network.size ()); - node2->stop (); + system.stop_node (*node2); } // Restart node { @@ -2896,7 +2888,7 @@ TEST (node, peer_cache_restart) auto list (node3->network.list (2)); ASSERT_EQ (node1->network.endpoint (), list[0]->get_endpoint ()); ASSERT_EQ (1, node3->network.size ()); - node3->stop (); + system.stop_node (*node3); } } diff --git a/nano/core_test/rep_crawler.cpp b/nano/core_test/rep_crawler.cpp index 0dc098ada..1addde26c 100644 --- a/nano/core_test/rep_crawler.cpp +++ b/nano/core_test/rep_crawler.cpp @@ -220,7 +220,7 @@ TEST (rep_crawler, rep_remove) ASSERT_TIMELY_EQ (10s, searching_node.rep_crawler.representative_count (), 2); // When Rep2 is stopped, it should not be found as principal representative anymore - node_rep2->stop (); + system.stop_node (*node_rep2); ASSERT_TIMELY_EQ (10s, searching_node.rep_crawler.representative_count (), 1); // Now only genesisRep should be found: @@ -239,7 +239,7 @@ TEST (rep_crawler, rep_connection_close) // Add working representative (node 2) system.wallet (1)->insert_adhoc (nano::dev::genesis_key.prv); ASSERT_TIMELY_EQ (10s, node1.rep_crawler.representative_count (), 1); - node2.stop (); + system.stop_node (node2); // Remove representative with closed channel ASSERT_TIMELY_EQ (10s, node1.rep_crawler.representative_count (), 0); } diff --git a/nano/core_test/socket.cpp b/nano/core_test/socket.cpp index bbbf9d88a..680e8ef25 100644 --- a/nano/core_test/socket.cpp +++ b/nano/core_test/socket.cpp @@ -103,8 +103,6 @@ TEST (socket, max_connections) ASSERT_TIMELY_EQ (5s, get_tcp_accept_successes (), 5); ASSERT_TIMELY_EQ (5s, connection_attempts, 8); // connections initiated by the client ASSERT_TIMELY_EQ (5s, server_sockets.size (), 5); // connections accepted by the server - - node->stop (); } TEST (socket, max_connections_per_ip) @@ -162,8 +160,6 @@ TEST (socket, max_connections_per_ip) ASSERT_TIMELY_EQ (5s, get_tcp_accept_successes (), max_ip_connections); ASSERT_TIMELY_EQ (5s, get_tcp_max_per_ip (), 1); ASSERT_TIMELY_EQ (5s, connection_attempts, max_ip_connections + 1); - - node->stop (); } TEST (socket, limited_subnet_address) @@ -283,8 +279,6 @@ TEST (socket, max_connections_per_subnetwork) ASSERT_TIMELY_EQ (5s, get_tcp_accept_successes (), max_subnetwork_connections); ASSERT_TIMELY_EQ (5s, get_tcp_max_per_subnetwork (), 1); ASSERT_TIMELY_EQ (5s, connection_attempts, max_subnetwork_connections + 1); - - node->stop (); } TEST (socket, disabled_max_peers_per_ip) @@ -344,8 +338,6 @@ TEST (socket, disabled_max_peers_per_ip) ASSERT_TIMELY_EQ (5s, get_tcp_accept_successes (), max_ip_connections + 1); ASSERT_TIMELY_EQ (5s, get_tcp_max_per_ip (), 0); ASSERT_TIMELY_EQ (5s, connection_attempts, max_ip_connections + 1); - - node->stop (); } TEST (socket, disconnection_of_silent_connections) @@ -399,8 +391,6 @@ TEST (socket, disconnection_of_silent_connections) ASSERT_EQ (0, get_tcp_io_timeout_drops ()); // Asserts the silent checker worked. ASSERT_EQ (1, get_tcp_silent_connection_drops ()); - - node->stop (); } TEST (socket, drop_policy) diff --git a/nano/core_test/telemetry.cpp b/nano/core_test/telemetry.cpp index e607a3603..fe6e5db72 100644 --- a/nano/core_test/telemetry.cpp +++ b/nano/core_test/telemetry.cpp @@ -334,16 +334,14 @@ TEST (telemetry, disconnected) nano::node_flags node_flags; auto node_client = system.add_node (node_flags); auto node_server = system.add_node (node_flags); - nano::test::wait_peer_connections (system); - auto channel = node_client->network.find_node_id (node_server->get_node_id ()); ASSERT_NE (nullptr, channel); // Ensure telemetry is available before disconnecting ASSERT_TIMELY (5s, node_client->telemetry.get_telemetry (channel->get_endpoint ())); - node_server->stop (); + system.stop_node (*node_server); ASSERT_TRUE (channel); // Ensure telemetry from disconnected peer is removed diff --git a/nano/test_common/system.cpp b/nano/test_common/system.cpp index 46c72dd10..3005d4c5e 100644 --- a/nano/test_common/system.cpp +++ b/nano/test_common/system.cpp @@ -211,6 +211,24 @@ std::shared_ptr nano::test::system::make_disconnected_node (std::opt return node; } +void nano::test::system::register_node (std::shared_ptr const & node) +{ + debug_assert (std::find (nodes.begin (), nodes.end (), node) == nodes.end ()); + nodes.push_back (node); +} + +void nano::test::system::stop_node (nano::node & node) +{ + auto stopped = std::async (std::launch::async, [&node] () { + node.stop (); + }); + auto ec = poll_until_true (5s, [&] () { + auto status = stopped.wait_for (0s); + return status == std::future_status::ready; + }); + debug_assert (!ec); +} + void nano::test::system::ledger_initialization_set (std::vector const & reps, nano::amount const & reserve) { nano::block_hash previous = nano::dev::genesis->hash (); diff --git a/nano/test_common/system.hpp b/nano/test_common/system.hpp index dffa0a47d..e0c9f127c 100644 --- a/nano/test_common/system.hpp +++ b/nano/test_common/system.hpp @@ -25,6 +25,8 @@ namespace test system (uint16_t, nano::transport::transport_type = nano::transport::transport_type::tcp, nano::node_flags = nano::node_flags ()); ~system (); + void stop (); + void ledger_initialization_set (std::vector const & reps, nano::amount const & reserve = 0); void generate_activity (nano::node &, std::vector &); void generate_mass_activity (uint32_t, nano::node &); @@ -50,7 +52,6 @@ namespace test std::error_code poll (std::chrono::nanoseconds const & sleep_time = std::chrono::milliseconds (50)); std::error_code poll_until_true (std::chrono::nanoseconds deadline, std::function); void delay_ms (std::chrono::milliseconds const & delay); - void stop (); void deadline_set (std::chrono::duration const & delta); /* * Convenience function to get a reference to a node at given index. Does bound checking. @@ -61,6 +62,8 @@ namespace test // Make an independent node that uses system resources but is not part of the system node list and does not automatically connect to other nodes std::shared_ptr make_disconnected_node (std::optional opt_node_config = std::nullopt, nano::node_flags = nano::node_flags ()); + void register_node (std::shared_ptr const &); + void stop_node (nano::node &); /* * Returns default config for node running in test environment From a615ea68a3ca35237dd33dc54507b8e806956b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Wed, 27 Mar 2024 10:26:21 +0100 Subject: [PATCH 114/128] Additional thread runner logging --- nano/lib/logging_enums.hpp | 1 + nano/lib/thread_roles.cpp | 7 +++++++ nano/lib/thread_roles.hpp | 2 ++ nano/lib/thread_runner.cpp | 4 +++- nano/lib/thread_runner.hpp | 5 ++++- 5 files changed, 17 insertions(+), 2 deletions(-) diff --git a/nano/lib/logging_enums.hpp b/nano/lib/logging_enums.hpp index a5782edd7..79f4df8de 100644 --- a/nano/lib/logging_enums.hpp +++ b/nano/lib/logging_enums.hpp @@ -74,6 +74,7 @@ enum class type vote_generator, rep_tiers, syn_cookies, + thread_runner, // bootstrap bulk_pull_client, diff --git a/nano/lib/thread_roles.cpp b/nano/lib/thread_roles.cpp index 7c75351c4..52eb3bba1 100644 --- a/nano/lib/thread_roles.cpp +++ b/nano/lib/thread_roles.cpp @@ -1,6 +1,13 @@ #include #include +#include + +std::string_view nano::thread_role::to_string (nano::thread_role::name name) +{ + return magic_enum::enum_name (name); +} + std::string nano::thread_role::get_string (nano::thread_role::name role) { std::string thread_role_name_string; diff --git a/nano/lib/thread_roles.hpp b/nano/lib/thread_roles.hpp index a00a55226..c1dfce2d3 100644 --- a/nano/lib/thread_roles.hpp +++ b/nano/lib/thread_roles.hpp @@ -50,6 +50,8 @@ enum class name network_reachout, }; +std::string_view to_string (name); + /* * Get/Set the identifier for the current thread */ diff --git a/nano/lib/thread_runner.cpp b/nano/lib/thread_runner.cpp index 363a21795..62a7625cd 100644 --- a/nano/lib/thread_runner.cpp +++ b/nano/lib/thread_runner.cpp @@ -19,11 +19,13 @@ nano::thread_runner::thread_runner (std::shared_ptr io_ for (auto i (0u); i < num_threads; ++i) { - threads.emplace_back (nano::thread_attributes::get_default (), [this] () { + threads.emplace_back (nano::thread_attributes::get_default (), [this, i] () { nano::thread_role::set (role); try { + logger.debug (nano::log::type::thread_runner, "Thread #{} ({}) started", i, to_string (role)); run (*io_ctx); + logger.debug (nano::log::type::thread_runner, "Thread #{} ({}) stopped", i, to_string (role)); } catch (std::exception const & ex) { diff --git a/nano/lib/thread_runner.hpp b/nano/lib/thread_runner.hpp index 975beab3f..116a1aa65 100644 --- a/nano/lib/thread_runner.hpp +++ b/nano/lib/thread_runner.hpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -11,8 +12,10 @@ namespace nano { class thread_runner final { + nano::logger logger; + public: - thread_runner (std::shared_ptr, unsigned num_threads, nano::thread_role::name thread_role = nano::thread_role::name::io); + thread_runner (std::shared_ptr, unsigned num_threads = nano::hardware_concurrency (), nano::thread_role::name thread_role = nano::thread_role::name::io); ~thread_runner (); /** Tells the IO context to stop processing events.*/ From 3a0649de02c1aca7402286dd774a01ca8d783d6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Wed, 27 Mar 2024 13:35:17 +0100 Subject: [PATCH 115/128] Remove unnecessary test --- nano/core_test/network.cpp | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/nano/core_test/network.cpp b/nano/core_test/network.cpp index 73bcd7d94..cafb62282 100644 --- a/nano/core_test/network.cpp +++ b/nano/core_test/network.cpp @@ -530,42 +530,6 @@ TEST (network, ipv6_from_ipv4) ASSERT_TRUE (endpoint2.address ().is_v6 ()); } -TEST (network, ipv6_bind_send_ipv4) -{ - nano::test::system system; - nano::endpoint endpoint1 (boost::asio::ip::address_v6::any (), 0); - nano::endpoint endpoint2 (boost::asio::ip::address_v4::any (), 0); - std::array bytes1{}; - std::atomic finish1{ false }; - nano::endpoint endpoint3; - boost::asio::ip::udp::socket socket1 (*system.io_ctx, endpoint1); - socket1.async_receive_from (boost::asio::buffer (bytes1.data (), bytes1.size ()), endpoint3, [&finish1] (boost::system::error_code const & error, size_t size_a) { - ASSERT_FALSE (error); - ASSERT_EQ (16, size_a); - finish1 = true; - }); - boost::asio::ip::udp::socket socket2 (*system.io_ctx, endpoint2); - nano::endpoint endpoint5 (boost::asio::ip::address_v4::loopback (), socket1.local_endpoint ().port ()); - nano::endpoint endpoint6 (boost::asio::ip::address_v6::v4_mapped (boost::asio::ip::address_v4::loopback ()), socket2.local_endpoint ().port ()); - socket2.async_send_to (boost::asio::buffer (std::array{}, 16), endpoint5, [] (boost::system::error_code const & error, size_t size_a) { - ASSERT_FALSE (error); - ASSERT_EQ (16, size_a); - }); - auto iterations (0); - ASSERT_TIMELY (5s, finish1); - ASSERT_EQ (endpoint6, endpoint3); - std::array bytes2; - nano::endpoint endpoint4; - socket2.async_receive_from (boost::asio::buffer (bytes2.data (), bytes2.size ()), endpoint4, [] (boost::system::error_code const & error, size_t size_a) { - ASSERT_FALSE (!error); - ASSERT_EQ (16, size_a); - }); - socket1.async_send_to (boost::asio::buffer (std::array{}, 16), endpoint6, [] (boost::system::error_code const & error, size_t size_a) { - ASSERT_FALSE (error); - ASSERT_EQ (16, size_a); - }); -} - TEST (network, endpoint_bad_fd) { nano::test::system system (1); From 95c52804f97f16fb282ada83e7706035839acbc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Wed, 27 Mar 2024 14:31:16 +0100 Subject: [PATCH 116/128] Fix lifetime issues in rpc components --- nano/rpc/rpc.cpp | 14 ++++++++++---- nano/rpc/rpc.hpp | 2 +- nano/rpc/rpc_request_processor.cpp | 5 ++--- nano/rpc/rpc_request_processor.hpp | 3 ++- nano/rpc_test/rpc.cpp | 7 +++++++ 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/nano/rpc/rpc.cpp b/nano/rpc/rpc.cpp index 196feee1a..9beb8ecc8 100644 --- a/nano/rpc/rpc.cpp +++ b/nano/rpc/rpc.cpp @@ -57,10 +57,16 @@ void nano::rpc::start () void nano::rpc::accept () { auto connection (std::make_shared (config, io_ctx, logger, rpc_handler_interface)); - acceptor.async_accept (connection->socket, boost::asio::bind_executor (connection->strand, [this, connection] (boost::system::error_code const & ec) { - if (ec != boost::asio::error::operation_aborted && acceptor.is_open ()) + acceptor.async_accept (connection->socket, + boost::asio::bind_executor (connection->strand, [this_w = std::weak_ptr{ shared_from_this () }, connection] (boost::system::error_code const & ec) { + auto this_l = this_w.lock (); + if (!this_l) { - accept (); + return; + } + if (ec != boost::asio::error::operation_aborted && this_l->acceptor.is_open ()) + { + this_l->accept (); } if (!ec) { @@ -68,7 +74,7 @@ void nano::rpc::accept () } else { - logger.error (nano::log::type::rpc, "Error accepting RPC connection: {}", ec.message ()); + this_l->logger.error (nano::log::type::rpc, "Error accepting RPC connection: {}", ec.message ()); } })); } diff --git a/nano/rpc/rpc.hpp b/nano/rpc/rpc.hpp index 353d26313..240763d42 100644 --- a/nano/rpc/rpc.hpp +++ b/nano/rpc/rpc.hpp @@ -17,7 +17,7 @@ namespace nano { class rpc_handler_interface; -class rpc +class rpc : public std::enable_shared_from_this { public: rpc (std::shared_ptr, nano::rpc_config config_a, nano::rpc_handler_interface & rpc_handler_interface_a); diff --git a/nano/rpc/rpc_request_processor.cpp b/nano/rpc/rpc_request_processor.cpp index d7315890c..e5231aef4 100644 --- a/nano/rpc/rpc_request_processor.cpp +++ b/nano/rpc/rpc_request_processor.cpp @@ -19,9 +19,9 @@ nano::rpc_request_processor::rpc_request_processor (boost::asio::io_context & io { connections.push_back (std::make_shared (nano::ipc::ipc_client (io_ctx), false)); auto connection = this->connections.back (); - connection->client.async_connect (ipc_address, ipc_port, [connection, &connections_mutex = this->connections_mutex] (nano::error err) { + connection->client.async_connect (ipc_address, ipc_port, + [connection] (nano::error err) { // Even if there is an error this needs to be set so that another attempt can be made to connect with the ipc connection - nano::lock_guard lk{ connections_mutex }; connection->is_available = true; }); } @@ -85,7 +85,6 @@ void nano::rpc_request_processor::read_payload (std::shared_ptr lk{ connections_mutex }; connection.is_available = true; // Allow people to use it now } diff --git a/nano/rpc/rpc_request_processor.hpp b/nano/rpc/rpc_request_processor.hpp index 155323985..d3983084d 100644 --- a/nano/rpc/rpc_request_processor.hpp +++ b/nano/rpc/rpc_request_processor.hpp @@ -5,6 +5,7 @@ #include #include +#include #include namespace nano @@ -17,7 +18,7 @@ struct ipc_connection } nano::ipc::ipc_client client; - bool is_available{ false }; + std::atomic is_available{ false }; }; struct rpc_request diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 284bea023..304ce27d6 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -36,6 +36,13 @@ using namespace std::chrono_literals; using namespace nano::test; +TEST (rpc, creation) +{ + nano::test::system system; + auto node = add_ipc_enabled_node (system); + ASSERT_NO_THROW (add_rpc (system, node)); +} + TEST (rpc, wrapped_task) { nano::test::system system; From b4fc9997da48fdb0d772c0aa26cb78e45d769924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Wed, 27 Mar 2024 15:30:59 +0100 Subject: [PATCH 117/128] Fix rpc tests --- nano/rpc_test/rpc.cpp | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 304ce27d6..f846ac9d4 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -1708,7 +1708,6 @@ TEST (rpc, keepalive) ASSERT_EQ (0, node0->network.size ()); ASSERT_NO_ERROR (system.poll ()); } - node1->stop (); } TEST (rpc, peers) @@ -5206,7 +5205,6 @@ TEST (rpc, online_reps) ASSERT_NE (representatives3.end (), item3); ASSERT_EQ (new_rep.to_account (), item3->first); ASSERT_EQ (representatives3.size (), 1); - node2->stop (); } TEST (rpc, confirmation_history) @@ -5341,7 +5339,6 @@ TEST (rpc, block_confirm_confirmed) ASSERT_TIMELY (10s, node->stats.count (nano::stat::type::error, nano::stat::detail::http_callback, nano::stat::dir::out) != 0); // Callback result is error because callback target port isn't listening ASSERT_EQ (1, node->stats.count (nano::stat::type::error, nano::stat::detail::http_callback, nano::stat::dir::out)); - node->stop (); } TEST (rpc, node_id) @@ -5978,12 +5975,13 @@ TEST (rpc, active_difficulty) } // This is mainly to check for threading issues with TSAN +// TODO: Use multiple threads to run io context TEST (rpc, simultaneous_calls) { // This tests simulatenous calls to the same node in different threads nano::test::system system; auto node = add_ipc_enabled_node (system); - nano::thread_runner runner (system.io_ctx, node->config.io_threads); + nano::node_rpc_config node_rpc_config; nano::ipc::ipc_server ipc_server (*node, node_rpc_config); nano::rpc_config rpc_config{ nano::dev::network_params.network, system.get_available_port (), true }; @@ -5991,8 +5989,9 @@ TEST (rpc, simultaneous_calls) ASSERT_TRUE (ipc_tcp_port.has_value ()); rpc_config.rpc_process.num_ipc_connections = 8; nano::ipc_rpc_processor ipc_rpc_processor (*system.io_ctx, rpc_config, ipc_tcp_port.value ()); - nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); - rpc.start (); + auto rpc = std::make_shared (system.io_ctx, rpc_config, ipc_rpc_processor); + nano::test::start_stop_guard stop_guard{ *rpc }; + boost::property_tree::ptree request; request.put ("action", "account_block_count"); request.put ("account", nano::dev::genesis_key.pub.to_account ()); @@ -6008,7 +6007,7 @@ TEST (rpc, simultaneous_calls) std::atomic count{ num }; for (int i = 0; i < num; ++i) { - std::thread ([&test_responses, &promise, &count, i, port = rpc.listening_port ()] () { + std::thread ([&test_responses, &promise, &count, i, port = rpc->listening_port ()] () { test_responses[i]->run (port); if (--count == 0) { @@ -6018,8 +6017,8 @@ TEST (rpc, simultaneous_calls) .detach (); } - promise.get_future ().wait (); - + auto future = promise.get_future (); + ASSERT_TIMELY (5s, future.wait_for (0s) == std::future_status::ready); ASSERT_TIMELY (60s, std::all_of (test_responses.begin (), test_responses.end (), [] (auto const & test_response) { return test_response->status != 0; })); for (int i = 0; i < num; ++i) @@ -6028,11 +6027,6 @@ TEST (rpc, simultaneous_calls) std::string block_count_text (test_responses[i]->json.get ("block_count")); ASSERT_EQ ("1", block_count_text); } - rpc.stop (); - system.stop (); - ipc_server.stop (); - system.io_ctx->stop (); - runner.join (); } // This tests that the inprocess RPC (i.e without using IPC) works correctly From 3ea2f5c7a958f8550c8b50cb6aeb700da418a830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Wed, 27 Mar 2024 15:31:18 +0100 Subject: [PATCH 118/128] Fix ipc acceptor error logging --- nano/node/ipc/ipc_server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nano/node/ipc/ipc_server.cpp b/nano/node/ipc/ipc_server.cpp index 3d13d6676..12ed3385f 100644 --- a/nano/node/ipc/ipc_server.cpp +++ b/nano/node/ipc/ipc_server.cpp @@ -511,7 +511,7 @@ public: } else { - node->logger.error (nano::log::type::ipc, "Acceptor error: ", ec.message ()); + node->logger.error (nano::log::type::ipc, "Acceptor error: {}", ec.message ()); } if (ec != boost::asio::error::operation_aborted && acceptor->is_open ()) From 2fd368a643813165fb817fc957fd8e880fa6fca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Wed, 27 Mar 2024 18:50:24 +0100 Subject: [PATCH 119/128] Make socket tests pass --- nano/core_test/socket.cpp | 42 ++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/nano/core_test/socket.cpp b/nano/core_test/socket.cpp index 680e8ef25..85d821179 100644 --- a/nano/core_test/socket.cpp +++ b/nano/core_test/socket.cpp @@ -31,8 +31,11 @@ TEST (socket, max_connections) // start a server socket that allows max 2 live connections auto listener = std::make_shared (server_port, *node, 2); nano::test::stop_guard stop_guard{ *listener }; - listener->start ([&server_sockets] (std::shared_ptr const & new_connection, boost::system::error_code const & ec_a) { - server_sockets.push_back (new_connection); + listener->start ([&server_sockets] (std::shared_ptr const & new_connection, boost::system::error_code const & ec) { + if (!ec) + { + server_sockets.push_back (new_connection); + } return true; }); @@ -124,8 +127,11 @@ TEST (socket, max_connections_per_ip) auto listener = std::make_shared (server_port, *node, max_global_connections); nano::test::stop_guard stop_guard{ *listener }; - listener->start ([&server_sockets] (std::shared_ptr const & new_connection, boost::system::error_code const & ec_a) { - server_sockets.push_back (new_connection); + listener->start ([&server_sockets] (std::shared_ptr const & new_connection, boost::system::error_code const & ec) { + if (!ec) + { + server_sockets.push_back (new_connection); + } return true; }); @@ -243,8 +249,11 @@ TEST (socket, max_connections_per_subnetwork) auto listener = std::make_shared (server_port, *node, max_global_connections); nano::test::stop_guard stop_guard{ *listener }; - listener->start ([&server_sockets] (std::shared_ptr const & new_connection, boost::system::error_code const & ec_a) { - server_sockets.push_back (new_connection); + listener->start ([&server_sockets] (std::shared_ptr const & new_connection, boost::system::error_code const & ec) { + if (!ec) + { + server_sockets.push_back (new_connection); + } return true; }); @@ -302,8 +311,11 @@ TEST (socket, disabled_max_peers_per_ip) auto server_socket = std::make_shared (server_port, *node, max_global_connections); nano::test::stop_guard stop_guard{ *server_socket }; - server_socket->start ([&server_sockets] (std::shared_ptr const & new_connection, boost::system::error_code const & ec_a) { - server_sockets.push_back (new_connection); + server_socket->start ([&server_sockets] (std::shared_ptr const & new_connection, boost::system::error_code const & ec) { + if (!ec) + { + server_sockets.push_back (new_connection); + } return true; }); @@ -361,8 +373,11 @@ TEST (socket, disconnection_of_silent_connections) // start a server listening socket auto listener = std::make_shared (server_port, *node, 1); nano::test::stop_guard stop_guard{ *listener }; - listener->start ([&server_data_socket] (std::shared_ptr const & new_connection, boost::system::error_code const & ec_a) { - server_data_socket = new_connection; + listener->start ([&server_data_socket] (std::shared_ptr const & new_connection, boost::system::error_code const & ec) { + if (!ec) + { + server_data_socket = new_connection; + } return true; }); @@ -411,8 +426,11 @@ TEST (socket, drop_policy) auto listener = std::make_shared (server_port, *node, 1); nano::test::stop_guard stop_guard{ *listener }; - listener->start ([&connections] (std::shared_ptr const & new_connection, boost::system::error_code const & ec_a) { - connections.push_back (new_connection); + listener->start ([&connections] (std::shared_ptr const & new_connection, boost::system::error_code const & ec) { + if (!ec) + { + connections.push_back (new_connection); + } return true; }); From f741d587e6a358c3de2b69ed94a3dd33ddc1c447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Wed, 27 Mar 2024 22:51:43 +0100 Subject: [PATCH 120/128] Use shared_ptr for storing rpc component --- nano/nano_node/daemon.cpp | 2 +- nano/rpc/rpc.cpp | 10 +++------- nano/rpc/rpc.hpp | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/nano/nano_node/daemon.cpp b/nano/nano_node/daemon.cpp index 879bc5e2d..0ed9ee705 100644 --- a/nano/nano_node/daemon.cpp +++ b/nano/nano_node/daemon.cpp @@ -150,7 +150,7 @@ void nano::daemon::run (std::filesystem::path const & data_path, nano::node_flag nano::ipc::ipc_server ipc_server (*node, config.rpc); std::unique_ptr rpc_process; - std::unique_ptr rpc; + std::shared_ptr rpc; std::unique_ptr rpc_handler; if (config.rpc_enable) { diff --git a/nano/rpc/rpc.cpp b/nano/rpc/rpc.cpp index 9beb8ecc8..2ef3e2615 100644 --- a/nano/rpc/rpc.cpp +++ b/nano/rpc/rpc.cpp @@ -85,20 +85,16 @@ void nano::rpc::stop () acceptor.close (); } -std::unique_ptr nano::get_rpc (std::shared_ptr io_ctx_a, nano::rpc_config const & config_a, nano::rpc_handler_interface & rpc_handler_interface_a) +std::shared_ptr nano::get_rpc (std::shared_ptr io_ctx_a, nano::rpc_config const & config_a, nano::rpc_handler_interface & rpc_handler_interface_a) { - std::unique_ptr impl; - if (config_a.tls_config && config_a.tls_config->enable_https) { #ifdef NANO_SECURE_RPC - impl = std::make_unique (io_ctx_a, config_a, rpc_handler_interface_a); + return std::make_shared (io_ctx_a, config_a, rpc_handler_interface_a); #endif } else { - impl = std::make_unique (io_ctx_a, config_a, rpc_handler_interface_a); + return std::make_shared (io_ctx_a, config_a, rpc_handler_interface_a); } - - return impl; } diff --git a/nano/rpc/rpc.hpp b/nano/rpc/rpc.hpp index 240763d42..73f4b0ebf 100644 --- a/nano/rpc/rpc.hpp +++ b/nano/rpc/rpc.hpp @@ -44,5 +44,5 @@ public: }; /** Returns the correct RPC implementation based on TLS configuration */ -std::unique_ptr get_rpc (std::shared_ptr, nano::rpc_config const & config_a, nano::rpc_handler_interface & rpc_handler_interface_a); +std::shared_ptr get_rpc (std::shared_ptr, nano::rpc_config const & config_a, nano::rpc_handler_interface & rpc_handler_interface_a); } From 8eb54ba01a8623d546dbd446d3a864e769c34d5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Fri, 29 Mar 2024 11:08:43 +0100 Subject: [PATCH 121/128] Fix disconnected nodes --- nano/test_common/system.cpp | 38 +++++++++++++++++++------------------ nano/test_common/system.hpp | 1 + 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/nano/test_common/system.cpp b/nano/test_common/system.cpp index 3005d4c5e..df300b0ea 100644 --- a/nano/test_common/system.cpp +++ b/nano/test_common/system.cpp @@ -75,19 +75,15 @@ void nano::test::system::stop () { logger.debug (nano::log::type::system, "Stopping..."); - // Keep io_context running while stopping - auto stopped = std::async (std::launch::async, [&] { - for (auto & node : nodes) - { - node->stop (); - } - }); - - auto ec = poll_until_true (10s, [&] { - auto status = stopped.wait_for (0s); - return status == std::future_status::ready; - }); - debug_assert (!ec); + // Keep io_context running while stopping nodes + for (auto & node : nodes) + { + stop_node (*node); + } + for (auto & node : disconnected_nodes) + { + stop_node (*node); + } io_guard.reset (); work.stop (); @@ -120,8 +116,9 @@ std::shared_ptr nano::test::system::add_node (nano::node_config cons wallet->insert_adhoc (rep->prv); } node->start (); - nodes.reserve (nodes.size () + 1); nodes.push_back (node); + + // Connect with other nodes if (nodes.size () > 1) { debug_assert (nodes.size () - 1 <= node->network_params.network.max_peers_per_ip || node->flags.disable_max_peers_per_ip); // Check that we don't start more nodes than limit for single IP address @@ -197,17 +194,22 @@ std::shared_ptr nano::test::system::add_node (nano::node_config cons return node; } +// TODO: Merge with add_node std::shared_ptr nano::test::system::make_disconnected_node (std::optional opt_node_config, nano::node_flags flags) { nano::node_config node_config = opt_node_config.has_value () ? *opt_node_config : default_config (); auto node = std::make_shared (io_ctx, nano::unique_path (), node_config, work, flags); - if (node->init_error ()) + for (auto i : initialization_blocks) { - std::cerr << "node init error\n"; - return nullptr; + auto result = node->ledger.process (node->store.tx_begin_write (), i); + debug_assert (result == nano::block_status::progress); } + debug_assert (!node->init_error ()); node->start (); - nodes.push_back (node); + disconnected_nodes.push_back (node); + + logger.debug (nano::log::type::system, "Node started (disconnected): {}", node->get_node_id ().to_node_id ()); + return node; } diff --git a/nano/test_common/system.hpp b/nano/test_common/system.hpp index e0c9f127c..971319818 100644 --- a/nano/test_common/system.hpp +++ b/nano/test_common/system.hpp @@ -80,6 +80,7 @@ namespace test std::shared_ptr io_ctx; boost::asio::executor_work_guard io_guard; std::vector> nodes; + std::vector> disconnected_nodes; nano::stats stats; nano::logger logger{ "tests" }; nano::work_pool work{ nano::dev::network_params.network, std::max (nano::hardware_concurrency (), 1u) }; From f6ad9d7800b84c36de31b8871880ed17c2c14f6c Mon Sep 17 00:00:00 2001 From: clemahieu Date: Sun, 31 Mar 2024 15:37:47 +0200 Subject: [PATCH 122/128] Fixing shared->unique assignment and no return in control flow. (#4529) --- nano/nano_wallet/entry.cpp | 2 +- nano/rpc/rpc.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/nano/nano_wallet/entry.cpp b/nano/nano_wallet/entry.cpp index 5c3a0a29a..d91266df0 100644 --- a/nano/nano_wallet/entry.cpp +++ b/nano/nano_wallet/entry.cpp @@ -175,7 +175,7 @@ int run_wallet (QApplication & application, int argc, char * const * argv, std:: nano::ipc::ipc_server ipc (*node, config.rpc); std::unique_ptr rpc_process; - std::unique_ptr rpc; + std::shared_ptr rpc; std::unique_ptr rpc_handler; if (config.rpc_enable) { diff --git a/nano/rpc/rpc.cpp b/nano/rpc/rpc.cpp index 2ef3e2615..c2cae1e3e 100644 --- a/nano/rpc/rpc.cpp +++ b/nano/rpc/rpc.cpp @@ -97,4 +97,5 @@ std::shared_ptr nano::get_rpc (std::shared_ptr (io_ctx_a, config_a, rpc_handler_interface_a); } + return nullptr; } From cc20a5b1633b2fee9a7d39fafba242b313bcaaa6 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Fri, 29 Mar 2024 22:11:51 +0000 Subject: [PATCH 123/128] Making collect_container_info a member of nano::ledger. --- nano/node/node.cpp | 2 +- nano/secure/ledger.cpp | 8 ++++---- nano/secure/ledger.hpp | 3 +-- nano/secure/rep_weights.cpp | 2 +- nano/secure/rep_weights.hpp | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/nano/node/node.cpp b/nano/node/node.cpp index c21f9818d..6be3dc66b 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -554,7 +554,7 @@ std::unique_ptr nano::collect_container_info (no { auto composite = std::make_unique (name); composite->add_component (collect_container_info (node.work, "work")); - composite->add_component (collect_container_info (node.ledger, "ledger")); + composite->add_component (node.ledger.collect_container_info ("ledger")); composite->add_component (collect_container_info (node.active, "active")); composite->add_component (collect_container_info (node.bootstrap_initiator, "bootstrap_initiator")); composite->add_component (node.tcp_listener->collect_container_info ("tcp_listener")); diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 1f2f373a0..35965a297 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -1605,12 +1605,12 @@ nano::uncemented_info::uncemented_info (nano::block_hash const & cemented_fronti { } -std::unique_ptr nano::collect_container_info (ledger & ledger, std::string const & name) +std::unique_ptr nano::ledger::collect_container_info (std::string const & name) const { - auto count = ledger.bootstrap_weights.size (); - auto sizeof_element = sizeof (decltype (ledger.bootstrap_weights)::value_type); + auto count = bootstrap_weights.size (); + auto sizeof_element = sizeof (decltype (bootstrap_weights)::value_type); auto composite = std::make_unique (name); composite->add_component (std::make_unique (container_info{ "bootstrap_weights", count, sizeof_element })); - composite->add_component (ledger.cache.rep_weights.collect_container_info ("rep_weights")); + composite->add_component (cache.rep_weights.collect_container_info ("rep_weights")); return composite; } diff --git a/nano/secure/ledger.hpp b/nano/secure/ledger.hpp index 1d25da133..6ddf472fe 100644 --- a/nano/secure/ledger.hpp +++ b/nano/secure/ledger.hpp @@ -103,6 +103,7 @@ public: nano::receivable_iterator receivable_upper_bound (store::transaction const & tx, nano::account const & account) const; // Returns the next receivable entry for the account 'account' with hash greater than 'hash' nano::receivable_iterator receivable_upper_bound (store::transaction const & tx, nano::account const & account, nano::block_hash const & hash) const; + std::unique_ptr collect_container_info (std::string const & name) const; static nano::uint128_t const unit; nano::ledger_constants & constants; nano::store::component & store; @@ -119,6 +120,4 @@ private: void initialize (nano::generate_cache_flags const &); void confirm (nano::store::write_transaction const & transaction, nano::block const & block); }; - -std::unique_ptr collect_container_info (ledger & ledger, std::string const & name); } diff --git a/nano/secure/rep_weights.cpp b/nano/secure/rep_weights.cpp index 632ace73a..7b9622ae0 100644 --- a/nano/secure/rep_weights.cpp +++ b/nano/secure/rep_weights.cpp @@ -126,7 +126,7 @@ std::size_t nano::rep_weights::size () const return rep_amounts.size (); } -std::unique_ptr nano::rep_weights::collect_container_info (std::string const & name) +std::unique_ptr nano::rep_weights::collect_container_info (std::string const & name) const { size_t rep_amounts_count; diff --git a/nano/secure/rep_weights.hpp b/nano/secure/rep_weights.hpp index 214e22b8e..f90bf19b7 100644 --- a/nano/secure/rep_weights.hpp +++ b/nano/secure/rep_weights.hpp @@ -29,7 +29,7 @@ public: /* Only use this method when loading rep weights from the database table */ void copy_from (rep_weights & other_a); size_t size () const; - std::unique_ptr collect_container_info (std::string const &); + std::unique_ptr collect_container_info (std::string const &) const; private: mutable nano::mutex mutex; From 051e1d071159f96036d3d66e4e09c8ed263e3fca Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Sat, 30 Mar 2024 14:11:36 +0000 Subject: [PATCH 124/128] Encapsulate ledger_cache counts and return them from functions on nano::ledger --- nano/core_test/active_transactions.cpp | 22 ++-- nano/core_test/block_store.cpp | 7 +- nano/core_test/bootstrap.cpp | 44 +++---- nano/core_test/confirming_set.cpp | 10 +- nano/core_test/conflicts.cpp | 2 +- nano/core_test/election.cpp | 4 +- nano/core_test/ledger.cpp | 135 ++++++++++------------ nano/core_test/ledger_confirm.cpp | 12 +- nano/core_test/node.cpp | 58 +++++----- nano/core_test/rep_crawler.cpp | 2 +- nano/core_test/request_aggregator.cpp | 2 +- nano/core_test/wallet.cpp | 8 +- nano/core_test/wallets.cpp | 2 +- nano/nano_node/entry.cpp | 38 +++--- nano/node/active_transactions.cpp | 2 +- nano/node/bootstrap_ascending/service.cpp | 2 +- nano/node/json_handler.cpp | 10 +- nano/node/node.cpp | 14 +-- nano/qt/qt.cpp | 8 +- nano/rpc_test/rpc.cpp | 2 +- nano/secure/ledger.cpp | 20 ++++ nano/secure/ledger.hpp | 4 + nano/secure/ledger_cache.hpp | 14 +++ nano/slow_test/bootstrap.cpp | 4 +- nano/slow_test/node.cpp | 56 ++++----- nano/test_common/system.cpp | 2 +- 26 files changed, 256 insertions(+), 228 deletions(-) diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index 734eeeb70..086195ef7 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -149,7 +149,7 @@ TEST (active_transactions, confirm_frontier) std::shared_ptr election2; ASSERT_TIMELY (5s, election2 = node2.active.election (send->qualified_root ())); ASSERT_TIMELY (5s, nano::test::confirmed (node2, { send })); - ASSERT_TIMELY_EQ (5s, node2.ledger.cache.cemented_count, 2); + ASSERT_TIMELY_EQ (5s, node2.ledger.cemented_count (), 2); ASSERT_TIMELY (5s, node2.active.empty ()); ASSERT_GT (election2->confirmation_request_count, 0u); } @@ -486,9 +486,9 @@ TEST (inactive_votes_cache, election_start) ASSERT_EQ (nano::block_status::progress, node.process (send2)); ASSERT_EQ (nano::block_status::progress, node.process (open1)); ASSERT_EQ (nano::block_status::progress, node.process (open2)); - ASSERT_TIMELY_EQ (5s, 5, node.ledger.cache.block_count); + ASSERT_TIMELY_EQ (5s, 5, node.ledger.block_count ()); ASSERT_TRUE (node.active.empty ()); - ASSERT_EQ (1, node.ledger.cache.cemented_count); + ASSERT_EQ (1, node.ledger.cemented_count ()); // These blocks will be processed later auto send3 = send_block_builder.make_block () .previous (send2->hash ()) @@ -510,7 +510,7 @@ TEST (inactive_votes_cache, election_start) node.vote_processor.vote (vote1, std::make_shared (node, node)); ASSERT_TIMELY_EQ (5s, node.vote_cache.size (), 3); ASSERT_TRUE (node.active.empty ()); - ASSERT_EQ (1, node.ledger.cache.cemented_count); + ASSERT_EQ (1, node.ledger.cemented_count ()); // 2 votes are required to start election (dev network) auto vote2 = nano::test::make_vote (key2, { open1, open2, send4 }); @@ -523,7 +523,7 @@ TEST (inactive_votes_cache, election_start) auto vote0 = nano::test::make_final_vote (nano::dev::genesis_key, { open1, open2, send4 }); node.vote_processor.vote (vote0, std::make_shared (node, node)); ASSERT_TIMELY_EQ (5s, 0, node.active.size ()); - ASSERT_TIMELY_EQ (5s, 5, node.ledger.cache.cemented_count); + ASSERT_TIMELY_EQ (5s, 5, node.ledger.cemented_count ()); ASSERT_TRUE (nano::test::confirmed (node, { send1, send2, open1, open2 })); // A late block arrival also checks the inactive votes cache @@ -536,7 +536,7 @@ TEST (inactive_votes_cache, election_start) // send7 cannot be voted on but an election should be started from inactive votes ASSERT_FALSE (node.ledger.dependents_confirmed (node.store.tx_begin_read (), *send4)); node.process_active (send4); - ASSERT_TIMELY_EQ (5s, 7, node.ledger.cache.cemented_count); + ASSERT_TIMELY_EQ (5s, 7, node.ledger.cemented_count ()); } namespace nano @@ -829,8 +829,8 @@ TEST (active_transactions, fork_filter_cleanup) // how about node1 picking up "send1" from node2? we know it does because we assert at // the end that it is within node1's AEC, but why node1.block_count doesn't increase? // - ASSERT_TIMELY_EQ (5s, node2.ledger.cache.block_count, 2); - ASSERT_TIMELY_EQ (5s, node1.ledger.cache.block_count, 2); + ASSERT_TIMELY_EQ (5s, node2.ledger.block_count (), 2); + ASSERT_TIMELY_EQ (5s, node1.ledger.block_count (), 2); // Block is erased from the duplicate filter ASSERT_TIMELY (5s, node1.network.publish_filter.apply (send_block_bytes.data (), send_block_bytes.size ())); @@ -890,7 +890,7 @@ TEST (active_transactions, fork_replacement_tally) auto vote = nano::test::make_final_vote (nano::dev::genesis_key, { send, open }); node1.vote_processor.vote (vote, std::make_shared (node1, node1)); } - ASSERT_TIMELY_EQ (5s, node1.ledger.cache.cemented_count, 1 + 2 * reps_count); + ASSERT_TIMELY_EQ (5s, node1.ledger.cemented_count (), 1 + 2 * reps_count); nano::keypair key; auto send_last = builder.make_block () @@ -1050,8 +1050,8 @@ TEST (active_transactions, confirm_new) // Let node2 know about the block ASSERT_TIMELY (5s, node2.block (send->hash ())); // Wait confirmation - ASSERT_TIMELY (5s, node1.ledger.cache.cemented_count == 2); - ASSERT_TIMELY (5s, node2.ledger.cache.cemented_count == 2); + ASSERT_TIMELY (5s, node1.ledger.cemented_count () == 2); + ASSERT_TIMELY (5s, node2.ledger.cemented_count () == 2); } // Ensures votes are tallied on election::publish even if no vote is inserted through inactive_votes_cache diff --git a/nano/core_test/block_store.cpp b/nano/core_test/block_store.cpp index 42106adcb..d5689673b 100644 --- a/nano/core_test/block_store.cpp +++ b/nano/core_test/block_store.cpp @@ -902,9 +902,10 @@ TEST (block_store, cemented_count_cache) auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants); ASSERT_TRUE (!store->init_error ()); auto transaction (store->tx_begin_write ()); - nano::ledger_cache ledger_cache{ store->rep_weight }; - store->initialize (transaction, ledger_cache, nano::dev::constants); - ASSERT_EQ (1, ledger_cache.cemented_count); + nano::stats stats; + nano::ledger ledger (*store, stats, nano::dev::constants); + store->initialize (transaction, ledger.cache, nano::dev::constants); + ASSERT_EQ (1, ledger.cemented_count ()); } TEST (block_store, block_random) diff --git a/nano/core_test/bootstrap.cpp b/nano/core_test/bootstrap.cpp index 4442b7ede..97105c331 100644 --- a/nano/core_test/bootstrap.cpp +++ b/nano/core_test/bootstrap.cpp @@ -661,8 +661,8 @@ TEST (bootstrap_processor, push_diamond_pruning) ASSERT_TRUE (node1->store.pruned.exists (transaction, open->hash ())); ASSERT_TRUE (node1->ledger.block_exists (transaction, send2->hash ())); ASSERT_TRUE (node1->ledger.block_exists (transaction, receive->hash ())); - ASSERT_EQ (2, node1->ledger.cache.pruned_count); - ASSERT_EQ (5, node1->ledger.cache.block_count); + ASSERT_EQ (2, node1->ledger.pruned_count ()); + ASSERT_EQ (5, node1->ledger.block_count ()); } // 2nd bootstrap @@ -973,22 +973,22 @@ TEST (bootstrap_processor, lazy_hash_pruning) ASSERT_TIMELY (5s, node1->block_confirmed (change1->hash ())); ASSERT_TIMELY (5s, node1->block_confirmed (change2->hash ())); ASSERT_TIMELY (5s, node1->active.empty ()); - ASSERT_EQ (5, node1->ledger.cache.block_count); - ASSERT_EQ (5, node1->ledger.cache.cemented_count); + ASSERT_EQ (5, node1->ledger.block_count ()); + ASSERT_EQ (5, node1->ledger.cemented_count ()); // Pruning action node1->ledger_pruning (2, false); - ASSERT_EQ (9, node0->ledger.cache.block_count); - ASSERT_EQ (0, node0->ledger.cache.pruned_count); - ASSERT_EQ (5, node1->ledger.cache.block_count); - ASSERT_EQ (3, node1->ledger.cache.pruned_count); + ASSERT_EQ (9, node0->ledger.block_count ()); + ASSERT_EQ (0, node0->ledger.pruned_count ()); + ASSERT_EQ (5, node1->ledger.block_count ()); + ASSERT_EQ (3, node1->ledger.pruned_count ()); // Start lazy bootstrap with last block in chain known nano::test::establish_tcp (system, *node1, node0->network.endpoint ()); node1->bootstrap_initiator.bootstrap_lazy (receive3->hash (), true); // Check processed blocks - ASSERT_TIMELY_EQ (5s, node1->ledger.cache.block_count, 9); + ASSERT_TIMELY_EQ (5s, node1->ledger.block_count (), 9); ASSERT_TIMELY (5s, node1->balance (key2.pub) != 0); ASSERT_TIMELY (5s, !node1->bootstrap_initiator.in_progress ()); } @@ -1369,14 +1369,14 @@ TEST (bootstrap_processor, lazy_pruning_missing_block) // Confirm last block to prune previous ASSERT_TRUE (nano::test::start_elections (system, *node1, { send1, send2, open, state_open }, true)); ASSERT_TIMELY (5s, nano::test::confirmed (*node1, { send2, open, state_open })); - ASSERT_EQ (5, node1->ledger.cache.block_count); - ASSERT_EQ (5, node1->ledger.cache.cemented_count); + ASSERT_EQ (5, node1->ledger.block_count ()); + ASSERT_EQ (5, node1->ledger.cemented_count ()); // Pruning action, send1 should get pruned - ASSERT_EQ (0, node1->ledger.cache.pruned_count); + ASSERT_EQ (0, node1->ledger.pruned_count ()); node1->ledger_pruning (2, false); - ASSERT_EQ (1, node1->ledger.cache.pruned_count); - ASSERT_EQ (5, node1->ledger.cache.block_count); + ASSERT_EQ (1, node1->ledger.pruned_count ()); + ASSERT_EQ (5, node1->ledger.block_count ()); ASSERT_TRUE (node1->ledger.store.pruned.exists (node1->ledger.store.tx_begin_read (), send1->hash ())); ASSERT_TRUE (nano::test::exists (*node1, { send2, open, state_open })); @@ -1392,7 +1392,7 @@ TEST (bootstrap_processor, lazy_pruning_missing_block) ASSERT_TIMELY (5s, lazy_attempt->stopped || lazy_attempt->requeued_pulls >= 4); // Some blocks cannot be retrieved from pruned node - ASSERT_EQ (1, node2->ledger.cache.block_count); + ASSERT_EQ (1, node2->ledger.block_count ()); ASSERT_TRUE (nano::test::block_or_pruned_none_exists (*node2, { send1, send2, open, state_open })); { auto transaction (node2->store.tx_begin_read ()); @@ -1401,7 +1401,7 @@ TEST (bootstrap_processor, lazy_pruning_missing_block) // Insert missing block node2->process_active (send1); - ASSERT_TIMELY_EQ (5s, 3, node2->ledger.cache.block_count); + ASSERT_TIMELY_EQ (5s, 3, node2->ledger.block_count ()); ASSERT_TIMELY (5s, nano::test::exists (*node2, { send1, send2 })); ASSERT_TRUE (nano::test::block_or_pruned_none_exists (*node2, { open, state_open })); } @@ -2050,18 +2050,18 @@ TEST (bulk, genesis_pruning) ASSERT_TRUE (nano::test::start_elections (system, *node1, { send1 }, true)); ASSERT_TIMELY (5s, node1->active.active (send2->qualified_root ())); - ASSERT_EQ (0, node1->ledger.cache.pruned_count); + ASSERT_EQ (0, node1->ledger.pruned_count ()); ASSERT_TRUE (nano::test::start_elections (system, *node1, { send2 }, true)); ASSERT_TIMELY (5s, node1->active.active (send3->qualified_root ())); - ASSERT_EQ (0, node1->ledger.cache.pruned_count); + ASSERT_EQ (0, node1->ledger.pruned_count ()); ASSERT_TRUE (nano::test::start_elections (system, *node1, { send3 }, true)); ASSERT_TIMELY (5s, nano::test::confirmed (*node1, { send3 })); node1->ledger_pruning (2, false); - ASSERT_EQ (2, node1->ledger.cache.pruned_count); - ASSERT_EQ (4, node1->ledger.cache.block_count); + ASSERT_EQ (2, node1->ledger.pruned_count ()); + ASSERT_EQ (4, node1->ledger.block_count ()); ASSERT_TRUE (node1->ledger.store.pruned.exists (node1->ledger.store.tx_begin_read (), send1->hash ())); ASSERT_FALSE (nano::test::exists (*node1, { send1 })); ASSERT_TRUE (node1->ledger.store.pruned.exists (node1->ledger.store.tx_begin_read (), send2->hash ())); @@ -2077,7 +2077,7 @@ TEST (bulk, genesis_pruning) ASSERT_TIMELY (5s, !node2->bootstrap_initiator.in_progress ()); // node2 still missing blocks - ASSERT_EQ (1, node2->ledger.cache.block_count); + ASSERT_EQ (1, node2->ledger.block_count ()); { auto transaction (node2->store.tx_begin_write ()); node2->unchecked.clear (); @@ -2086,7 +2086,7 @@ TEST (bulk, genesis_pruning) // Insert pruned blocks node2->process_active (send1); node2->process_active (send2); - ASSERT_TIMELY_EQ (5s, 3, node2->ledger.cache.block_count); + ASSERT_TIMELY_EQ (5s, 3, node2->ledger.block_count ()); // New bootstrap to sync up everything ASSERT_TIMELY_EQ (5s, node2->bootstrap_initiator.connections->connections_count, 0); diff --git a/nano/core_test/confirming_set.cpp b/nano/core_test/confirming_set.cpp index d7137eb54..4b5b8676b 100644 --- a/nano/core_test/confirming_set.cpp +++ b/nano/core_test/confirming_set.cpp @@ -46,7 +46,7 @@ TEST (confirming_set, process_one) std::unique_lock lock{ mutex }; ASSERT_TRUE (condition.wait_for (lock, 5s, [&] () { return count == 1; })); ASSERT_EQ (1, ctx.stats ().count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (2, ctx.ledger ().cache.cemented_count); + ASSERT_EQ (2, ctx.ledger ().cemented_count ()); } TEST (confirming_set, process_multiple) @@ -64,7 +64,7 @@ TEST (confirming_set, process_multiple) std::unique_lock lock{ mutex }; ASSERT_TRUE (condition.wait_for (lock, 5s, [&] () { return count == 2; })); ASSERT_EQ (2, ctx.stats ().count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (3, ctx.ledger ().cache.cemented_count); + ASSERT_EQ (3, ctx.ledger ().cemented_count ()); } TEST (confirmation_callback, observer_callbacks) @@ -109,7 +109,7 @@ TEST (confirmation_callback, observer_callbacks) ASSERT_TIMELY_EQ (5s, 2, node->ledger.stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); ASSERT_EQ (2, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (3, node->ledger.cache.cemented_count); + ASSERT_EQ (3, node->ledger.cemented_count ()); ASSERT_EQ (0, node->active.election_winner_details_size ()); } @@ -188,7 +188,7 @@ TEST (confirmation_callback, confirmed_history) ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_quorum, nano::stat::dir::out)); ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::inactive_conf_height, nano::stat::dir::out)); ASSERT_EQ (2, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (3, node->ledger.cache.cemented_count); + ASSERT_EQ (3, node->ledger.cemented_count ()); ASSERT_EQ (0, node->active.election_winner_details_size ()); } @@ -251,7 +251,7 @@ TEST (confirmation_callback, dependent_election) ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_quorum, nano::stat::dir::out)); ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_conf_height, nano::stat::dir::out)); ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::inactive_conf_height, nano::stat::dir::out)); - ASSERT_EQ (4, node->ledger.cache.cemented_count); + ASSERT_EQ (4, node->ledger.cemented_count ()); ASSERT_EQ (0, node->active.election_winner_details_size ()); } diff --git a/nano/core_test/conflicts.cpp b/nano/core_test/conflicts.cpp index 323154046..a4175a7f3 100644 --- a/nano/core_test/conflicts.cpp +++ b/nano/core_test/conflicts.cpp @@ -99,7 +99,7 @@ TEST (conflicts, add_two) // create 2 new accounts, that receive 1 raw each, all blocks are force confirmed auto [send1, open1] = nano::test::setup_new_account (system, *node, 1, gk, key1, gk.pub, true); auto [send2, open2] = nano::test::setup_new_account (system, *node, 1, gk, key2, gk.pub, true); - ASSERT_EQ (5, node->ledger.cache.cemented_count); + ASSERT_EQ (5, node->ledger.cemented_count ()); // send 1 raw to account key3 from key1 auto send_a = nano::state_block_builder () diff --git a/nano/core_test/election.cpp b/nano/core_test/election.cpp index ac4c47a63..140052cee 100644 --- a/nano/core_test/election.cpp +++ b/nano/core_test/election.cpp @@ -238,13 +238,13 @@ TEST (election, quorum_minimum_update_weight_before_quorum_checks) .work (*system.work.generate (open1->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, node1.process (send2)); - ASSERT_TIMELY_EQ (5s, node1.ledger.cache.block_count, 4); + ASSERT_TIMELY_EQ (5s, node1.ledger.block_count (), 4); node_config.peering_port = system.get_available_port (); auto & node2 = *system.add_node (node_config); system.wallet (1)->insert_adhoc (key1.prv); - ASSERT_TIMELY_EQ (10s, node2.ledger.cache.block_count, 4); + ASSERT_TIMELY_EQ (10s, node2.ledger.block_count (), 4); std::shared_ptr election; ASSERT_TIMELY (5s, (election = node1.active.election (send1->qualified_root ())) != nullptr); diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index af04a99be..e0f9d69aa 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -56,7 +56,7 @@ TEST (ledger, genesis_balance) ASSERT_EQ (nano::dev::constants.genesis_amount, balance); auto info = ledger.account_info (transaction, nano::dev::genesis_key.pub); ASSERT_TRUE (info); - ASSERT_EQ (1, ledger.cache.account_count); + ASSERT_EQ (1, ledger.account_count ()); // Frontier time should have been updated when genesis balance was added ASSERT_GE (nano::seconds_since_epoch (), info->modified); ASSERT_LT (nano::seconds_since_epoch () - info->modified, 10); @@ -189,7 +189,7 @@ TEST (ledger, process_send) ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key (key2.pub, hash1))); ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.account_balance (transaction, nano::dev::genesis_key.pub)); ASSERT_EQ (0, ledger.account_receivable (transaction, key2.pub)); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, process_receive) @@ -272,7 +272,7 @@ TEST (ledger, process_receive) ASSERT_TRUE (pending1); ASSERT_EQ (nano::dev::genesis_key.pub, pending1->source); ASSERT_EQ (25, pending1->amount.number ()); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, rollback_receiver) @@ -320,7 +320,7 @@ TEST (ledger, rollback_receiver) ASSERT_EQ (0, ledger.weight (key2.pub)); ASSERT_EQ (0, ledger.weight (key3.pub)); ASSERT_FALSE (ledger.account_info (transaction, key2.pub)); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ key2.pub, hash1 })); } @@ -1626,7 +1626,7 @@ TEST (ledger, fail_open_fork_previous) .work (*pool.generate (key1.pub)) .build (); ASSERT_EQ (nano::block_status::fork, ledger.process (transaction, block4)); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, fail_open_account_mismatch) @@ -1657,7 +1657,7 @@ TEST (ledger, fail_open_account_mismatch) .work (*pool.generate (badkey.pub)) .build (); ASSERT_NE (nano::block_status::progress, ledger.process (transaction, block2)); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, fail_receive_old) @@ -2265,7 +2265,7 @@ TEST (ledger, bootstrap_rep_weight) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send)); } - ASSERT_EQ (2, ledger.cache.block_count); + ASSERT_EQ (2, ledger.block_count ()); { ledger.bootstrap_weight_max_blocks = 3; ledger.bootstrap_weights[key2.pub] = 1000; @@ -2286,7 +2286,7 @@ TEST (ledger, bootstrap_rep_weight) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send)); } - ASSERT_EQ (3, ledger.cache.block_count); + ASSERT_EQ (3, ledger.block_count ()); ASSERT_EQ (0, ledger.weight (key2.pub)); } @@ -2452,7 +2452,7 @@ TEST (ledger, state_send_receive) ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, receive1->hash ())); ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub)); ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); ASSERT_EQ (3, receive2->sideband ().height); ASSERT_FALSE (receive2->is_send ()); ASSERT_TRUE (receive2->is_receive ()); @@ -2588,7 +2588,7 @@ TEST (ledger, state_open) ASSERT_EQ (nano::Gxrb_ratio, ledger.balance (transaction, open1->hash ())); ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, open1->hash ())); ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub)); - ASSERT_EQ (ledger.cache.account_count, store.account.count (transaction)); + ASSERT_EQ (ledger.account_count (), store.account.count (transaction)); ASSERT_EQ (1, open2->sideband ().height); ASSERT_FALSE (open2->is_send ()); ASSERT_TRUE (open2->is_receive ()); @@ -2918,7 +2918,7 @@ TEST (ledger, state_state_open_fork) .build (); ASSERT_EQ (nano::block_status::fork, ledger.process (transaction, open2)); ASSERT_EQ (open1->root (), open2->root ()); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, state_open_previous_fail) @@ -3195,7 +3195,7 @@ TEST (ledger, state_rollback_send) ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub)); ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); ASSERT_FALSE (ledger.successor (transaction, nano::dev::genesis->hash ())); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, state_rollback_receive) @@ -3237,7 +3237,7 @@ TEST (ledger, state_rollback_receive) ASSERT_FALSE (ledger.block_exists (transaction, receive1->hash ())); ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.account_balance (transaction, nano::dev::genesis_key.pub)); ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::dev::genesis_key.pub)); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, state_rollback_received_send) @@ -3280,7 +3280,7 @@ TEST (ledger, state_rollback_received_send) ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub)); ASSERT_EQ (0, ledger.account_balance (transaction, key.pub)); ASSERT_EQ (0, ledger.weight (key.pub)); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, state_rep_change_rollback) @@ -3349,7 +3349,7 @@ TEST (ledger, state_open_rollback) ASSERT_TRUE (info); ASSERT_EQ (nano::dev::genesis_key.pub, info->source); ASSERT_EQ (nano::Gxrb_ratio, info->amount.number ()); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, state_send_change_rollback) @@ -3377,7 +3377,7 @@ TEST (ledger, state_send_change_rollback) ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.account_balance (transaction, nano::dev::genesis_key.pub)); ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub)); ASSERT_EQ (0, ledger.weight (rep.pub)); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, state_receive_change_rollback) @@ -3416,7 +3416,7 @@ TEST (ledger, state_receive_change_rollback) ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.account_balance (transaction, nano::dev::genesis_key.pub)); ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::dev::genesis_key.pub)); ASSERT_EQ (0, ledger.weight (rep.pub)); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, epoch_blocks_v1_general) @@ -3932,7 +3932,7 @@ TEST (ledger, epoch_blocks_receive_upgrade) ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, epoch4)); ASSERT_EQ (nano::epoch::epoch_2, epoch4->sideband ().details.epoch); ASSERT_EQ (nano::epoch::epoch_0, epoch4->sideband ().source_epoch); // Not used for epoch blocks - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, epoch_blocks_fork) @@ -4794,12 +4794,12 @@ TEST (ledger, cache) auto genesis_weight = nano::dev::constants.genesis_amount - i; auto pruned_count = i; - auto cache_check = [&, i] (nano::ledger_cache const & cache_a) { - ASSERT_EQ (account_count, cache_a.account_count); - ASSERT_EQ (block_count, cache_a.block_count); - ASSERT_EQ (cemented_count, cache_a.cemented_count); - ASSERT_EQ (genesis_weight, cache_a.rep_weights.representation_get (nano::dev::genesis_key.pub)); - ASSERT_EQ (pruned_count, cache_a.pruned_count); + auto cache_check = [&, i] (nano::ledger const & ledger) { + ASSERT_EQ (account_count, ledger.account_count ()); + ASSERT_EQ (block_count, ledger.block_count ()); + ASSERT_EQ (cemented_count, ledger.cemented_count ()); + ASSERT_EQ (genesis_weight, ledger.cache.rep_weights.representation_get (nano::dev::genesis_key.pub)); + ASSERT_EQ (pruned_count, ledger.pruned_count ()); }; nano::keypair key; @@ -4829,8 +4829,8 @@ TEST (ledger, cache) ++block_count; --genesis_weight; - cache_check (ledger.cache); - cache_check (nano::ledger (store, stats, nano::dev::constants).cache); + cache_check (ledger); + cache_check (nano::ledger (store, stats, nano::dev::constants)); { auto transaction (store.tx_begin_write ()); @@ -4839,47 +4839,36 @@ TEST (ledger, cache) ++block_count; ++account_count; - cache_check (ledger.cache); - cache_check (nano::ledger (store, stats, nano::dev::constants).cache); + cache_check (ledger); + cache_check (nano::ledger (store, stats, nano::dev::constants)); { auto transaction (store.tx_begin_write ()); - nano::confirmation_height_info height; - ASSERT_FALSE (ledger.store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, height)); - ++height.height; - height.frontier = send->hash (); - ledger.store.confirmation_height.put (transaction, nano::dev::genesis_key.pub, height); + ledger.confirm (transaction, send->hash ()); ASSERT_TRUE (ledger.block_confirmed (transaction, send->hash ())); - ++ledger.cache.cemented_count; } ++cemented_count; - cache_check (ledger.cache); - cache_check (nano::ledger (store, stats, nano::dev::constants).cache); + cache_check (ledger); + cache_check (nano::ledger (store, stats, nano::dev::constants)); { auto transaction (store.tx_begin_write ()); - nano::confirmation_height_info height; - ledger.store.confirmation_height.get (transaction, key.pub, height); - height.height += 1; - height.frontier = open->hash (); - ledger.store.confirmation_height.put (transaction, key.pub, height); + ledger.confirm (transaction, open->hash ()); ASSERT_TRUE (ledger.block_confirmed (transaction, open->hash ())); - ++ledger.cache.cemented_count; } ++cemented_count; - cache_check (ledger.cache); - cache_check (nano::ledger (store, stats, nano::dev::constants).cache); + cache_check (ledger); + cache_check (nano::ledger (store, stats, nano::dev::constants)); { auto transaction (store.tx_begin_write ()); - ledger.store.pruned.put (transaction, open->hash ()); - ++ledger.cache.pruned_count; + ledger.pruning_action (transaction, open->hash (), 1); } ++pruned_count; - cache_check (ledger.cache); - cache_check (nano::ledger (store, stats, nano::dev::constants).cache); + cache_check (ledger); + cache_check (nano::ledger (store, stats, nano::dev::constants)); } } @@ -4962,9 +4951,9 @@ TEST (ledger, pruning_action) ASSERT_EQ (1, ledger.pruning_action (transaction, send2->hash (), 1)); ASSERT_TRUE (store->pruned.exists (transaction, send2->hash ())); ASSERT_FALSE (store->block.exists (transaction, send2->hash ())); - ASSERT_EQ (store->account.count (transaction), ledger.cache.account_count); - ASSERT_EQ (store->pruned.count (transaction), ledger.cache.pruned_count); - ASSERT_EQ (store->block.count (transaction), ledger.cache.block_count - ledger.cache.pruned_count); + ASSERT_EQ (store->account.count (transaction), ledger.account_count ()); + ASSERT_EQ (store->pruned.count (transaction), ledger.pruned_count ()); + ASSERT_EQ (store->block.count (transaction), ledger.block_count () - ledger.pruned_count ()); } TEST (ledger, pruning_large_chain) @@ -5016,8 +5005,8 @@ TEST (ledger, pruning_large_chain) ASSERT_TRUE (store->pruned.exists (transaction, last_hash)); ASSERT_TRUE (store->block.exists (transaction, nano::dev::genesis->hash ())); ASSERT_FALSE (store->block.exists (transaction, last_hash)); - ASSERT_EQ (store->pruned.count (transaction), ledger.cache.pruned_count); - ASSERT_EQ (store->block.count (transaction), ledger.cache.block_count - ledger.cache.pruned_count); + ASSERT_EQ (store->pruned.count (transaction), ledger.pruned_count ()); + ASSERT_EQ (store->block.count (transaction), ledger.block_count () - ledger.pruned_count ()); ASSERT_EQ (send_receive_pairs * 2, store->pruned.count (transaction)); ASSERT_EQ (1, store->block.count (transaction)); // Genesis } @@ -5094,8 +5083,8 @@ TEST (ledger, pruning_source_rollback) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive1)); ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); - ASSERT_EQ (2, ledger.cache.pruned_count); - ASSERT_EQ (5, ledger.cache.block_count); + ASSERT_EQ (2, ledger.pruned_count ()); + ASSERT_EQ (5, ledger.block_count ()); // Rollback receive block ASSERT_FALSE (ledger.rollback (transaction, receive1->hash ())); auto info2 = ledger.pending_info (transaction, nano::pending_key (nano::dev::genesis_key.pub, send1->hash ())); @@ -5106,8 +5095,8 @@ TEST (ledger, pruning_source_rollback) // Process receive block again ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive1)); ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); - ASSERT_EQ (2, ledger.cache.pruned_count); - ASSERT_EQ (5, ledger.cache.block_count); + ASSERT_EQ (2, ledger.pruned_count ()); + ASSERT_EQ (5, ledger.block_count ()); } TEST (ledger, pruning_source_rollback_legacy) @@ -5182,8 +5171,8 @@ TEST (ledger, pruning_source_rollback_legacy) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive1)); ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); - ASSERT_EQ (2, ledger.cache.pruned_count); - ASSERT_EQ (5, ledger.cache.block_count); + ASSERT_EQ (2, ledger.pruned_count ()); + ASSERT_EQ (5, ledger.block_count ()); // Rollback receive block ASSERT_FALSE (ledger.rollback (transaction, receive1->hash ())); auto info3 = ledger.pending_info (transaction, nano::pending_key (nano::dev::genesis_key.pub, send1->hash ())); @@ -5194,8 +5183,8 @@ TEST (ledger, pruning_source_rollback_legacy) // Process receive block again ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive1)); ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); - ASSERT_EQ (2, ledger.cache.pruned_count); - ASSERT_EQ (5, ledger.cache.block_count); + ASSERT_EQ (2, ledger.pruned_count ()); + ASSERT_EQ (5, ledger.block_count ()); // Receiving pruned block (open) auto open1 = builder .open () @@ -5207,8 +5196,8 @@ TEST (ledger, pruning_source_rollback_legacy) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open1)); ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ key1.pub, send2->hash () })); - ASSERT_EQ (2, ledger.cache.pruned_count); - ASSERT_EQ (6, ledger.cache.block_count); + ASSERT_EQ (2, ledger.pruned_count ()); + ASSERT_EQ (6, ledger.block_count ()); // Rollback open block ASSERT_FALSE (ledger.rollback (transaction, open1->hash ())); auto info4 = ledger.pending_info (transaction, nano::pending_key (key1.pub, send2->hash ())); @@ -5219,8 +5208,8 @@ TEST (ledger, pruning_source_rollback_legacy) // Process open block again ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open1)); ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ key1.pub, send2->hash () })); - ASSERT_EQ (2, ledger.cache.pruned_count); - ASSERT_EQ (6, ledger.cache.block_count); + ASSERT_EQ (2, ledger.pruned_count ()); + ASSERT_EQ (6, ledger.block_count ()); } TEST (ledger, pruning_process_error) @@ -5246,8 +5235,8 @@ TEST (ledger, pruning_process_error) .work (*pool.generate (nano::dev::genesis->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send1)); - ASSERT_EQ (0, ledger.cache.pruned_count); - ASSERT_EQ (2, ledger.cache.block_count); + ASSERT_EQ (0, ledger.pruned_count ()); + ASSERT_EQ (2, ledger.block_count ()); // Pruning action for latest block (not valid action) ASSERT_EQ (1, ledger.pruning_action (transaction, send1->hash (), 1)); ASSERT_FALSE (store->block.exists (transaction, send1->hash ())); @@ -5266,8 +5255,8 @@ TEST (ledger, pruning_process_error) .work (*pool.generate (send1->hash ())) .build (); ASSERT_EQ (nano::block_status::gap_previous, ledger.process (transaction, send2)); - ASSERT_EQ (1, ledger.cache.pruned_count); - ASSERT_EQ (2, ledger.cache.block_count); + ASSERT_EQ (1, ledger.pruned_count ()); + ASSERT_EQ (2, ledger.block_count ()); } TEST (ledger, pruning_legacy_blocks) @@ -5350,10 +5339,10 @@ TEST (ledger, pruning_legacy_blocks) ASSERT_FALSE (store->block.exists (transaction, open1->hash ())); ASSERT_TRUE (store->pruned.exists (transaction, open1->hash ())); ASSERT_TRUE (store->block.exists (transaction, send3->hash ())); - ASSERT_EQ (4, ledger.cache.pruned_count); - ASSERT_EQ (7, ledger.cache.block_count); - ASSERT_EQ (store->pruned.count (transaction), ledger.cache.pruned_count); - ASSERT_EQ (store->block.count (transaction), ledger.cache.block_count - ledger.cache.pruned_count); + ASSERT_EQ (4, ledger.pruned_count ()); + ASSERT_EQ (7, ledger.block_count ()); + ASSERT_EQ (store->pruned.count (transaction), ledger.pruned_count ()); + ASSERT_EQ (store->block.count (transaction), ledger.block_count () - ledger.pruned_count ()); } TEST (ledger, pruning_safe_functions) diff --git a/nano/core_test/ledger_confirm.cpp b/nano/core_test/ledger_confirm.cpp index f22e36b0e..ee3a4c1e8 100644 --- a/nano/core_test/ledger_confirm.cpp +++ b/nano/core_test/ledger_confirm.cpp @@ -48,7 +48,7 @@ TEST (ledger_confirm, single) ASSERT_TRUE (node->ledger.rollback (transaction, latest1)); ASSERT_TRUE (node->ledger.rollback (transaction, send1->hash ())); ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (2, node->ledger.cache.cemented_count); + ASSERT_EQ (2, node->ledger.cemented_count ()); } TEST (ledger_confirm, multiple_accounts) @@ -193,7 +193,7 @@ TEST (ledger_confirm, multiple_accounts) auto confirmed = node->ledger.confirm (transaction, receive3->hash ()); ASSERT_EQ (10, confirmed.size ()); ASSERT_EQ (10, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (11, node->ledger.cache.cemented_count); + ASSERT_EQ (11, node->ledger.cemented_count ()); ASSERT_TRUE (node->ledger.block_confirmed (transaction, receive3->hash ())); ASSERT_EQ (4, node->ledger.account_info (transaction, nano::dev::genesis_key.pub).value ().block_count); @@ -343,7 +343,7 @@ TEST (ledger_confirm, send_receive_between_2_accounts) auto confirmed = node->ledger.confirm (transaction, receive4->hash ()); ASSERT_EQ (10, confirmed.size ()); ASSERT_EQ (10, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (11, node->ledger.cache.cemented_count); + ASSERT_EQ (11, node->ledger.cemented_count ()); ASSERT_TRUE (node->ledger.block_confirmed (transaction, receive4->hash ())); ASSERT_EQ (7, node->ledger.account_info (transaction, nano::dev::genesis_key.pub).value ().block_count); @@ -440,7 +440,7 @@ TEST (ledger_confirm, send_receive_self) ASSERT_EQ (8, node->ledger.account_info (transaction, nano::dev::genesis_key.pub).value ().block_count); ASSERT_EQ (7, node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().height); ASSERT_EQ (receive3->hash (), node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().frontier); - ASSERT_EQ (7, node->ledger.cache.cemented_count); + ASSERT_EQ (7, node->ledger.cemented_count ()); } TEST (ledger_confirm, all_block_types) @@ -660,7 +660,7 @@ TEST (ledger_confirm, all_block_types) auto confirmed = node->ledger.confirm (transaction, state_send2->hash ()); ASSERT_EQ (15, confirmed.size ()); ASSERT_EQ (15, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (16, node->ledger.cache.cemented_count); + ASSERT_EQ (16, node->ledger.cemented_count ()); ASSERT_TRUE (node->ledger.block_confirmed (transaction, state_send2->hash ())); nano::confirmation_height_info confirmation_height_info; @@ -749,7 +749,7 @@ TEST (ledger_confirm, observers) node1->ledger.confirm (transaction, send1->hash ()); ASSERT_TRUE (node1->ledger.block_confirmed (transaction, send1->hash ())); ASSERT_EQ (1, node1->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (2, node1->ledger.cache.cemented_count); + ASSERT_EQ (2, node1->ledger.cemented_count ()); } TEST (ledger_confirm, election_winner_details_clearing_node_process_confirmed) diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index d396a2cba..2aa0526ab 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -277,9 +277,9 @@ TEST (node, auto_bootstrap) ASSERT_TIMELY (10s, !node1->bootstrap_initiator.in_progress ()); ASSERT_TRUE (node1->ledger.block_or_pruned_exists (send1->hash ())); // Wait block receive - ASSERT_TIMELY_EQ (5s, node1->ledger.cache.block_count, 3); + ASSERT_TIMELY_EQ (5s, node1->ledger.block_count (), 3); // Confirmation for all blocks - ASSERT_TIMELY_EQ (5s, node1->ledger.cache.cemented_count, 3); + ASSERT_TIMELY_EQ (5s, node1->ledger.cemented_count (), 3); } TEST (node, auto_bootstrap_reverse) @@ -427,7 +427,7 @@ TEST (node, search_receivable_pruned) // Confirmation ASSERT_TIMELY (10s, node1->active.empty () && node2->active.empty ()); ASSERT_TIMELY (5s, node1->ledger.block_confirmed (node1->store.tx_begin_read (), send2->hash ())); - ASSERT_TIMELY_EQ (5s, node2->ledger.cache.cemented_count, 3); + ASSERT_TIMELY_EQ (5s, node2->ledger.cemented_count (), 3); system.wallet (0)->store.erase (node1->wallets.tx_begin_write (), nano::dev::genesis_key.pub); // Pruning @@ -435,7 +435,7 @@ TEST (node, search_receivable_pruned) auto transaction (node2->store.tx_begin_write ()); ASSERT_EQ (1, node2->ledger.pruning_action (transaction, send1->hash (), 1)); } - ASSERT_EQ (1, node2->ledger.cache.pruned_count); + ASSERT_EQ (1, node2->ledger.pruned_count ()); ASSERT_TRUE (node2->ledger.block_or_pruned_exists (send1->hash ())); // true for pruned // Receive pruned block @@ -1277,7 +1277,7 @@ TEST (node, DISABLED_broadcast_elected) auto election (node->active.election (block->qualified_root ())); ASSERT_NE (nullptr, election); election->force_confirm (); - ASSERT_TIMELY_EQ (5s, 4, node->ledger.cache.cemented_count) + ASSERT_TIMELY_EQ (5s, 4, node->ledger.cemented_count ()) } system.wallet (0)->insert_adhoc (rep_big.prv); @@ -1592,7 +1592,7 @@ TEST (node, unconfirmed_send) ASSERT_TIMELY (5s, node1.block_confirmed (send2->hash ())); ASSERT_TIMELY (5s, node2.block_confirmed (send3->hash ())); ASSERT_TIMELY (5s, node1.block_confirmed (send3->hash ())); - ASSERT_TIMELY_EQ (5s, node2.ledger.cache.cemented_count, 7); + ASSERT_TIMELY_EQ (5s, node2.ledger.cemented_count (), 7); ASSERT_TIMELY_EQ (5s, node1.balance (nano::dev::genesis_key.pub), nano::dev::constants.genesis_amount); } @@ -1914,7 +1914,7 @@ TEST (node, local_votes_cache) std::shared_ptr election; ASSERT_TIMELY (5s, election = node.active.election (send2->qualified_root ())); election->force_confirm (); - ASSERT_TIMELY_EQ (3s, node.ledger.cache.cemented_count, 3); + ASSERT_TIMELY_EQ (3s, node.ledger.cemented_count (), 3); system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv); nano::confirm_req message1{ nano::dev::network_params.network, send1->hash (), send1->root () }; nano::confirm_req message2{ nano::dev::network_params.network, send2->hash (), send2->root () }; @@ -3081,7 +3081,7 @@ TEST (node, rollback_vote_self) // Process and mark the first 2 blocks as confirmed to allow voting ASSERT_TRUE (nano::test::process (node, { send1, open })); ASSERT_TRUE (nano::test::start_elections (system, node, { send1, open }, true)); - ASSERT_TIMELY_EQ (5s, node.ledger.cache.cemented_count, 3); + ASSERT_TIMELY_EQ (5s, node.ledger.cemented_count (), 3); // wait until the rep weights have caught up with the weight transfer ASSERT_TIMELY_EQ (5s, nano::dev::constants.genesis_amount / 2, node.weight (key.pub)); @@ -3363,7 +3363,7 @@ TEST (node, dependency_graph) { key3_receive->hash (), { key3_open->hash (), key1_send2->hash () } }, { key3_epoch->hash (), { key3_receive->hash () } }, }; - ASSERT_EQ (node.ledger.cache.block_count - 2, dependency_graph.size ()); + ASSERT_EQ (node.ledger.block_count () - 2, dependency_graph.size ()); // Start an election for the first block of the dependency graph, and ensure all blocks are eventually confirmed system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv); @@ -3389,9 +3389,9 @@ TEST (node, dependency_graph) }); EXPECT_FALSE (error); - return error || node.ledger.cache.cemented_count == node.ledger.cache.block_count; + return error || node.ledger.cemented_count () == node.ledger.block_count (); })); - ASSERT_EQ (node.ledger.cache.cemented_count, node.ledger.cache.block_count); + ASSERT_EQ (node.ledger.cemented_count (), node.ledger.block_count ()); ASSERT_TIMELY (5s, node.active.empty ()); } @@ -3558,8 +3558,8 @@ TEST (node, dependency_graph_frontier) ASSERT_TIMELY (10s, node2.active.active (gen_send1->qualified_root ())); node1.start_election (gen_send1); - ASSERT_TIMELY_EQ (15s, node1.ledger.cache.cemented_count, node1.ledger.cache.block_count); - ASSERT_TIMELY_EQ (15s, node2.ledger.cache.cemented_count, node2.ledger.cache.block_count); + ASSERT_TIMELY_EQ (15s, node1.ledger.cemented_count (), node1.ledger.block_count ()); + ASSERT_TIMELY_EQ (15s, node2.ledger.cemented_count (), node2.ledger.block_count ()); } namespace nano @@ -3733,11 +3733,11 @@ TEST (node, pruning_automatic) ASSERT_TIMELY (5s, node1.block_confirmed (send2->hash ())); // Check pruning result - ASSERT_EQ (3, node1.ledger.cache.block_count); - ASSERT_TIMELY_EQ (5s, node1.ledger.cache.pruned_count, 1); + ASSERT_EQ (3, node1.ledger.block_count ()); + ASSERT_TIMELY_EQ (5s, node1.ledger.pruned_count (), 1); ASSERT_TIMELY_EQ (5s, node1.store.pruned.count (node1.store.tx_begin_read ()), 1); - ASSERT_EQ (1, node1.ledger.cache.pruned_count); - ASSERT_EQ (3, node1.ledger.cache.block_count); + ASSERT_EQ (1, node1.ledger.pruned_count ()); + ASSERT_EQ (3, node1.ledger.block_count ()); ASSERT_TRUE (nano::test::block_or_pruned_all_exists (node1, { nano::dev::genesis, send1, send2 })); } @@ -3784,19 +3784,19 @@ TEST (node, pruning_age) ASSERT_TIMELY (5s, node1.block_confirmed (send2->hash ())); // Three blocks in total, nothing pruned yet - ASSERT_EQ (0, node1.ledger.cache.pruned_count); - ASSERT_EQ (3, node1.ledger.cache.block_count); + ASSERT_EQ (0, node1.ledger.pruned_count ()); + ASSERT_EQ (3, node1.ledger.block_count ()); // Pruning with default age 1 day node1.ledger_pruning (1, true); - ASSERT_EQ (0, node1.ledger.cache.pruned_count); - ASSERT_EQ (3, node1.ledger.cache.block_count); + ASSERT_EQ (0, node1.ledger.pruned_count ()); + ASSERT_EQ (3, node1.ledger.block_count ()); // Pruning with max age 0 node1.config.max_pruning_age = std::chrono::seconds{ 0 }; node1.ledger_pruning (1, true); - ASSERT_EQ (1, node1.ledger.cache.pruned_count); - ASSERT_EQ (3, node1.ledger.cache.block_count); + ASSERT_EQ (1, node1.ledger.pruned_count ()); + ASSERT_EQ (3, node1.ledger.block_count ()); ASSERT_TRUE (nano::test::block_or_pruned_all_exists (node1, { nano::dev::genesis, send1, send2 })); } @@ -3845,19 +3845,19 @@ TEST (node, pruning_depth) ASSERT_TIMELY (5s, node1.block_confirmed (send2->hash ())); // Three blocks in total, nothing pruned yet - ASSERT_EQ (0, node1.ledger.cache.pruned_count); - ASSERT_EQ (3, node1.ledger.cache.block_count); + ASSERT_EQ (0, node1.ledger.pruned_count ()); + ASSERT_EQ (3, node1.ledger.block_count ()); // Pruning with default depth (unlimited) node1.ledger_pruning (1, true); - ASSERT_EQ (0, node1.ledger.cache.pruned_count); - ASSERT_EQ (3, node1.ledger.cache.block_count); + ASSERT_EQ (0, node1.ledger.pruned_count ()); + ASSERT_EQ (3, node1.ledger.block_count ()); // Pruning with max depth 1 node1.config.max_pruning_depth = 1; node1.ledger_pruning (1, true); - ASSERT_EQ (1, node1.ledger.cache.pruned_count); - ASSERT_EQ (3, node1.ledger.cache.block_count); + ASSERT_EQ (1, node1.ledger.pruned_count ()); + ASSERT_EQ (3, node1.ledger.block_count ()); ASSERT_TRUE (nano::test::block_or_pruned_all_exists (node1, { nano::dev::genesis, send1, send2 })); } diff --git a/nano/core_test/rep_crawler.cpp b/nano/core_test/rep_crawler.cpp index 1addde26c..293733a76 100644 --- a/nano/core_test/rep_crawler.cpp +++ b/nano/core_test/rep_crawler.cpp @@ -252,7 +252,7 @@ TEST (rep_crawler, recently_confirmed) { nano::test::system system (1); auto & node1 (*system.nodes[0]); - ASSERT_EQ (1, node1.ledger.cache.block_count); + ASSERT_EQ (1, node1.ledger.block_count ()); auto const block = nano::dev::genesis; node1.active.recently_confirmed.put (block->qualified_root (), block->hash ()); auto & node2 (*system.add_node ()); diff --git a/nano/core_test/request_aggregator.cpp b/nano/core_test/request_aggregator.cpp index 99d429ab0..0730a49da 100644 --- a/nano/core_test/request_aggregator.cpp +++ b/nano/core_test/request_aggregator.cpp @@ -293,7 +293,7 @@ TEST (request_aggregator, split) std::shared_ptr election; ASSERT_TIMELY (5s, election = node.active.election (blocks.back ()->qualified_root ())); election->force_confirm (); - ASSERT_TIMELY_EQ (5s, max_vbh + 2, node.ledger.cache.cemented_count); + ASSERT_TIMELY_EQ (5s, max_vbh + 2, node.ledger.cemented_count ()); ASSERT_EQ (max_vbh + 1, request.size ()); auto client = std::make_shared (node); std::shared_ptr dummy_channel = std::make_shared (node, client); diff --git a/nano/core_test/wallet.cpp b/nano/core_test/wallet.cpp index ef40fe8c7..05d49392f 100644 --- a/nano/core_test/wallet.cpp +++ b/nano/core_test/wallet.cpp @@ -1186,7 +1186,7 @@ TEST (wallet, search_receivable) wallet.insert_adhoc (nano::dev::genesis_key.prv); // Pending search should create the receive block - ASSERT_EQ (2, node.ledger.cache.block_count); + ASSERT_EQ (2, node.ledger.block_count ()); ASSERT_FALSE (wallet.search_receivable (wallet.wallets.tx_begin_read ())); ASSERT_TIMELY_EQ (3s, node.balance (nano::dev::genesis_key.pub), nano::dev::constants.genesis_amount); auto receive_hash = node.ledger.latest (node.store.tx_begin_read (), nano::dev::genesis_key.pub); @@ -1220,12 +1220,12 @@ TEST (wallet, receive_pruned) auto send2 = wallet1.send_action (nano::dev::genesis_key.pub, key.pub, 1, 1); // Pruning - ASSERT_TIMELY_EQ (5s, node2.ledger.cache.cemented_count, 3); + ASSERT_TIMELY_EQ (5s, node2.ledger.cemented_count (), 3); { auto transaction = node2.store.tx_begin_write (); ASSERT_EQ (1, node2.ledger.pruning_action (transaction, send1->hash (), 2)); } - ASSERT_EQ (1, node2.ledger.cache.pruned_count); + ASSERT_EQ (1, node2.ledger.pruned_count ()); ASSERT_TRUE (node2.ledger.block_or_pruned_exists (send1->hash ())); ASSERT_FALSE (node2.ledger.block_exists (node2.store.tx_begin_read (), send1->hash ())); @@ -1234,5 +1234,5 @@ TEST (wallet, receive_pruned) auto open1 = wallet2.receive_action (send1->hash (), key.pub, amount, send1->destination (), 1); ASSERT_NE (nullptr, open1); ASSERT_EQ (amount, node2.ledger.balance (node2.store.tx_begin_read (), open1->hash ())); - ASSERT_TIMELY_EQ (5s, node2.ledger.cache.cemented_count, 4); + ASSERT_TIMELY_EQ (5s, node2.ledger.cemented_count (), 4); } diff --git a/nano/core_test/wallets.cpp b/nano/core_test/wallets.cpp index fd5c54318..283f776db 100644 --- a/nano/core_test/wallets.cpp +++ b/nano/core_test/wallets.cpp @@ -243,7 +243,7 @@ TEST (wallets, search_receivable) wallet->insert_adhoc (nano::dev::genesis_key.prv); // Pending search should create the receive block - ASSERT_EQ (2, node.ledger.cache.block_count); + ASSERT_EQ (2, node.ledger.block_count ()); if (search_all) { node.wallets.search_receivable_all (); diff --git a/nano/nano_node/entry.cpp b/nano/nano_node/entry.cpp index 55e6d7f5c..ec93fb2e8 100644 --- a/nano/nano_node/entry.cpp +++ b/nano/nano_node/entry.cpp @@ -169,7 +169,7 @@ int main (int argc, char * const * argv) auto const & hardcoded = bootstrap_weights.second; auto const hardcoded_height = bootstrap_weights.first; auto const ledger_unfiltered = node->ledger.cache.rep_weights.get_rep_amounts (); - auto const ledger_height = node->ledger.cache.block_count.load (); + auto const ledger_height = node->ledger.block_count (); auto get_total = [] (decltype (bootstrap_weights.second) const & reps) -> nano::uint128_union { return std::accumulate (reps.begin (), reps.end (), nano::uint128_t{ 0 }, [] (auto sum, auto const & rep) { return sum + rep.second; }); @@ -327,7 +327,7 @@ int main (int argc, char * const * argv) node_flags.generate_cache.block_count = true; nano::inactive_node inactive_node (data_path, node_flags); auto node = inactive_node.node; - std::cout << boost::str (boost::format ("Block count: %1%\n") % node->ledger.cache.block_count); + std::cout << boost::str (boost::format ("Block count: %1%\n") % node->ledger.block_count ()); } else if (vm.count ("debug_bootstrap_generate")) { @@ -448,7 +448,7 @@ int main (int argc, char * const * argv) nano::update_flags (node_flags, vm); node_flags.generate_cache.account_count = true; nano::inactive_node inactive_node (data_path, node_flags); - std::cout << boost::str (boost::format ("Frontier count: %1%\n") % inactive_node.node->ledger.cache.account_count); + std::cout << boost::str (boost::format ("Frontier count: %1%\n") % inactive_node.node->ledger.account_count ()); } else if (vm.count ("debug_profile_kdf")) { @@ -987,14 +987,14 @@ int main (int argc, char * const * argv) blocks.pop_front (); } nano::timer timer_l (nano::timer_state::started); - while (node->ledger.cache.block_count != max_blocks + 1) + while (node->ledger.block_count () != max_blocks + 1) { std::this_thread::sleep_for (std::chrono::milliseconds (10)); // Message each 15 seconds if (timer_l.after_deadline (std::chrono::seconds (15))) { timer_l.restart (); - std::cout << boost::str (boost::format ("%1% (%2%) blocks processed (unchecked), %3% remaining") % node->ledger.cache.block_count % node->unchecked.count () % node->block_processor.size ()) << std::endl; + std::cout << boost::str (boost::format ("%1% (%2%) blocks processed (unchecked), %3% remaining") % node->ledger.block_count () % node->unchecked.count () % node->block_processor.size ()) << std::endl; } } @@ -1002,7 +1002,7 @@ int main (int argc, char * const * argv) auto time (std::chrono::duration_cast (end - begin).count ()); node->stop (); std::cout << boost::str (boost::format ("%|1$ 12d| us \n%2% blocks per second\n") % time % (max_blocks * 1000000 / time)); - release_assert (node->ledger.cache.block_count == max_blocks + 1); + release_assert (node->ledger.block_count () == max_blocks + 1); } else if (vm.count ("debug_profile_votes")) { @@ -1202,12 +1202,12 @@ int main (int argc, char * const * argv) node1->block_processor.add (block); } auto iteration (0); - while (node1->ledger.cache.block_count != count * 2 + 1) + while (node1->ledger.block_count () != count * 2 + 1) { std::this_thread::sleep_for (std::chrono::milliseconds (500)); if (++iteration % 60 == 0) { - std::cout << boost::str (boost::format ("%1% blocks processed\n") % node1->ledger.cache.block_count); + std::cout << boost::str (boost::format ("%1% blocks processed\n") % node1->ledger.block_count ()); } } // Confirm blocks for node1 @@ -1215,12 +1215,12 @@ int main (int argc, char * const * argv) { node1->confirming_set.add (block->hash ()); } - while (node1->ledger.cache.cemented_count != node1->ledger.cache.block_count) + while (node1->ledger.cemented_count () != node1->ledger.block_count ()) { std::this_thread::sleep_for (std::chrono::milliseconds (500)); if (++iteration % 60 == 0) { - std::cout << boost::str (boost::format ("%1% blocks cemented\n") % node1->ledger.cache.cemented_count); + std::cout << boost::str (boost::format ("%1% blocks cemented\n") % node1->ledger.cemented_count ()); } } @@ -1250,12 +1250,12 @@ int main (int argc, char * const * argv) node2->block_processor.add (block); blocks.pop_front (); } - while (node2->ledger.cache.block_count != count * 2 + 1) + while (node2->ledger.block_count () != count * 2 + 1) { std::this_thread::sleep_for (std::chrono::milliseconds (500)); if (++iteration % 60 == 0) { - std::cout << boost::str (boost::format ("%1% blocks processed\n") % node2->ledger.cache.block_count); + std::cout << boost::str (boost::format ("%1% blocks processed\n") % node2->ledger.block_count ()); } } // Insert representative @@ -1274,12 +1274,12 @@ int main (int argc, char * const * argv) auto begin (std::chrono::high_resolution_clock::now ()); std::cout << boost::str (boost::format ("Starting confirming %1% frontiers (test node)\n") % (count + 1)); // Wait for full frontiers confirmation - while (node2->ledger.cache.cemented_count != node2->ledger.cache.block_count) + while (node2->ledger.cemented_count () != node2->ledger.block_count ()) { std::this_thread::sleep_for (std::chrono::milliseconds (25)); if (++iteration % 1200 == 0) { - std::cout << boost::str (boost::format ("%1% blocks confirmed\n") % node2->ledger.cache.cemented_count); + std::cout << boost::str (boost::format ("%1% blocks confirmed\n") % node2->ledger.cemented_count ()); } } auto end (std::chrono::high_resolution_clock::now ()); @@ -1793,7 +1793,7 @@ int main (int argc, char * const * argv) nano::inactive_node inactive_node (data_path, node_flags); auto source_node = inactive_node.node; auto transaction (source_node->store.tx_begin_read ()); - block_count = source_node->ledger.cache.block_count; + block_count = source_node->ledger.block_count (); std::cout << boost::str (boost::format ("Performing bootstrap emulation, %1% blocks in ledger...") % block_count) << std::endl; for (auto i (source_node->store.account.begin (transaction)), n (source_node->store.account.end ()); i != n; ++i) { @@ -1824,7 +1824,7 @@ int main (int argc, char * const * argv) } } nano::timer timer_l (nano::timer_state::started); - while (node.node->ledger.cache.block_count != block_count) + while (node.node->ledger.block_count () != block_count) { std::this_thread::sleep_for (std::chrono::milliseconds (500)); // Add epoch open blocks again if required @@ -1839,7 +1839,7 @@ int main (int argc, char * const * argv) if (timer_l.after_deadline (std::chrono::seconds (60))) { timer_l.restart (); - std::cout << boost::str (boost::format ("%1% (%2%) blocks processed (unchecked)") % node.node->ledger.cache.block_count % node.node->unchecked.count ()) << std::endl; + std::cout << boost::str (boost::format ("%1% (%2%) blocks processed (unchecked)") % node.node->ledger.block_count () % node.node->unchecked.count ()) << std::endl; } } @@ -1849,7 +1849,7 @@ int main (int argc, char * const * argv) auto seconds (time / us_in_second); nano::remove_temporary_directories (); std::cout << boost::str (boost::format ("%|1$ 12d| seconds \n%2% blocks per second") % seconds % (block_count * us_in_second / time)) << std::endl; - release_assert (node.node->ledger.cache.block_count == block_count); + release_assert (node.node->ledger.block_count () == block_count); } else if (vm.count ("debug_peers")) { @@ -1868,7 +1868,7 @@ int main (int argc, char * const * argv) node_flags.generate_cache.cemented_count = true; nano::update_flags (node_flags, vm); nano::inactive_node node (data_path, node_flags); - std::cout << "Total cemented block count: " << node.node->ledger.cache.cemented_count << std::endl; + std::cout << "Total cemented block count: " << node.node->ledger.cemented_count () << std::endl; } else if (vm.count ("debug_prune")) { diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 708078a53..5a95e0b8d 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -111,7 +111,7 @@ void nano::active_transactions::block_cemented_callback (std::shared_ptr= node.ledger.bootstrap_weight_max_blocks; + bool cemented_bootstrap_count_reached = node.ledger.cemented_count () >= node.ledger.bootstrap_weight_max_blocks; bool was_active = status.type == nano::election_status_type::active_confirmed_quorum || status.type == nano::election_status_type::active_confirmation_height; // Next-block activations are only done for blocks with previously active elections diff --git a/nano/node/bootstrap_ascending/service.cpp b/nano/node/bootstrap_ascending/service.cpp index c3e9bd2d1..3dd31bfd1 100644 --- a/nano/node/bootstrap_ascending/service.cpp +++ b/nano/node/bootstrap_ascending/service.cpp @@ -485,7 +485,7 @@ std::size_t nano::bootstrap_ascending::service::compute_throttle_size () const { // Scales logarithmically with ledger block // Returns: config.throttle_coefficient * sqrt(block_count) - std::size_t size_new = config.bootstrap_ascending.throttle_coefficient * std::sqrt (ledger.cache.block_count.load ()); + std::size_t size_new = config.bootstrap_ascending.throttle_coefficient * std::sqrt (ledger.block_count ()); return size_new == 0 ? 16 : size_new; } diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index ccec370e1..4dec6693c 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -1446,13 +1446,13 @@ void nano::json_handler::block_account () void nano::json_handler::block_count () { - response_l.put ("count", std::to_string (node.ledger.cache.block_count)); + response_l.put ("count", std::to_string (node.ledger.block_count ())); response_l.put ("unchecked", std::to_string (node.unchecked.count ())); - response_l.put ("cemented", std::to_string (node.ledger.cache.cemented_count)); + response_l.put ("cemented", std::to_string (node.ledger.cemented_count ())); if (node.flags.enable_pruning) { - response_l.put ("full", std::to_string (node.ledger.cache.block_count - node.ledger.cache.pruned_count)); - response_l.put ("pruned", std::to_string (node.ledger.cache.pruned_count)); + response_l.put ("full", std::to_string (node.ledger.block_count () - node.ledger.pruned_count ())); + response_l.put ("pruned", std::to_string (node.ledger.pruned_count ())); } response_errors (); } @@ -2351,7 +2351,7 @@ void nano::json_handler::frontiers () void nano::json_handler::account_count () { - auto size (node.ledger.cache.account_count.load ()); + auto size (node.ledger.account_count ()); response_l.put ("count", std::to_string (size)); response_errors (); } diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 6be3dc66b..10a710f60 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -422,10 +422,10 @@ nano::node::node (std::shared_ptr io_ctx_a, std::filesy ledger.bootstrap_weight_max_blocks = bootstrap_weights.first; logger.info (nano::log::type::node, "Initial bootstrap height: {}", ledger.bootstrap_weight_max_blocks); - logger.info (nano::log::type::node, "Current ledger height: {}", ledger.cache.block_count.load ()); + logger.info (nano::log::type::node, "Current ledger height: {}", ledger.block_count ()); // Use bootstrap weights if initial bootstrap is not completed - const bool use_bootstrap_weight = ledger.cache.block_count < bootstrap_weights.first; + const bool use_bootstrap_weight = ledger.block_count () < bootstrap_weights.first; if (use_bootstrap_weight) { logger.info (nano::log::type::node, "Using predefined representative weights, since block count is less than bootstrap threshold"); @@ -827,7 +827,7 @@ void nano::node::ongoing_bootstrap () } // Differential bootstrap with max age (75% of all legacy attempts) uint32_t frontiers_age (std::numeric_limits::max ()); - auto bootstrap_weight_reached (ledger.cache.block_count >= ledger.bootstrap_weight_max_blocks); + auto bootstrap_weight_reached (ledger.block_count () >= ledger.bootstrap_weight_max_blocks); auto previous_bootstrap_count (stats.count (nano::stat::type::bootstrap, nano::stat::detail::initiate, nano::stat::dir::out) + stats.count (nano::stat::type::bootstrap, nano::stat::detail::initiate_legacy_age, nano::stat::dir::out)); /* - Maximum value for 25% of attempts or if block count is below preconfigured value (initial bootstrap not finished) @@ -1025,7 +1025,7 @@ void nano::node::ledger_pruning (uint64_t const batch_size_a, bool bootstrap_wei void nano::node::ongoing_ledger_pruning () { - auto bootstrap_weight_reached (ledger.cache.block_count >= ledger.bootstrap_weight_max_blocks); + auto bootstrap_weight_reached (ledger.block_count () >= ledger.bootstrap_weight_max_blocks); ledger_pruning (flags.block_processor_batch_size != 0 ? flags.block_processor_batch_size : 2 * 1024, bootstrap_weight_reached); auto const ledger_pruning_interval (bootstrap_weight_reached ? config.max_pruning_age : std::min (config.max_pruning_age, std::chrono::seconds (15 * 60))); auto this_l (shared ()); @@ -1352,15 +1352,15 @@ nano::telemetry_data nano::node::local_telemetry () const { nano::telemetry_data telemetry_data; telemetry_data.node_id = node_id.pub; - telemetry_data.block_count = ledger.cache.block_count; - telemetry_data.cemented_count = ledger.cache.cemented_count; + telemetry_data.block_count = ledger.block_count (); + telemetry_data.cemented_count = ledger.cemented_count (); telemetry_data.bandwidth_cap = config.bandwidth_limit; telemetry_data.protocol_version = network_params.network.protocol_version; telemetry_data.uptime = std::chrono::duration_cast (std::chrono::steady_clock::now () - startup_time).count (); telemetry_data.unchecked_count = unchecked.count (); telemetry_data.genesis_block = network_params.ledger.genesis->hash (); telemetry_data.peer_count = nano::narrow_cast (network.size ()); - telemetry_data.account_count = ledger.cache.account_count; + telemetry_data.account_count = ledger.account_count (); telemetry_data.major_version = nano::get_major_node_version (); telemetry_data.minor_version = nano::get_minor_node_version (); telemetry_data.patch_version = nano::get_patch_node_version (); diff --git a/nano/qt/qt.cpp b/nano/qt/qt.cpp index d33db65cc..3742c308e 100644 --- a/nano/qt/qt.cpp +++ b/nano/qt/qt.cpp @@ -939,9 +939,9 @@ std::string nano_qt::status::text () size_t cemented (0); std::string count_string; { - auto size (wallet.wallet_m->wallets.node.ledger.cache.block_count.load ()); + auto size (wallet.wallet_m->wallets.node.ledger.block_count ()); unchecked = wallet.wallet_m->wallets.node.unchecked.count (); - cemented = wallet.wallet_m->wallets.node.ledger.cache.cemented_count.load (); + cemented = wallet.wallet_m->wallets.node.ledger.cemented_count (); count_string = std::to_string (size); } @@ -979,8 +979,8 @@ std::string nano_qt::status::text () if (wallet.node.flags.enable_pruning) { - count_string += ", Full: " + std::to_string (wallet.wallet_m->wallets.node.ledger.cache.block_count - wallet.wallet_m->wallets.node.ledger.cache.pruned_count); - count_string += ", Pruned: " + std::to_string (wallet.wallet_m->wallets.node.ledger.cache.pruned_count); + count_string += ", Full: " + std::to_string (wallet.wallet_m->wallets.node.ledger.block_count () - wallet.wallet_m->wallets.node.ledger.pruned_count ()); + count_string += ", Pruned: " + std::to_string (wallet.wallet_m->wallets.node.ledger.pruned_count ()); } result += count_string.c_str (); diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index f846ac9d4..15a0aa8b8 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -6599,7 +6599,7 @@ TEST (rpc, receive_pruned) auto transaction (node2->store.tx_begin_write ()); ASSERT_EQ (2, node2->ledger.pruning_action (transaction, send2->hash (), 1)); } - ASSERT_EQ (2, node2->ledger.cache.pruned_count); + ASSERT_EQ (2, node2->ledger.pruned_count ()); ASSERT_TRUE (node2->ledger.block_or_pruned_exists (send1->hash ())); ASSERT_FALSE (node2->ledger.block_exists (node2->store.tx_begin_read (), send1->hash ())); ASSERT_TRUE (node2->ledger.block_or_pruned_exists (send2->hash ())); diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 35965a297..442c687a3 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -1600,6 +1600,26 @@ nano::receivable_iterator nano::ledger::receivable_upper_bound (store::transacti return nano::receivable_iterator{ *this, tx, result }; } +uint64_t nano::ledger::cemented_count () const +{ + return cache.cemented_count; +} + +uint64_t nano::ledger::block_count () const +{ + return cache.block_count; +} + +uint64_t nano::ledger::account_count () const +{ + return cache.account_count; +} + +uint64_t nano::ledger::pruned_count () const +{ + return cache.pruned_count; +} + nano::uncemented_info::uncemented_info (nano::block_hash const & cemented_frontier, nano::block_hash const & frontier, nano::account const & account) : cemented_frontier (cemented_frontier), frontier (frontier), account (account) { diff --git a/nano/secure/ledger.hpp b/nano/secure/ledger.hpp index 6ddf472fe..57f12f677 100644 --- a/nano/secure/ledger.hpp +++ b/nano/secure/ledger.hpp @@ -104,6 +104,10 @@ public: // Returns the next receivable entry for the account 'account' with hash greater than 'hash' nano::receivable_iterator receivable_upper_bound (store::transaction const & tx, nano::account const & account, nano::block_hash const & hash) const; std::unique_ptr collect_container_info (std::string const & name) const; + uint64_t cemented_count () const; + uint64_t block_count () const; + uint64_t account_count () const; + uint64_t pruned_count () const; static nano::uint128_t const unit; nano::ledger_constants & constants; nano::store::component & store; diff --git a/nano/secure/ledger_cache.hpp b/nano/secure/ledger_cache.hpp index 1d8026efe..81503b6df 100644 --- a/nano/secure/ledger_cache.hpp +++ b/nano/secure/ledger_cache.hpp @@ -6,14 +6,28 @@ #include +namespace nano +{ +class ledger; +} +namespace nano::store +{ +class component; +} + namespace nano { /* Holds an in-memory cache of various counts */ class ledger_cache { + friend class store::component; + friend class ledger; + public: explicit ledger_cache (nano::store::rep_weight & rep_weight_store_a, nano::uint128_t min_rep_weight_a = 0); nano::rep_weights rep_weights; + +private: std::atomic cemented_count{ 0 }; std::atomic block_count{ 0 }; std::atomic pruned_count{ 0 }; diff --git a/nano/slow_test/bootstrap.cpp b/nano/slow_test/bootstrap.cpp index 95662612a..d6b9d4d94 100644 --- a/nano/slow_test/bootstrap.cpp +++ b/nano/slow_test/bootstrap.cpp @@ -169,10 +169,10 @@ TEST (bootstrap_ascending, profile) } });*/ - std::cout << "server count: " << server->ledger.cache.block_count << std::endl; + std::cout << "server count: " << server->ledger.block_count () << std::endl; nano::test::rate_observer rate; - rate.observe ("count", [&] () { return client->ledger.cache.block_count.load (); }); + rate.observe ("count", [&] () { return client->ledger.block_count (); }); rate.observe ("unchecked", [&] () { return client->unchecked.count (); }); rate.observe ("block_processor", [&] () { return client->block_processor.size (); }); rate.observe ("priority", [&] () { return client->ascendboot.priority_size (); }); diff --git a/nano/slow_test/node.cpp b/nano/slow_test/node.cpp index f9f3d57cd..bc8f38b37 100644 --- a/nano/slow_test/node.cpp +++ b/nano/slow_test/node.cpp @@ -709,12 +709,12 @@ TEST (confirmation_height, many_accounts_single_confirmation) cemented_count += i->second.height; } - ASSERT_EQ (cemented_count, node->ledger.cache.cemented_count); + ASSERT_EQ (cemented_count, node->ledger.cemented_count ()); ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in), num_accounts * 2 - 2); ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed_bounded, nano::stat::dir::in), num_accounts * 2 - 2); ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed_unbounded, nano::stat::dir::in), 0); - ASSERT_TIMELY_EQ (40s, (node->ledger.cache.cemented_count - 1), node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); + ASSERT_TIMELY_EQ (40s, (node->ledger.cemented_count () - 1), node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); ASSERT_TIMELY_EQ (10s, node->active.election_winner_details_size (), 0); } @@ -777,7 +777,7 @@ TEST (confirmation_height, many_accounts_many_confirmations) ASSERT_GE (num_confirmed_bounded, nano::confirmation_height::unbounded_cutoff); ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed_unbounded, nano::stat::dir::in), num_blocks_to_confirm - num_confirmed_bounded); - ASSERT_TIMELY_EQ (60s, (node->ledger.cache.cemented_count - 1), node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); + ASSERT_TIMELY_EQ (60s, (node->ledger.cemented_count () - 1), node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); auto transaction = node->store.tx_begin_read (); size_t cemented_count = 0; @@ -787,9 +787,9 @@ TEST (confirmation_height, many_accounts_many_confirmations) } ASSERT_EQ (num_blocks_to_confirm + 1, cemented_count); - ASSERT_EQ (cemented_count, node->ledger.cache.cemented_count); + ASSERT_EQ (cemented_count, node->ledger.cemented_count ()); - ASSERT_TIMELY_EQ (20s, (node->ledger.cache.cemented_count - 1), node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); + ASSERT_TIMELY_EQ (20s, (node->ledger.cemented_count () - 1), node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); ASSERT_TIMELY_EQ (10s, node->active.election_winner_details_size (), 0); } @@ -931,12 +931,12 @@ TEST (confirmation_height, long_chains) cemented_count += i->second.height; } - ASSERT_EQ (cemented_count, node->ledger.cache.cemented_count); + ASSERT_EQ (cemented_count, node->ledger.cemented_count ()); ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in), num_blocks * 2 + 2); ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed_bounded, nano::stat::dir::in), num_blocks * 2 + 2); ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed_unbounded, nano::stat::dir::in), 0); - ASSERT_TIMELY_EQ (40s, (node->ledger.cache.cemented_count - 1), node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); + ASSERT_TIMELY_EQ (40s, (node->ledger.cemented_count () - 1), node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); ASSERT_TIMELY_EQ (10s, node->active.election_winner_details_size (), 0); } @@ -976,11 +976,11 @@ TEST (confirmation_height, dynamic_algorithm) } node->confirming_set.add (state_blocks.front ()->hash ()); - ASSERT_TIMELY_EQ (20s, node->ledger.cache.cemented_count, 2); + ASSERT_TIMELY_EQ (20s, node->ledger.cemented_count (), 2); node->confirming_set.add (latest_genesis->hash ()); - ASSERT_TIMELY_EQ (20s, node->ledger.cache.cemented_count, num_blocks + 1); + ASSERT_TIMELY_EQ (20s, node->ledger.cemented_count (), num_blocks + 1); ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in), num_blocks); ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed_bounded, nano::stat::dir::in), 1); @@ -1094,7 +1094,7 @@ TEST (confirmation_height, many_accounts_send_receive_self) } system.deadline_set (200s); - while ((node->ledger.cache.cemented_count - 1) != node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)) + while ((node->ledger.cemented_count () - 1) != node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)) { ASSERT_NO_ERROR (system.poll ()); } @@ -1107,10 +1107,10 @@ TEST (confirmation_height, many_accounts_send_receive_self) } ASSERT_EQ (num_blocks_to_confirm + 1, cemented_count); - ASSERT_EQ (cemented_count, node->ledger.cache.cemented_count); + ASSERT_EQ (cemented_count, node->ledger.cemented_count ()); system.deadline_set (60s); - while ((node->ledger.cache.cemented_count - 1) != node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)) + while ((node->ledger.cemented_count () - 1) != node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)) { ASSERT_NO_ERROR (system.poll ()); } @@ -1264,7 +1264,7 @@ TEST (confirmation_height, many_accounts_send_receive_self_no_elections) } ASSERT_EQ (num_blocks_to_confirm + 1, cemented_count); - ASSERT_EQ (cemented_count, ledger.cache.cemented_count); + ASSERT_EQ (cemented_count, ledger.cemented_count ()); } } @@ -1471,7 +1471,7 @@ TEST (telemetry, under_load) std::thread thread1 (thread_func, nano::dev::genesis_key, latest_genesis, nano::dev::constants.genesis_amount - num_blocks); std::thread thread2 (thread_func, key, latest_key, num_blocks); - ASSERT_TIMELY_EQ (200s, node1->ledger.cache.block_count, num_blocks * 2 + 3); + ASSERT_TIMELY_EQ (200s, node1->ledger.block_count (), num_blocks * 2 + 3); thread1.join (); thread2.join (); @@ -1702,8 +1702,8 @@ TEST (node, mass_epoch_upgrader) info.pending_hash = block->hash (); } } - ASSERT_EQ (1 + total_accounts, node.ledger.cache.block_count); - ASSERT_EQ (1, node.ledger.cache.account_count); + ASSERT_EQ (1 + total_accounts, node.ledger.block_count ()); + ASSERT_EQ (1, node.ledger.account_count ()); // Receive for half of accounts for (auto const & info : opened) @@ -1723,32 +1723,32 @@ TEST (node, mass_epoch_upgrader) ASSERT_NE (nullptr, block); ASSERT_EQ (nano::block_status::progress, node.process (block)); } - ASSERT_EQ (1 + total_accounts + opened.size (), node.ledger.cache.block_count); - ASSERT_EQ (1 + opened.size (), node.ledger.cache.account_count); + ASSERT_EQ (1 + total_accounts + opened.size (), node.ledger.block_count ()); + ASSERT_EQ (1 + opened.size (), node.ledger.account_count ()); nano::keypair epoch_signer (nano::dev::genesis_key); - auto const block_count_before = node.ledger.cache.block_count.load (); + auto const block_count_before = node.ledger.block_count (); auto const total_to_upgrade = 1 + total_accounts; std::cout << "Mass upgrading " << total_to_upgrade << " accounts" << std::endl; - while (node.ledger.cache.block_count != block_count_before + total_to_upgrade) + while (node.ledger.block_count () != block_count_before + total_to_upgrade) { - auto const pre_upgrade = node.ledger.cache.block_count.load (); + auto const pre_upgrade = node.ledger.block_count (); auto upgrade_count = std::min (batch_size, block_count_before + total_to_upgrade - pre_upgrade); ASSERT_FALSE (node.epoch_upgrader.start (epoch_signer.prv, nano::epoch::epoch_1, upgrade_count, threads)); // Already ongoing - should fail ASSERT_TRUE (node.epoch_upgrader.start (epoch_signer.prv, nano::epoch::epoch_1, upgrade_count, threads)); system.deadline_set (60s); - while (node.ledger.cache.block_count != pre_upgrade + upgrade_count) + while (node.ledger.block_count () != pre_upgrade + upgrade_count) { ASSERT_NO_ERROR (system.poll ()); std::this_thread::sleep_for (200ms); - std::cout << node.ledger.cache.block_count - block_count_before << " / " << total_to_upgrade << std::endl; + std::cout << node.ledger.block_count () - block_count_before << " / " << total_to_upgrade << std::endl; } std::this_thread::sleep_for (50ms); } auto expected_blocks = block_count_before + total_accounts + 1; - ASSERT_EQ (expected_blocks, node.ledger.cache.block_count); + ASSERT_EQ (expected_blocks, node.ledger.block_count ()); // Check upgrade { auto transaction (node.store.tx_begin_read ()); @@ -1794,7 +1794,7 @@ TEST (node, mass_block_new) { node.process_active (block); } - ASSERT_TIMELY_EQ (200s, node.ledger.cache.block_count, next_block_count); + ASSERT_TIMELY_EQ (200s, node.ledger.block_count (), next_block_count); next_block_count += num_blocks; while (node.block_processor.size () > 0) { @@ -1971,7 +1971,7 @@ TEST (node, aggressive_flooding) ASSERT_TIMELY (!nano::slow_instrumentation () ? 10s : 40s, all_received ()); - ASSERT_TIMELY_EQ (!nano::slow_instrumentation () ? 10s : 40s, node1.ledger.cache.block_count, 1 + 2 * nodes_wallets.size ()); + ASSERT_TIMELY_EQ (!nano::slow_instrumentation () ? 10s : 40s, node1.ledger.block_count (), 1 + 2 * nodes_wallets.size ()); // Wait until the main node sees all representatives ASSERT_TIMELY_EQ (!nano::slow_instrumentation () ? 10s : 40s, node1.rep_crawler.principal_representatives ().size (), nodes_wallets.size ()); @@ -2008,7 +2008,7 @@ TEST (node, aggressive_flooding) // All blocks: genesis + (send+open) for each representative + 2 local blocks // The main node only sees all blocks if other nodes are flooding their PR's open block to all other PRs - ASSERT_EQ (1 + 2 * nodes_wallets.size () + 2, node1.ledger.cache.block_count); + ASSERT_EQ (1 + 2 * nodes_wallets.size () + 2, node1.ledger.block_count ()); } TEST (node, send_single_many_peers) @@ -2166,7 +2166,7 @@ TEST (system, block_sequence) std::string message; for (auto i : system.nodes) { - message += boost::str (boost::format ("N:%1% b:%2% c:%3% a:%4% s:%5% p:%6%\n") % std::to_string (i->network.port) % std::to_string (i->ledger.cache.block_count) % std::to_string (i->ledger.cache.cemented_count) % std::to_string (i->active.size ()) % std::to_string (i->scheduler.priority.size ()) % std::to_string (i->network.size ())); + message += boost::str (boost::format ("N:%1% b:%2% c:%3% a:%4% s:%5% p:%6%\n") % std::to_string (i->network.port) % std::to_string (i->ledger.block_count ()) % std::to_string (i->ledger.cemented_count ()) % std::to_string (i->active.size ()) % std::to_string (i->scheduler.priority.size ()) % std::to_string (i->network.size ())); nano::lock_guard lock{ i->active.mutex }; for (auto const & j : i->active.roots) { diff --git a/nano/test_common/system.cpp b/nano/test_common/system.cpp index df300b0ea..d5bc43b1d 100644 --- a/nano/test_common/system.cpp +++ b/nano/test_common/system.cpp @@ -610,7 +610,7 @@ void nano::test::system::generate_mass_activity (uint32_t count_a, nano::node & { auto now (std::chrono::steady_clock::now ()); auto us (std::chrono::duration_cast (now - previous).count ()); - auto count = node_a.ledger.cache.block_count.load (); + auto count = node_a.ledger.block_count (); std::cerr << boost::str (boost::format ("Mass activity iteration %1% us %2% us/t %3% block count: %4%\n") % i % us % (us / 256) % count); previous = now; } From e41498221c0e092867870c46a943096151dcfc4e Mon Sep 17 00:00:00 2001 From: clemahieu Date: Mon, 1 Apr 2024 17:40:14 +0200 Subject: [PATCH 125/128] Use ledger::confirm (#4531) * Use ledger::confirm instead of modifying confirmation_height_store * Remove usages of confirmation_height_store where results are unused. --- nano/core_test/block_store.cpp | 8 -------- nano/core_test/ledger.cpp | 23 +++++------------------ nano/rpc_test/rpc.cpp | 7 ------- nano/slow_test/node.cpp | 1 - 4 files changed, 5 insertions(+), 34 deletions(-) diff --git a/nano/core_test/block_store.cpp b/nano/core_test/block_store.cpp index d5689673b..c1cf35ab3 100644 --- a/nano/core_test/block_store.cpp +++ b/nano/core_test/block_store.cpp @@ -480,7 +480,6 @@ TEST (block_store, frontier_retrieval) nano::account account1{}; nano::account_info info1 (0, 0, 0, 0, 0, 0, nano::epoch::epoch_0); auto transaction (store->tx_begin_write ()); - store->confirmation_height.put (transaction, account1, { 0, nano::block_hash (0) }); store->account.put (transaction, account1, info1); nano::account_info info2; store->account.get (transaction, account1, info2); @@ -604,10 +603,6 @@ TEST (block_store, latest_find) nano::account account2 (3); nano::block_hash hash2 (4); auto transaction (store->tx_begin_write ()); - store->confirmation_height.put (transaction, account1, { 0, nano::block_hash (0) }); - store->account.put (transaction, account1, { hash1, account1, hash1, 100, 0, 300, nano::epoch::epoch_0 }); - store->confirmation_height.put (transaction, account2, { 0, nano::block_hash (0) }); - store->account.put (transaction, account2, { hash2, account2, hash2, 200, 0, 400, nano::epoch::epoch_0 }); auto first (store->account.begin (transaction)); auto second (store->account.begin (transaction)); ++second; @@ -768,7 +763,6 @@ TEST (block_store, latest_exists) nano::account two (2); nano::account_info info; auto transaction (store->tx_begin_write ()); - store->confirmation_height.put (transaction, two, { 0, nano::block_hash (0) }); store->account.put (transaction, two, info); nano::account one (1); ASSERT_FALSE (store->account.exists (transaction, one)); @@ -786,7 +780,6 @@ TEST (block_store, large_iteration) nano::account account; nano::random_pool::generate_block (account.bytes.data (), account.bytes.size ()); accounts1.insert (account); - store->confirmation_height.put (transaction, account, { 0, nano::block_hash (0) }); store->account.put (transaction, account, nano::account_info ()); } std::unordered_set accounts2; @@ -889,7 +882,6 @@ TEST (block_store, account_count) auto transaction (store->tx_begin_write ()); ASSERT_EQ (0, store->account.count (transaction)); nano::account account (200); - store->confirmation_height.put (transaction, account, { 0, nano::block_hash (0) }); store->account.put (transaction, account, nano::account_info ()); } auto transaction (store->tx_begin_read ()); diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index e0f9d69aa..1e300c34a 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -4667,10 +4667,7 @@ TEST (ledger, dependents_confirmed) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive1)); ASSERT_FALSE (ledger.dependents_confirmed (transaction, *receive1)); - nano::confirmation_height_info height; - ASSERT_FALSE (ledger.store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, height)); - height.height += 1; - ledger.store.confirmation_height.put (transaction, nano::dev::genesis_key.pub, height); + ledger.confirm (transaction, send1->hash ()); ASSERT_TRUE (ledger.dependents_confirmed (transaction, *receive1)); auto receive2 = builder.state () .account (key1.pub) @@ -4683,13 +4680,9 @@ TEST (ledger, dependents_confirmed) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive2)); ASSERT_FALSE (ledger.dependents_confirmed (transaction, *receive2)); - ASSERT_TRUE (ledger.store.confirmation_height.get (transaction, key1.pub, height)); - height.height += 1; - ledger.store.confirmation_height.put (transaction, key1.pub, height); + ledger.confirm (transaction, receive1->hash ()); ASSERT_FALSE (ledger.dependents_confirmed (transaction, *receive2)); - ASSERT_FALSE (ledger.store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, height)); - height.height += 1; - ledger.store.confirmation_height.put (transaction, nano::dev::genesis_key.pub, height); + ledger.confirm (transaction, send2->hash ()); ASSERT_TRUE (ledger.dependents_confirmed (transaction, *receive2)); } @@ -4726,10 +4719,7 @@ TEST (ledger, dependents_confirmed_pruning) .work (*pool.generate (send1->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2)); - nano::confirmation_height_info height; - ASSERT_FALSE (ledger.store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, height)); - height.height = 3; - ledger.store.confirmation_height.put (transaction, nano::dev::genesis_key.pub, height); + ledger.confirm (transaction, send2->hash ()); ASSERT_TRUE (ledger.block_confirmed (transaction, send1->hash ())); ASSERT_EQ (2, ledger.pruning_action (transaction, send2->hash (), 1)); auto receive1 = builder.state () @@ -4767,10 +4757,7 @@ TEST (ledger, block_confirmed) ASSERT_FALSE (ledger.block_confirmed (transaction, send1->hash ())); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send1)); ASSERT_FALSE (ledger.block_confirmed (transaction, send1->hash ())); - nano::confirmation_height_info height; - ASSERT_FALSE (ledger.store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, height)); - ++height.height; - ledger.store.confirmation_height.put (transaction, nano::dev::genesis_key.pub, height); + ledger.confirm (transaction, send1->hash ()); ASSERT_TRUE (ledger.block_confirmed (transaction, send1->hash ())); } diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 15a0aa8b8..c429760a7 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -843,7 +843,6 @@ TEST (rpc, frontier) nano::block_hash hash; nano::random_pool::generate_block (hash.bytes.data (), hash.bytes.size ()); source[key.pub] = hash; - node->store.confirmation_height.put (transaction, key.pub, { 0, nano::block_hash (0) }); node->store.account.put (transaction, key.pub, nano::account_info (hash, 0, 0, 0, 0, 0, nano::epoch::epoch_0)); } } @@ -881,7 +880,6 @@ TEST (rpc, frontier_limited) nano::block_hash hash; nano::random_pool::generate_block (hash.bytes.data (), hash.bytes.size ()); source[key.pub] = hash; - node->store.confirmation_height.put (transaction, key.pub, { 0, nano::block_hash (0) }); node->store.account.put (transaction, key.pub, nano::account_info (hash, 0, 0, 0, 0, 0, nano::epoch::epoch_0)); } } @@ -909,7 +907,6 @@ TEST (rpc, frontier_startpoint) nano::block_hash hash; nano::random_pool::generate_block (hash.bytes.data (), hash.bytes.size ()); source[key.pub] = hash; - node->store.confirmation_height.put (transaction, key.pub, { 0, nano::block_hash (0) }); node->store.account.put (transaction, key.pub, nano::account_info (hash, 0, 0, 0, 0, 0, nano::epoch::epoch_0)); } } @@ -3823,10 +3820,6 @@ TEST (rpc, account_info) .build (); ASSERT_EQ (nano::block_status::progress, node1->process (send)); auto time = nano::seconds_since_epoch (); - { - auto transaction = node1->store.tx_begin_write (); - node1->store.confirmation_height.put (transaction, nano::dev::genesis_key.pub, { 1, nano::dev::genesis->hash () }); - } request.put ("account", nano::dev::genesis_key.pub.to_account ()); { diff --git a/nano/slow_test/node.cpp b/nano/slow_test/node.cpp index bc8f38b37..78f50fd46 100644 --- a/nano/slow_test/node.cpp +++ b/nano/slow_test/node.cpp @@ -234,7 +234,6 @@ TEST (store, load) { nano::account account; nano::random_pool::generate_block (account.bytes.data (), account.bytes.size ()); - system.nodes[0]->store.confirmation_height.put (transaction, account, { 0, nano::block_hash (0) }); system.nodes[0]->store.account.put (transaction, account, nano::account_info ()); } } From 697f2ce05deab5bc9cdc0b17cefddb65c5a3b3ad Mon Sep 17 00:00:00 2001 From: clemahieu Date: Mon, 1 Apr 2024 17:40:24 +0200 Subject: [PATCH 126/128] Removing unconfirmed_frontiers command line option. (#4530) When unconfirmed blocks are moved in to memory this information will not be queryable outside the node process. --- nano/core_test/ledger.cpp | 35 ----------------------------------- nano/nano_node/entry.cpp | 14 -------------- nano/secure/ledger.cpp | 36 ------------------------------------ nano/secure/ledger.hpp | 10 ---------- 4 files changed, 95 deletions(-) diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 1e300c34a..aa9b6e54d 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -5517,41 +5517,6 @@ TEST (ledger, migrate_lmdb_to_rocksdb) ASSERT_EQ (rocksdb_store.final_vote.get (rocksdb_transaction, nano::root (send->previous ()))[0], nano::block_hash (2)); } -TEST (ledger, unconfirmed_frontiers) -{ - auto ctx = nano::test::context::ledger_empty (); - auto & ledger = ctx.ledger (); - auto & store = ctx.store (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; - - auto unconfirmed_frontiers = ledger.unconfirmed_frontiers (); - ASSERT_TRUE (unconfirmed_frontiers.empty ()); - - nano::state_block_builder builder; - nano::keypair key; - auto const latest = ledger.latest (store.tx_begin_read (), nano::dev::genesis_key.pub); - auto send = builder.make_block () - .account (nano::dev::genesis_key.pub) - .previous (latest) - .representative (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - 100) - .link (key.pub) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*pool.generate (latest)) - .build (); - - ASSERT_EQ (nano::block_status::progress, ledger.process (store.tx_begin_write (), send)); - - unconfirmed_frontiers = ledger.unconfirmed_frontiers (); - ASSERT_EQ (unconfirmed_frontiers.size (), 1); - ASSERT_EQ (unconfirmed_frontiers.begin ()->first, 1); - nano::uncemented_info uncemented_info1{ latest, send->hash (), nano::dev::genesis_key.pub }; - auto uncemented_info2 = unconfirmed_frontiers.begin ()->second; - ASSERT_EQ (uncemented_info1.account, uncemented_info2.account); - ASSERT_EQ (uncemented_info1.cemented_frontier, uncemented_info2.cemented_frontier); - ASSERT_EQ (uncemented_info1.frontier, uncemented_info2.frontier); -} - TEST (ledger, is_send_genesis) { auto ctx = nano::test::context::ledger_empty (); diff --git a/nano/nano_node/entry.cpp b/nano/nano_node/entry.cpp index ec93fb2e8..ff031e71d 100644 --- a/nano/nano_node/entry.cpp +++ b/nano/nano_node/entry.cpp @@ -1993,20 +1993,6 @@ int main (int argc, char * const * argv) output_account_version_number (i, unopened_account_version_totals[i]); } } - else if (vm.count ("debug_unconfirmed_frontiers")) - { - auto inactive_node = nano::default_inactive_node (data_path, vm); - auto node = inactive_node->node; - - auto unconfirmed_frontiers = node->ledger.unconfirmed_frontiers (); - std::cout << "Account: Height delta | Frontier | Confirmed frontier\n"; - for (auto const & [height_delta, unconfirmed_info] : unconfirmed_frontiers) - { - std::cout << (boost::format ("%1%: %2% %3% %4%\n") % unconfirmed_info.account.to_account () % height_delta % unconfirmed_info.frontier.to_string () % unconfirmed_info.cemented_frontier.to_string ()).str (); - } - - std::cout << "\nNumber of unconfirmed frontiers: " << unconfirmed_frontiers.size () << std::endl; - } else if (vm.count ("version")) { std::cout << "Version " << NANO_VERSION_STRING << "\n" diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 442c687a3..c6cb0f4ad 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -1370,37 +1370,6 @@ uint64_t nano::ledger::pruning_action (store::write_transaction & transaction_a, return pruned_count; } -std::multimap> nano::ledger::unconfirmed_frontiers () const -{ - nano::locked>> result; - using result_t = decltype (result)::value_type; - - store.account.for_each_par ([this, &result] (store::read_transaction const & transaction_a, store::iterator i, store::iterator n) { - result_t unconfirmed_frontiers_l; - for (; i != n; ++i) - { - auto const & account (i->first); - auto const & account_info (i->second); - - nano::confirmation_height_info conf_height_info; - this->store.confirmation_height.get (transaction_a, account, conf_height_info); - - if (account_info.block_count != conf_height_info.height) - { - // Always output as no confirmation height has been set on the account yet - auto height_delta = account_info.block_count - conf_height_info.height; - auto const & frontier = account_info.head; - auto const & cemented_frontier = conf_height_info.frontier; - unconfirmed_frontiers_l.emplace (std::piecewise_construct, std::forward_as_tuple (height_delta), std::forward_as_tuple (cemented_frontier, frontier, i->first)); - } - } - // Merge results - auto result_locked = result.lock (); - result_locked->insert (unconfirmed_frontiers_l.begin (), unconfirmed_frontiers_l.end ()); - }); - return result; -} - // A precondition is that the store is an LMDB store bool nano::ledger::migrate_lmdb_to_rocksdb (std::filesystem::path const & data_path_a) const { @@ -1620,11 +1589,6 @@ uint64_t nano::ledger::pruned_count () const return cache.pruned_count; } -nano::uncemented_info::uncemented_info (nano::block_hash const & cemented_frontier, nano::block_hash const & frontier, nano::account const & account) : - cemented_frontier (cemented_frontier), frontier (frontier), account (account) -{ -} - std::unique_ptr nano::ledger::collect_container_info (std::string const & name) const { auto count = bootstrap_weights.size (); diff --git a/nano/secure/ledger.hpp b/nano/secure/ledger.hpp index 57f12f677..e754b7a91 100644 --- a/nano/secure/ledger.hpp +++ b/nano/secure/ledger.hpp @@ -27,15 +27,6 @@ class pending_info; class pending_key; class stats; -class uncemented_info -{ -public: - uncemented_info (nano::block_hash const & cemented_frontier, nano::block_hash const & frontier, nano::account const & account); - nano::block_hash cemented_frontier; - nano::block_hash frontier; - nano::account account; -}; - class ledger final { friend class receivable_iterator; @@ -90,7 +81,6 @@ public: std::shared_ptr find_receive_block_by_send_hash (store::transaction const & transaction, nano::account const & destination, nano::block_hash const & send_block_hash); nano::account const & epoch_signer (nano::link const &) const; nano::link const & epoch_link (nano::epoch) const; - std::multimap> unconfirmed_frontiers () const; bool migrate_lmdb_to_rocksdb (std::filesystem::path const &) const; bool bootstrap_weight_reached () const; static nano::epoch version (nano::block const & block); From 7dbf75e2e9ff158c50dc3054f02b29b008c8f20c Mon Sep 17 00:00:00 2001 From: clemahieu Date: Wed, 3 Apr 2024 18:58:17 +0200 Subject: [PATCH 127/128] Fix pruning tests that did not confirm blocks before pruning. Strongly ensure blocks are confirmed while pruning. (#4532) node::collect_ledger_pruning_targets already ensures blocks are confirmed before pruning however this was not the case in tests. Removed one test that only tested erroneous behavior. --- nano/core_test/bootstrap.cpp | 1 + nano/core_test/ledger.cpp | 55 +++++-------------------------- nano/core_test/ledger_confirm.cpp | 1 + nano/qt_test/qt.cpp | 4 +++ nano/secure/ledger.cpp | 1 + 5 files changed, 15 insertions(+), 47 deletions(-) diff --git a/nano/core_test/bootstrap.cpp b/nano/core_test/bootstrap.cpp index 97105c331..30b29e48d 100644 --- a/nano/core_test/bootstrap.cpp +++ b/nano/core_test/bootstrap.cpp @@ -652,6 +652,7 @@ TEST (bootstrap_processor, push_diamond_pruning) { auto transaction (node1->store.tx_begin_write ()); + node1->ledger.confirm (transaction, open->hash ()); ASSERT_EQ (1, node1->ledger.pruning_action (transaction, send1->hash (), 2)); ASSERT_EQ (1, node1->ledger.pruning_action (transaction, open->hash (), 1)); ASSERT_TRUE (node1->ledger.block_exists (transaction, nano::dev::genesis->hash ())); diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index aa9b6e54d..35d1f419d 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -4900,6 +4900,7 @@ TEST (ledger, pruning_action) ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2)); ASSERT_TRUE (store->block.exists (transaction, send2->hash ())); // Pruning action + ledger.confirm (transaction, send1->hash ()); ASSERT_EQ (1, ledger.pruning_action (transaction, send1->hash (), 1)); ASSERT_EQ (0, ledger.pruning_action (transaction, nano::dev::genesis->hash (), 1)); ASSERT_TRUE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); @@ -4935,6 +4936,7 @@ TEST (ledger, pruning_action) ASSERT_FALSE (receive1_stored->sideband ().details.is_epoch); // Middle block pruning ASSERT_TRUE (store->block.exists (transaction, send2->hash ())); + ledger.confirm (transaction, send2->hash ()); ASSERT_EQ (1, ledger.pruning_action (transaction, send2->hash (), 1)); ASSERT_TRUE (store->pruned.exists (transaction, send2->hash ())); ASSERT_FALSE (store->block.exists (transaction, send2->hash ())); @@ -4987,6 +4989,7 @@ TEST (ledger, pruning_large_chain) } ASSERT_EQ (0, store->pruned.count (transaction)); ASSERT_EQ (send_receive_pairs * 2 + 1, store->block.count (transaction)); + ledger.confirm (transaction, last_hash); // Pruning action ASSERT_EQ (send_receive_pairs * 2, ledger.pruning_action (transaction, last_hash, 5)); ASSERT_TRUE (store->pruned.exists (transaction, last_hash)); @@ -5045,6 +5048,7 @@ TEST (ledger, pruning_source_rollback) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2)); ASSERT_TRUE (store->block.exists (transaction, send2->hash ())); + ledger.confirm (transaction, send1->hash ()); // Pruning action ASSERT_EQ (2, ledger.pruning_action (transaction, send1->hash (), 1)); ASSERT_FALSE (store->block.exists (transaction, send1->hash ())); @@ -5131,6 +5135,7 @@ TEST (ledger, pruning_source_rollback_legacy) ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send3)); ASSERT_TRUE (store->block.exists (transaction, send3->hash ())); ASSERT_TRUE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send3->hash () })); + ledger.confirm (transaction, send2->hash ()); // Pruning action ASSERT_EQ (2, ledger.pruning_action (transaction, send2->hash (), 1)); ASSERT_FALSE (store->block.exists (transaction, send2->hash ())); @@ -5199,53 +5204,6 @@ TEST (ledger, pruning_source_rollback_legacy) ASSERT_EQ (6, ledger.block_count ()); } -TEST (ledger, pruning_process_error) -{ - nano::logger logger; - auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants); - ASSERT_TRUE (!store->init_error ()); - nano::stats stats; - nano::ledger ledger (*store, stats, nano::dev::constants); - ledger.pruning = true; - auto transaction (store->tx_begin_write ()); - store->initialize (transaction, ledger.cache, ledger.constants); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; - nano::block_builder builder; - auto send1 = builder - .state () - .account (nano::dev::genesis_key.pub) - .previous (nano::dev::genesis->hash ()) - .representative (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) - .link (nano::dev::genesis_key.pub) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*pool.generate (nano::dev::genesis->hash ())) - .build (); - ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send1)); - ASSERT_EQ (0, ledger.pruned_count ()); - ASSERT_EQ (2, ledger.block_count ()); - // Pruning action for latest block (not valid action) - ASSERT_EQ (1, ledger.pruning_action (transaction, send1->hash (), 1)); - ASSERT_FALSE (store->block.exists (transaction, send1->hash ())); - ASSERT_TRUE (store->pruned.exists (transaction, send1->hash ())); - // Attempt to process pruned block again - ASSERT_EQ (nano::block_status::old, ledger.process (transaction, send1)); - // Attept to process new block after pruned - auto send2 = builder - .state () - .account (nano::dev::genesis_key.pub) - .previous (send1->hash ()) - .representative (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2) - .link (nano::dev::genesis_key.pub) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*pool.generate (send1->hash ())) - .build (); - ASSERT_EQ (nano::block_status::gap_previous, ledger.process (transaction, send2)); - ASSERT_EQ (1, ledger.pruned_count ()); - ASSERT_EQ (2, ledger.block_count ()); -} - TEST (ledger, pruning_legacy_blocks) { nano::logger logger; @@ -5312,6 +5270,7 @@ TEST (ledger, pruning_legacy_blocks) .work (*pool.generate (open1->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send3)); + ledger.confirm (transaction, open1->hash ()); // Pruning action ASSERT_EQ (3, ledger.pruning_action (transaction, change1->hash (), 2)); ASSERT_EQ (1, ledger.pruning_action (transaction, open1->hash (), 1)); @@ -5368,6 +5327,7 @@ TEST (ledger, pruning_safe_functions) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2)); ASSERT_TRUE (store->block.exists (transaction, send2->hash ())); + ledger.confirm (transaction, send1->hash ()); // Pruning action ASSERT_EQ (1, ledger.pruning_action (transaction, send1->hash (), 1)); ASSERT_FALSE (store->block.exists (transaction, send1->hash ())); @@ -5419,6 +5379,7 @@ TEST (ledger, hash_root_random) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2)); ASSERT_TRUE (store->block.exists (transaction, send2->hash ())); + ledger.confirm (transaction, send1->hash ()); // Pruning action ASSERT_EQ (1, ledger.pruning_action (transaction, send1->hash (), 1)); ASSERT_FALSE (store->block.exists (transaction, send1->hash ())); diff --git a/nano/core_test/ledger_confirm.cpp b/nano/core_test/ledger_confirm.cpp index ee3a4c1e8..dc6e55c37 100644 --- a/nano/core_test/ledger_confirm.cpp +++ b/nano/core_test/ledger_confirm.cpp @@ -845,6 +845,7 @@ TEST (ledger_confirm, pruned_source) ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2)); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send3)); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open2)); + ledger.confirm (transaction, send2->hash ()); ASSERT_EQ (2, ledger.pruning_action (transaction, send2->hash (), 2)); ASSERT_FALSE (ledger.block_exists (transaction, send2->hash ())); ASSERT_FALSE (ledger.block_confirmed (transaction, open2->hash ())); diff --git a/nano/qt_test/qt.cpp b/nano/qt_test/qt.cpp index d6758ff88..16f7306cb 100644 --- a/nano/qt_test/qt.cpp +++ b/nano/qt_test/qt.cpp @@ -581,6 +581,7 @@ TEST (history, pruned_source) ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive)); auto open = std::make_shared (send2->hash (), key.pub, key.pub, key.prv, key.pub, *system.work.generate (key.pub)); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open)); + ledger.confirm (transaction, send1->hash ()); ASSERT_EQ (1, ledger.pruning_action (transaction, send1->hash (), 2)); next_pruning = send2->hash (); } @@ -593,6 +594,7 @@ TEST (history, pruned_source) // Additional legacy test { auto transaction (store->tx_begin_write ()); + ledger.confirm (transaction, next_pruning); ASSERT_EQ (1, ledger.pruning_action (transaction, next_pruning, 2)); } history1.refresh (); @@ -608,7 +610,9 @@ TEST (history, pruned_source) auto latest_key (ledger.latest (transaction, key.pub)); auto receive = std::make_shared (key.pub, latest_key, key.pub, 200, send->hash (), key.prv, key.pub, *system.work.generate (latest_key)); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive)); + ledger.confirm (transaction, latest); ASSERT_EQ (1, ledger.pruning_action (transaction, latest, 2)); + ledger.confirm (transaction, latest_key); ASSERT_EQ (1, ledger.pruning_action (transaction, latest_key, 2)); } history1.refresh (); diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index c6cb0f4ad..ab13ca171 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -1346,6 +1346,7 @@ uint64_t nano::ledger::pruning_action (store::write_transaction & transaction_a, auto block_l = block (transaction_a, hash); if (block_l != nullptr) { + release_assert (block_confirmed (transaction_a, hash)); store.block.del (transaction_a, hash); store.pruned.put (transaction_a, hash); hash = block_l->previous (); From 311bf685bf6c269a57b7e2d9fe7be864eb7cfba8 Mon Sep 17 00:00:00 2001 From: RickiNano <81099017+RickiNano@users.noreply.github.com> Date: Wed, 3 Apr 2024 22:24:16 +0200 Subject: [PATCH 128/128] Rpc endpoint for election statistics (#4533) --- nano/node/json_handler.cpp | 39 ++++++++++++++++++++++++++++++++++++++ nano/node/json_handler.hpp | 1 + nano/rpc_test/rpc.cpp | 15 +++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 4dec6693c..7d226c465 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -2002,6 +2002,44 @@ void nano::json_handler::confirmation_active () response_errors (); } +void nano::json_handler::election_statistics () +{ + auto active_elections = node.active.list_active (); + unsigned normal_count = 0; + unsigned hinted_count = 0; + unsigned optimistic_count = 0; + unsigned total_count = 0; + + for (auto const & election : active_elections) + { + total_count++; + switch (election->behavior ()) + { + case election_behavior::normal: + normal_count++; + break; + case election_behavior::hinted: + hinted_count++; + break; + case election_behavior::optimistic: + optimistic_count++; + break; + } + } + + auto utilization_percentage = (static_cast (total_count * 100) / node.config.active_elections_size); + std::stringstream stream; + stream << std::fixed << std::setprecision (2) << utilization_percentage; + + response_l.put ("normal", normal_count); + response_l.put ("hinted", hinted_count); + response_l.put ("optimistic", optimistic_count); + response_l.put ("total", total_count); + response_l.put ("aec_utilization_percentage", stream.str ()); + + response_errors (); +} + void nano::json_handler::confirmation_history () { boost::property_tree::ptree elections; @@ -5327,6 +5365,7 @@ ipc_json_handler_no_arg_func_map create_ipc_json_handler_no_arg_func_map () no_arg_funcs.emplace ("delegators", &nano::json_handler::delegators); no_arg_funcs.emplace ("delegators_count", &nano::json_handler::delegators_count); no_arg_funcs.emplace ("deterministic_key", &nano::json_handler::deterministic_key); + no_arg_funcs.emplace ("election_statistics", &nano::json_handler::election_statistics); no_arg_funcs.emplace ("epoch_upgrade", &nano::json_handler::epoch_upgrade); no_arg_funcs.emplace ("frontiers", &nano::json_handler::frontiers); no_arg_funcs.emplace ("frontier_count", &nano::json_handler::account_count); diff --git a/nano/node/json_handler.hpp b/nano/node/json_handler.hpp index cc8d7b7a8..33eca6c6c 100644 --- a/nano/node/json_handler.hpp +++ b/nano/node/json_handler.hpp @@ -46,6 +46,7 @@ public: void accounts_pending (); void accounts_receivable (); void active_difficulty (); + void election_statistics (); void available_supply (); void block_info (); void block_confirm (); diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index c429760a7..3b9168a00 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -6866,3 +6866,18 @@ TEST (rpc, confirmation_info) ASSERT_EQ (0, response.get ("total_tally")); } } + +TEST (rpc, election_statistics) +{ + nano::test::system system; + auto node1 = add_ipc_enabled_node (system); + auto const rpc_ctx = add_rpc (system, node1); + boost::property_tree::ptree request1; + request1.put ("action", "election_statistics"); + auto response1 (wait_response (system, rpc_ctx, request1)); + ASSERT_EQ ("0", response1.get ("normal")); + ASSERT_EQ ("0", response1.get ("hinted")); + ASSERT_EQ ("0", response1.get ("optimistic")); + ASSERT_EQ ("0", response1.get ("total")); + ASSERT_EQ ("0.00", response1.get ("aec_utilization_percentage")); +}