From f379b3489f454c2539de8f1c6952049926cd6677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Mon, 6 Jan 2025 20:27:23 +0100 Subject: [PATCH 1/6] Reset bootstrap state command --- nano/core_test/bootstrap.cpp | 4 +++- nano/lib/stats_enums.hpp | 1 + nano/node/bootstrap/account_sets.cpp | 6 ++++++ nano/node/bootstrap/account_sets.hpp | 2 ++ nano/node/bootstrap/bootstrap_service.cpp | 14 ++++++++++++++ nano/node/bootstrap/bootstrap_service.hpp | 7 ++++++- nano/node/bootstrap/database_scan.cpp | 11 +++++++++++ nano/node/bootstrap/database_scan.hpp | 2 ++ nano/node/bootstrap/frontier_scan.cpp | 10 ++++++++++ nano/node/bootstrap/frontier_scan.hpp | 12 ++++++++++++ nano/node/bootstrap/peer_scoring.cpp | 5 +++++ nano/node/bootstrap/peer_scoring.hpp | 6 ++++++ nano/node/bootstrap/throttle.cpp | 6 ++++++ nano/node/bootstrap/throttle.hpp | 6 ++++++ nano/node/json_handler.cpp | 8 ++++++++ nano/node/json_handler.hpp | 1 + nano/rpc/rpc_handler.cpp | 2 ++ 17 files changed, 101 insertions(+), 2 deletions(-) diff --git a/nano/core_test/bootstrap.cpp b/nano/core_test/bootstrap.cpp index ccebd97cd4..8d866031b0 100644 --- a/nano/core_test/bootstrap.cpp +++ b/nano/core_test/bootstrap.cpp @@ -571,4 +571,6 @@ TEST (bootstrap, frontier_scan_cannot_prioritize) ASSERT_ALWAYS (1s, std::none_of (opens2.begin (), opens2.end (), [&node1] (auto const & block) { return node1.bootstrap.prioritized (block->account ()); })); -} \ No newline at end of file +} + +// TODO: Test bootstrap.reset () \ No newline at end of file diff --git a/nano/lib/stats_enums.hpp b/nano/lib/stats_enums.hpp index 429741d2f3..3f142013ac 100644 --- a/nano/lib/stats_enums.hpp +++ b/nano/lib/stats_enums.hpp @@ -166,6 +166,7 @@ enum class detail other, drop, queued, + reset, // processing queue queue, diff --git a/nano/node/bootstrap/account_sets.cpp b/nano/node/bootstrap/account_sets.cpp index 421ffddb46..f0639ff9cb 100644 --- a/nano/node/bootstrap/account_sets.cpp +++ b/nano/node/bootstrap/account_sets.cpp @@ -19,6 +19,12 @@ nano::bootstrap::account_sets::account_sets (nano::account_sets_config const & c { } +void nano::bootstrap::account_sets::reset () +{ + priorities.clear (); + blocking.clear (); +} + void nano::bootstrap::account_sets::priority_up (nano::account const & account) { if (account.is_zero ()) diff --git a/nano/node/bootstrap/account_sets.hpp b/nano/node/bootstrap/account_sets.hpp index cac5785c8a..5a5a0f7796 100644 --- a/nano/node/bootstrap/account_sets.hpp +++ b/nano/node/bootstrap/account_sets.hpp @@ -33,6 +33,8 @@ class account_sets public: account_sets (account_sets_config const &, nano::stats &); + void reset (); + /** * If an account is not blocked, increase its priority. * If the account does not exist in priority set and is not blocked, inserts a new entry. diff --git a/nano/node/bootstrap/bootstrap_service.cpp b/nano/node/bootstrap/bootstrap_service.cpp index 8f60f9924f..13727436e3 100644 --- a/nano/node/bootstrap/bootstrap_service.cpp +++ b/nano/node/bootstrap/bootstrap_service.cpp @@ -145,6 +145,20 @@ void nano::bootstrap_service::stop () workers.stop (); } +void nano::bootstrap_service::reset () +{ + nano::lock_guard<nano::mutex> lock{ mutex }; + + stats.inc (nano::stat::type::bootstrap, nano::stat::detail::reset); + logger.info (nano::log::type::bootstrap, "Resetting bootstrap state"); + + accounts.reset (); + database_scan.reset (); + frontiers.reset (); + scoring.reset (); + throttle.reset (); +} + bool nano::bootstrap_service::send (std::shared_ptr<nano::transport::channel> const & channel, async_tag tag) { debug_assert (tag.type != query_type::invalid); diff --git a/nano/node/bootstrap/bootstrap_service.hpp b/nano/node/bootstrap/bootstrap_service.hpp index f28d3fbe14..408980b57c 100644 --- a/nano/node/bootstrap/bootstrap_service.hpp +++ b/nano/node/bootstrap/bootstrap_service.hpp @@ -37,10 +37,15 @@ class bootstrap_service void stop (); /** - * Process `asc_pull_ack` message coming from network + * Process bootstrap messages coming from the network */ void process (nano::asc_pull_ack const & message, std::shared_ptr<nano::transport::channel> const &); + /** + * Clears priority and blocking accounts state + */ + void reset (); + std::size_t blocked_size () const; std::size_t priority_size () const; std::size_t score_size () const; diff --git a/nano/node/bootstrap/database_scan.cpp b/nano/node/bootstrap/database_scan.cpp index 0135129392..d985a2b48e 100644 --- a/nano/node/bootstrap/database_scan.cpp +++ b/nano/node/bootstrap/database_scan.cpp @@ -19,6 +19,17 @@ nano::bootstrap::database_scan::database_scan (nano::ledger & ledger_a) : { } +void nano::bootstrap::database_scan::reset () +{ + queue.clear (); + + account_scanner.next = nano::account{ 0 }; + account_scanner.completed = 0; + + pending_scanner.next = nano::account{ 0 }; + pending_scanner.completed = 0; +} + nano::account nano::bootstrap::database_scan::next (std::function<bool (nano::account const &)> const & filter) { if (queue.empty ()) diff --git a/nano/node/bootstrap/database_scan.hpp b/nano/node/bootstrap/database_scan.hpp index 758ad3efce..1dc932365c 100644 --- a/nano/node/bootstrap/database_scan.hpp +++ b/nano/node/bootstrap/database_scan.hpp @@ -39,6 +39,8 @@ class database_scan // Indicates if a full ledger iteration has taken place e.g. warmed up bool warmed_up () const; + void reset (); + nano::container_info container_info () const; private: // Dependencies diff --git a/nano/node/bootstrap/frontier_scan.cpp b/nano/node/bootstrap/frontier_scan.cpp index a65d64fa5e..4ab1240415 100644 --- a/nano/node/bootstrap/frontier_scan.cpp +++ b/nano/node/bootstrap/frontier_scan.cpp @@ -23,6 +23,16 @@ nano::bootstrap::frontier_scan::frontier_scan (frontier_scan_config const & conf release_assert (!heads.empty ()); } +void nano::bootstrap::frontier_scan::reset () +{ + for (auto it = heads.begin (); it != heads.end (); ++it) + { + heads.modify (it, [] (frontier_head & head) { + head.reset (); + }); + } +} + nano::account nano::bootstrap::frontier_scan::next () { auto const cutoff = std::chrono::steady_clock::now () - config.cooldown; diff --git a/nano/node/bootstrap/frontier_scan.hpp b/nano/node/bootstrap/frontier_scan.hpp index fe7682ff8e..df961c347d 100644 --- a/nano/node/bootstrap/frontier_scan.hpp +++ b/nano/node/bootstrap/frontier_scan.hpp @@ -31,6 +31,8 @@ class frontier_scan nano::account next (); bool process (nano::account start, std::deque<std::pair<nano::account, nano::block_hash>> const & response); + void reset (); + nano::container_info container_info () const; private: // Dependencies @@ -65,6 +67,16 @@ class frontier_scan { return start; } + + void reset () + { + next = start; + candidates.clear (); + requests = 0; + completed = 0; + timestamp = {}; + processed = 0; + } }; // clang-format off diff --git a/nano/node/bootstrap/peer_scoring.cpp b/nano/node/bootstrap/peer_scoring.cpp index 1e406ab962..29daf7225d 100644 --- a/nano/node/bootstrap/peer_scoring.cpp +++ b/nano/node/bootstrap/peer_scoring.cpp @@ -12,6 +12,11 @@ nano::bootstrap::peer_scoring::peer_scoring (bootstrap_config const & config_a, { } +void nano::bootstrap::peer_scoring::reset () +{ + scoring.clear (); +} + bool nano::bootstrap::peer_scoring::limit_exceeded (std::shared_ptr<nano::transport::channel> const & channel) const { auto & index = scoring.get<tag_channel> (); diff --git a/nano/node/bootstrap/peer_scoring.hpp b/nano/node/bootstrap/peer_scoring.hpp index 798015c543..09b46cf321 100644 --- a/nano/node/bootstrap/peer_scoring.hpp +++ b/nano/node/bootstrap/peer_scoring.hpp @@ -24,6 +24,8 @@ class peer_scoring public: peer_scoring (bootstrap_config const &, nano::network_constants const &); + void reset (); + // Returns true if channel limit has been exceeded bool limit_exceeded (std::shared_ptr<nano::transport::channel> const & channel) const; bool try_send_message (std::shared_ptr<nano::transport::channel> const & channel); @@ -52,10 +54,12 @@ class peer_scoring { public: explicit peer_score (std::shared_ptr<nano::transport::channel> const &, uint64_t, uint64_t, uint64_t); + std::weak_ptr<nano::transport::channel> channel; // std::weak_ptr does not provide ordering so the naked pointer is also tracked and used for ordering channels // This pointer may be invalid if the channel has been destroyed nano::transport::channel * channel_ptr; + // Acquire reference to the shared channel object if it is still valid [[nodiscard]] std::shared_ptr<nano::transport::channel> shared () const { @@ -66,10 +70,12 @@ class peer_scoring } return result; } + void decay () { outstanding = outstanding > 0 ? outstanding - 1 : 0; } + // Number of outstanding requests to a peer uint64_t outstanding{ 0 }; uint64_t request_count_total{ 0 }; diff --git a/nano/node/bootstrap/throttle.cpp b/nano/node/bootstrap/throttle.cpp index 422773d3f5..9df3721ab6 100644 --- a/nano/node/bootstrap/throttle.cpp +++ b/nano/node/bootstrap/throttle.cpp @@ -8,6 +8,12 @@ nano::bootstrap::throttle::throttle (std::size_t size) : debug_assert (size > 0); } +void nano::bootstrap::throttle::reset () +{ + successes_m = samples.size (); + std::fill (samples.begin (), samples.end (), true); +} + bool nano::bootstrap::throttle::throttled () const { return successes_m == 0; diff --git a/nano/node/bootstrap/throttle.hpp b/nano/node/bootstrap/throttle.hpp index a688e54d55..f71e5a9ad0 100644 --- a/nano/node/bootstrap/throttle.hpp +++ b/nano/node/bootstrap/throttle.hpp @@ -11,17 +11,23 @@ class throttle public: // Initialized with all true samples explicit throttle (std::size_t size); + [[nodiscard]] bool throttled () const; void add (bool success); + // Resizes the number of samples tracked // Drops the oldest samples if the size decreases // Adds false samples if the size increases void resize (std::size_t size); + [[nodiscard]] std::size_t size () const; [[nodiscard]] std::size_t successes () const; + void reset (); + private: void pop (); + // Bit set that tracks sample results. True when something was retrieved, false otherwise std::deque<bool> samples; std::size_t successes_m; diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index fcdca6a24c..6e9547aef6 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -5194,6 +5194,13 @@ void nano::json_handler::debug_bootstrap_priority_info () response_errors (); } +void nano::json_handler::bootstrap_reset () +{ + node.bootstrap.reset (); + response_l.put ("success", ""); + response_errors (); +} + void nano::inprocess_rpc_handler::process_request (std::string const &, std::string const & body_a, std::function<void (std::string const &)> response_a) { // Note that if the rpc action is async, the shared_ptr<json_handler> lifetime will be extended by the action handler @@ -5360,6 +5367,7 @@ ipc_json_handler_no_arg_func_map create_ipc_json_handler_no_arg_func_map () no_arg_funcs.emplace ("work_peers_clear", &nano::json_handler::work_peers_clear); no_arg_funcs.emplace ("populate_backlog", &nano::json_handler::populate_backlog); no_arg_funcs.emplace ("debug_bootstrap_priority_info", &nano::json_handler::debug_bootstrap_priority_info); + no_arg_funcs.emplace ("bootstrap_reset", &nano::json_handler::bootstrap_reset); return no_arg_funcs; } diff --git a/nano/node/json_handler.hpp b/nano/node/json_handler.hpp index 5780054871..b45b446782 100644 --- a/nano/node/json_handler.hpp +++ b/nano/node/json_handler.hpp @@ -71,6 +71,7 @@ class json_handler : public std::enable_shared_from_this<nano::json_handler> void confirmation_info (); void confirmation_quorum (); void debug_bootstrap_priority_info (); + void bootstrap_reset (); void database_txn_tracker (); void delegators (); void delegators_count (); diff --git a/nano/rpc/rpc_handler.cpp b/nano/rpc/rpc_handler.cpp index 4d4a6f36dc..804c38f300 100644 --- a/nano/rpc/rpc_handler.cpp +++ b/nano/rpc/rpc_handler.cpp @@ -152,6 +152,8 @@ std::unordered_set<std::string> create_rpc_control_impls () set.emplace ("backoff_info"); set.emplace ("block_create"); set.emplace ("bootstrap_lazy"); + set.emplace ("bootstrap_reset"); + set.emplace ("debug_bootstrap_priority_info"); set.emplace ("database_txn_tracker"); set.emplace ("epoch_upgrade"); set.emplace ("keepalive"); From b683a6179b7c874cfbabf79160a4c997b83ae65b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Mon, 6 Jan 2025 19:57:13 +0100 Subject: [PATCH 2/6] Extend bootstrap priorities info rpc --- nano/node/json_handler.cpp | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 6e9547aef6..64f40ec068 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -5166,29 +5166,32 @@ void nano::json_handler::debug_bootstrap_priority_info () { auto [blocking, priorities] = node.bootstrap.info (); - // priorities + // Priorities { - boost::property_tree::ptree response_priorities; + boost::property_tree::ptree response_l; for (auto const & entry : priorities) { - const auto account = entry.account; - const auto priority = entry.priority; + boost::property_tree::ptree entry_l; + entry_l.put ("account", entry.account.to_account ()); + entry_l.put ("priority", entry.priority); - response_priorities.put (account.to_account (), priority); + response_l.push_back (std::make_pair ("", entry_l)); } - response_l.add_child ("priorities", response_priorities); + response_l.add_child ("priorities", response_l); } - // blocking + // Blocking { - boost::property_tree::ptree response_blocking; + boost::property_tree::ptree response_l; for (auto const & entry : blocking) { - const auto account = entry.account; - const auto dependency = entry.dependency; + boost::property_tree::ptree entry_l; + entry_l.put ("account", entry.account.to_account ()); + entry_l.put ("dependency", entry.dependency.to_string ()); + entry_l.put ("dependency_account", entry.dependency_account.to_account ()); - response_blocking.put (account.to_account (), dependency.to_string ()); + response_l.push_back (std::make_pair ("", entry_l)); } - response_l.add_child ("blocking", response_blocking); + response_l.add_child ("blocking", response_l); } } response_errors (); From 0cce7967f1319fa97afdbcb0b9cb4a3834274528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Tue, 7 Jan 2025 15:46:11 +0100 Subject: [PATCH 3/6] Rename rpc command `debug_bootstrap_priority_info` to `bootstrap_priorities` --- nano/node/json_handler.cpp | 4 ++-- nano/node/json_handler.hpp | 2 +- nano/rpc/rpc_handler.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 64f40ec068..4fe43b3b92 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -5160,7 +5160,7 @@ void nano::json_handler::populate_backlog () response_errors (); } -void nano::json_handler::debug_bootstrap_priority_info () +void nano::json_handler::bootstrap_priorities () { if (!ec) { @@ -5369,7 +5369,7 @@ ipc_json_handler_no_arg_func_map create_ipc_json_handler_no_arg_func_map () no_arg_funcs.emplace ("work_peers", &nano::json_handler::work_peers); no_arg_funcs.emplace ("work_peers_clear", &nano::json_handler::work_peers_clear); no_arg_funcs.emplace ("populate_backlog", &nano::json_handler::populate_backlog); - no_arg_funcs.emplace ("debug_bootstrap_priority_info", &nano::json_handler::debug_bootstrap_priority_info); + no_arg_funcs.emplace ("bootstrap_priorities", &nano::json_handler::bootstrap_priorities); no_arg_funcs.emplace ("bootstrap_reset", &nano::json_handler::bootstrap_reset); return no_arg_funcs; } diff --git a/nano/node/json_handler.hpp b/nano/node/json_handler.hpp index b45b446782..22ab99ad9f 100644 --- a/nano/node/json_handler.hpp +++ b/nano/node/json_handler.hpp @@ -70,7 +70,7 @@ class json_handler : public std::enable_shared_from_this<nano::json_handler> void confirmation_history (); void confirmation_info (); void confirmation_quorum (); - void debug_bootstrap_priority_info (); + void bootstrap_priorities (); void bootstrap_reset (); void database_txn_tracker (); void delegators (); diff --git a/nano/rpc/rpc_handler.cpp b/nano/rpc/rpc_handler.cpp index 804c38f300..053140ebaa 100644 --- a/nano/rpc/rpc_handler.cpp +++ b/nano/rpc/rpc_handler.cpp @@ -153,7 +153,7 @@ std::unordered_set<std::string> create_rpc_control_impls () set.emplace ("block_create"); set.emplace ("bootstrap_lazy"); set.emplace ("bootstrap_reset"); - set.emplace ("debug_bootstrap_priority_info"); + set.emplace ("bootstrap_priorities"); set.emplace ("database_txn_tracker"); set.emplace ("epoch_upgrade"); set.emplace ("keepalive"); From 0b56b3953b37b97544b2d4b074022caf1d43e3ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Tue, 7 Jan 2025 17:21:59 +0100 Subject: [PATCH 4/6] Implement `bootstrap_status` rpc --- nano/node/bootstrap/bootstrap_service.cpp | 9 +++++++++ nano/node/bootstrap/bootstrap_service.hpp | 11 +++++++++-- nano/node/json_handler.cpp | 7 ++++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/nano/node/bootstrap/bootstrap_service.cpp b/nano/node/bootstrap/bootstrap_service.cpp index 13727436e3..5c98ba0c85 100644 --- a/nano/node/bootstrap/bootstrap_service.cpp +++ b/nano/node/bootstrap/bootstrap_service.cpp @@ -1164,6 +1164,15 @@ auto nano::bootstrap_service::info () const -> nano::bootstrap::account_sets::in return accounts.info (); } +auto nano::bootstrap_service::status () const -> status_result +{ + nano::lock_guard<nano::mutex> lock{ mutex }; + return { + .priorities = accounts.priority_size (), + .blocking = accounts.blocked_size (), + }; +} + std::size_t nano::bootstrap_service::compute_throttle_size () const { auto ledger_size = ledger.account_count (); diff --git a/nano/node/bootstrap/bootstrap_service.hpp b/nano/node/bootstrap/bootstrap_service.hpp index 408980b57c..ee9e31cc33 100644 --- a/nano/node/bootstrap/bootstrap_service.hpp +++ b/nano/node/bootstrap/bootstrap_service.hpp @@ -53,10 +53,17 @@ class bootstrap_service bool prioritized (nano::account const &) const; bool blocked (nano::account const &) const; - nano::container_info container_info () const; - nano::bootstrap::account_sets::info_t info () const; + struct status_result + { + size_t priorities; + size_t blocking; + }; + status_result status () const; + + nano::container_info container_info () const; + private: // Dependencies bootstrap_config const & config; nano::network_constants const & network_constants; diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 4fe43b3b92..091482b39b 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -1843,7 +1843,12 @@ void nano::json_handler::bootstrap_lazy () */ void nano::json_handler::bootstrap_status () { - // TODO: Bootstrap status for ascending bootstrap + auto status = node.bootstrap.status (); + + // Only summary information + response_l.put ("priorities", status.priorities); + response_l.put ("blocking", status.blocking); + response_errors (); } From bff6d3513759849db01e089ea4739945799c5616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Tue, 7 Jan 2025 15:48:52 +0100 Subject: [PATCH 5/6] Cleanup --- nano/node/json_handler.cpp | 99 ++++++++++++++++++++------------------ nano/node/json_handler.hpp | 6 +-- 2 files changed, 54 insertions(+), 51 deletions(-) diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 091482b39b..ce399d2db1 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -1831,10 +1831,7 @@ void nano::json_handler::bootstrap_lazy () { auto hash (hash_impl ()); bool const force = request.get<bool> ("force", false); - if (!ec) - { - ec = nano::error_rpc::disabled_bootstrap_lazy; - } + ec = nano::error_rpc::disabled_bootstrap_lazy; response_errors (); } @@ -1852,6 +1849,56 @@ void nano::json_handler::bootstrap_status () response_errors (); } +/* + * @warning This is an internal/diagnostic RPC, do not rely on its interface being stable + */ +void nano::json_handler::bootstrap_priorities () +{ + if (!ec) + { + auto [blocking, priorities] = node.bootstrap.info (); + + // Priorities + { + boost::property_tree::ptree resp_l; + for (auto const & entry : priorities) + { + boost::property_tree::ptree entry_l; + entry_l.put ("account", entry.account.to_account ()); + entry_l.put ("priority", entry.priority); + + resp_l.push_back (std::make_pair ("", entry_l)); + } + response_l.add_child ("priorities", resp_l); + } + // Blocking + { + boost::property_tree::ptree resp_l; + for (auto const & entry : blocking) + { + boost::property_tree::ptree entry_l; + entry_l.put ("account", entry.account.to_account ()); + entry_l.put ("dependency", entry.dependency.to_string ()); + entry_l.put ("dependency_account", entry.dependency_account.to_account ()); + + resp_l.push_back (std::make_pair ("", entry_l)); + } + response_l.add_child ("blocking", resp_l); + } + } + response_errors (); +} + +/* + * @warning This is an internal/diagnostic RPC, do not rely on its interface being stable + */ +void nano::json_handler::bootstrap_reset () +{ + node.bootstrap.reset (); + response_l.put ("success", ""); + response_errors (); +} + void nano::json_handler::chain (bool successors) { successors = successors != request.get<bool> ("reverse", false); @@ -5165,50 +5212,6 @@ void nano::json_handler::populate_backlog () response_errors (); } -void nano::json_handler::bootstrap_priorities () -{ - if (!ec) - { - auto [blocking, priorities] = node.bootstrap.info (); - - // Priorities - { - boost::property_tree::ptree response_l; - for (auto const & entry : priorities) - { - boost::property_tree::ptree entry_l; - entry_l.put ("account", entry.account.to_account ()); - entry_l.put ("priority", entry.priority); - - response_l.push_back (std::make_pair ("", entry_l)); - } - response_l.add_child ("priorities", response_l); - } - // Blocking - { - boost::property_tree::ptree response_l; - for (auto const & entry : blocking) - { - boost::property_tree::ptree entry_l; - entry_l.put ("account", entry.account.to_account ()); - entry_l.put ("dependency", entry.dependency.to_string ()); - entry_l.put ("dependency_account", entry.dependency_account.to_account ()); - - response_l.push_back (std::make_pair ("", entry_l)); - } - response_l.add_child ("blocking", response_l); - } - } - response_errors (); -} - -void nano::json_handler::bootstrap_reset () -{ - node.bootstrap.reset (); - response_l.put ("success", ""); - response_errors (); -} - void nano::inprocess_rpc_handler::process_request (std::string const &, std::string const & body_a, std::function<void (std::string const &)> response_a) { // Note that if the rpc action is async, the shared_ptr<json_handler> lifetime will be extended by the action handler diff --git a/nano/node/json_handler.hpp b/nano/node/json_handler.hpp index 22ab99ad9f..95aa314fd4 100644 --- a/nano/node/json_handler.hpp +++ b/nano/node/json_handler.hpp @@ -65,13 +65,13 @@ class json_handler : public std::enable_shared_from_this<nano::json_handler> void bootstrap_any (); void bootstrap_lazy (); void bootstrap_status (); - void chain (bool = false); + void bootstrap_priorities (); + void bootstrap_reset (); + void chain (bool successors = false); void confirmation_active (); void confirmation_history (); void confirmation_info (); void confirmation_quorum (); - void bootstrap_priorities (); - void bootstrap_reset (); void database_txn_tracker (); void delegators (); void delegators_count (); From f9d7c76a6824e92f3432a104fc4f29454660af05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Tue, 7 Jan 2025 16:15:27 +0100 Subject: [PATCH 6/6] Tests --- nano/core_test/bootstrap.cpp | 56 +++++++++++++++--- nano/node/node.cpp | 10 ++++ nano/node/node.hpp | 4 +- nano/rpc_test/rpc.cpp | 108 +++++++++++++++++++++++++++++++++++ 4 files changed, 168 insertions(+), 10 deletions(-) diff --git a/nano/core_test/bootstrap.cpp b/nano/core_test/bootstrap.cpp index 8d866031b0..b2c17a6744 100644 --- a/nano/core_test/bootstrap.cpp +++ b/nano/core_test/bootstrap.cpp @@ -270,18 +270,12 @@ TEST (bootstrap, trace_base) .sign (key.prv, key.pub) .work (*system.work.generate (key.pub)) .build (); - // std::cerr << "Genesis key: " << nano::dev::genesis_key.pub.to_account () << std::endl; - // std::cerr << "Key: " << key.pub.to_account () << std::endl; - // std::cerr << "Genesis: " << nano::dev::genesis->hash ().to_string () << std::endl; - // std::cerr << "send1: " << send1->hash ().to_string () << std::endl; - // std::cerr << "receive1: " << receive1->hash ().to_string () << std::endl; auto & node1 = *system.add_node (); - // 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.ledger.any.receivable_end (), node1.ledger.any.receivable_upper_bound (node1.ledger.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); } @@ -573,4 +567,48 @@ TEST (bootstrap, frontier_scan_cannot_prioritize) })); } -// TODO: Test bootstrap.reset () \ No newline at end of file +/* + * Tests that bootstrap.reset() properly recovers when called during an ongoing bootstrap + * This mimics node restart behaviour so this also ensures that the bootstrap is able to recover from a node restart + */ +TEST (bootstrap, reset) +{ + nano::test::system system; + + // Test configuration + int const chain_count = 10; + int const blocks_per_chain = 5; + + nano::node_config config; + // Disable election activation + config.backlog_scan.enable = false; + config.priority_scheduler.enable = false; + config.optimistic_scheduler.enable = false; + config.hinted_scheduler.enable = false; + // Add request limits to slow down bootstrap + config.bootstrap.rate_limit = 30; + + // Start server node + auto & node_server = *system.add_node (config); + + // Create multiple chains of blocks + auto chains = nano::test::setup_chains (system, node_server, chain_count, blocks_per_chain); + + int const total_blocks = node_server.block_count (); + int const halfway_blocks = total_blocks / 2; + + // Start client node and begin bootstrap + auto & node_client = *system.add_node (config); + ASSERT_LE (node_client.block_count (), halfway_blocks); // Should not be synced yet + + // Wait until bootstrap has started and processed some blocks but not all + // Target approximately halfway through + ASSERT_TIMELY (15s, node_client.block_count () >= halfway_blocks); + ASSERT_LT (node_client.block_count (), total_blocks); + + // Reset bootstrap halfway through the process + node_client.bootstrap.reset (); + + // Bootstrap should automatically restart and eventually sync all blocks + ASSERT_TIMELY_EQ (30s, node_client.block_count (), total_blocks); +} \ No newline at end of file diff --git a/nano/node/node.cpp b/nano/node/node.cpp index b6838515fa..96847d1c37 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -1105,6 +1105,16 @@ void nano::node::bootstrap_block (const nano::block_hash & hash) } } +uint64_t nano::node::block_count () const +{ + return ledger.block_count (); +} + +uint64_t nano::node::cemented_count () const +{ + return ledger.cemented_count (); +} + nano::account nano::node::get_node_id () const { return node_id.pub; diff --git a/nano/node/node.hpp b/nano/node/node.hpp index fc6581829a..6b58ff1548 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -77,11 +77,13 @@ class node final : public std::enable_shared_from_this<node> void add_initial_peers (); void start_election (std::shared_ptr<nano::block> const & block); bool block_confirmed (nano::block_hash const &); - // This function may spuriously return false after returning true until the database transaction is refreshed bool block_confirmed_or_being_confirmed (nano::secure::transaction const &, nano::block_hash const &); bool block_confirmed_or_being_confirmed (nano::block_hash const &); + uint64_t block_count () const; + uint64_t cemented_count () const; + void do_rpc_callback (boost::asio::ip::tcp::resolver::iterator i_a, std::string const &, uint16_t, std::shared_ptr<std::string> const &, std::shared_ptr<std::string> const &, std::shared_ptr<boost::asio::ip::tcp::resolver> const &); bool online () const; bool init_error () const; diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 82c9837d5b..f3a2e50644 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -6876,3 +6876,111 @@ TEST (rpc, election_statistics) ASSERT_LT (response.get<int> ("max_election_age"), 5000); ASSERT_LT (response.get<int> ("average_election_age"), 5000); } + +TEST (rpc, bootstrap_priorities) +{ + nano::test::system system; + + // Test configuration + int const chain_count = 5; + int const blocks_per_chain = 2; + + nano::node_config config; + config.bootstrap.rate_limit = 10; // Add request limits to slow down bootstrap + + // Start server node + auto & node_server = *system.add_node (config); + + // Create multiple chains of blocks + auto chains = nano::test::setup_chains (system, node_server, chain_count, blocks_per_chain); + + int const total_blocks = node_server.block_count (); + int const halfway_blocks = total_blocks / 2; + + // Start client node to observe bootstrap + auto node = add_ipc_enabled_node (system); + auto const rpc_ctx = add_rpc (system, node); + + // Wait until bootstrap has started and processed some blocks but not all + // Target approximately halfway through + ASSERT_TIMELY (15s, node->block_count () >= halfway_blocks); + + boost::property_tree::ptree request; + request.put ("action", "bootstrap_priorities"); + auto response = wait_response (system, rpc_ctx, request); + + ASSERT_TRUE (response.get_child_optional ("priorities")); + ASSERT_TRUE (response.get_child_optional ("blocking")); + ASSERT_FALSE (response.get_child_optional ("priorities").value ().empty ()); +} + +TEST (rpc, bootstrap_reset) +{ + nano::test::system system; + + // Test configuration + int const chain_count = 5; + int const blocks_per_chain = 2; + + nano::node_config config; + config.bootstrap.rate_limit = 10; // Add request limits to slow down bootstrap + + // Start server node + auto & node_server = *system.add_node (config); + + // Create multiple chains of blocks + auto chains = nano::test::setup_chains (system, node_server, chain_count, blocks_per_chain); + + int const total_blocks = node_server.block_count (); + int const halfway_blocks = total_blocks / 2; + + // Start client node to observe bootstrap + auto node = add_ipc_enabled_node (system); + auto const rpc_ctx = add_rpc (system, node); + + // Wait until bootstrap has started and processed some blocks but not all + // Target approximately halfway through + ASSERT_TIMELY (15s, node->block_count () >= halfway_blocks); + + boost::property_tree::ptree request; + request.put ("action", "bootstrap_reset"); + auto response = wait_response (system, rpc_ctx, request); + + ASSERT_TRUE (response.get<std::string> ("success").empty ()); +} + +TEST (rpc, bootstrap_status) +{ + nano::test::system system; + + // Test configuration + int const chain_count = 5; + int const blocks_per_chain = 2; + + nano::node_config config; + config.bootstrap.rate_limit = 10; // Add request limits to slow down bootstrap + + // Start server node + auto & node_server = *system.add_node (config); + + // Create multiple chains of blocks + auto chains = nano::test::setup_chains (system, node_server, chain_count, blocks_per_chain); + + int const total_blocks = node_server.block_count (); + int const halfway_blocks = total_blocks / 2; + + // Start client node to observe bootstrap + auto node = add_ipc_enabled_node (system); + auto const rpc_ctx = add_rpc (system, node); + + // Wait until bootstrap has started and processed some blocks but not all + // Target approximately halfway through + ASSERT_TIMELY (15s, node->block_count () >= halfway_blocks); + + boost::property_tree::ptree request; + request.put ("action", "bootstrap_status"); + auto response = wait_response (system, rpc_ctx, request); + + ASSERT_GT (response.get<int> ("priorities"), 0); + ASSERT_EQ (response.get<int> ("blocking"), 0); +} \ No newline at end of file