From ab50aff66dad6d9f32373faeb5c4a0da1fdf746b Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Fri, 11 Oct 2019 17:08:39 +0100 Subject: [PATCH] Active elections loop tuning (#2306) * Active elections tuning and removing per-block confirm_req Co-authored-by: Srayman * Dont broadcast and request confirmation in the same loop for the same election * Revert socket queue_size_max to original 128, to be increased via another PR * Use non-doxygen in-definition * Consistent loop initialization * Move the alternating condition check upwards * Fix incorrect documentation on max 16 representatives from rep_crawler * Cleanup rep_crawler public methods * Weight-ordered confirmation requests * Rename confirm_frontiers to search_frontiers and split request_confirm into escalate/broadcast/confirm_req methods * clang-format * Add missing bundle insertion check and fix test * More block broadcasting in tests --- nano/core_test/active_transactions.cpp | 88 ++--- nano/core_test/confirmation_height.cpp | 2 +- nano/core_test/node.cpp | 18 +- nano/lib/config.hpp | 2 +- nano/node/active_transactions.cpp | 454 +++++++++++++------------ nano/node/active_transactions.hpp | 30 +- nano/node/blockprocessor.cpp | 24 +- nano/node/election.cpp | 10 +- nano/node/election.hpp | 5 +- nano/node/json_handler.cpp | 2 +- nano/node/network.cpp | 11 +- nano/node/node.cpp | 4 +- nano/node/nodeconfig.cpp | 3 +- nano/node/nodeconfig.hpp | 2 +- nano/node/repcrawler.cpp | 13 - nano/node/repcrawler.hpp | 9 +- 16 files changed, 334 insertions(+), 343 deletions(-) diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index 77bc416e..bb913b65 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -22,15 +22,15 @@ TEST (active_transactions, bounded_active_elections) { node.process_active (send); node.active.start (send); + ASSERT_NO_ERROR (system.poll ()); ASSERT_FALSE (node.active.empty ()); ASSERT_LE (node.active.size (), node.config.active_elections_size); + ++count; done = count > node.active.size (); - count++; - ASSERT_NO_ERROR (system.poll ()); auto previous_hash = send->hash (); send = std::make_shared (nano::test_genesis_key.pub, previous_hash, nano::test_genesis_key.pub, nano::genesis_amount - count * nano::xrb_ratio, nano::test_genesis_key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (previous_hash)); - //sleep this thread for the max delay between request loop rounds possible for such a small active_elections_size - std::this_thread::sleep_for (std::chrono::milliseconds (node.network_params.network.request_interval_ms + (node_config.active_elections_size * 20))); + //sleep this thread between request loop rounds + std::this_thread::sleep_for (std::chrono::milliseconds (2 * node.network_params.network.request_interval_ms)); } } @@ -246,51 +246,50 @@ TEST (active_transactions, keep_local) nano::system system; nano::node_config node_config (24000, system.logging); node_config.enable_voting = false; - node_config.active_elections_size = 3; //bound to 3, wont drop wallet created transactions, but good to test dropping remote + node_config.active_elections_size = 2; //bound to 2, wont drop wallet created transactions, but good to test dropping remote // Disable frontier confirmation to allow the test to finish before node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - auto & node1 = *system.add_node (node_config); + auto & node = *system.add_node (node_config); auto & wallet (*system.wallet (0)); nano::genesis genesis; //key 1/2 will be managed by the wallet - nano::keypair key1, key2, key3, key4; + nano::keypair key1, key2, key3, key4, key5, key6; wallet.insert_adhoc (nano::test_genesis_key.prv); - wallet.insert_adhoc (key1.prv); - wallet.insert_adhoc (key2.prv); - auto send1 (wallet.send_action (nano::test_genesis_key.pub, key1.pub, node1.config.receive_minimum.number ())); - auto send2 (wallet.send_action (nano::test_genesis_key.pub, key2.pub, node1.config.receive_minimum.number ())); - auto send3 (wallet.send_action (nano::test_genesis_key.pub, key3.pub, node1.config.receive_minimum.number ())); - auto send4 (wallet.send_action (nano::test_genesis_key.pub, key4.pub, node1.config.receive_minimum.number ())); + auto send1 (wallet.send_action (nano::test_genesis_key.pub, key1.pub, node.config.receive_minimum.number ())); + auto send2 (wallet.send_action (nano::test_genesis_key.pub, key2.pub, node.config.receive_minimum.number ())); + auto send3 (wallet.send_action (nano::test_genesis_key.pub, key3.pub, node.config.receive_minimum.number ())); + auto send4 (wallet.send_action (nano::test_genesis_key.pub, key4.pub, node.config.receive_minimum.number ())); + auto send5 (wallet.send_action (nano::test_genesis_key.pub, key5.pub, node.config.receive_minimum.number ())); + auto send6 (wallet.send_action (nano::test_genesis_key.pub, key6.pub, node.config.receive_minimum.number ())); system.deadline_set (10s); - while (node1.active.size () != 4) + // should not drop wallet created transactions + while (node.active.size () != 6) { ASSERT_NO_ERROR (system.poll ()); } - while (node1.active.size () != 0) + while (!node.active.empty ()) { - nano::lock_guard active_guard (node1.active.mutex); - auto it (node1.active.roots.begin ()); - while (!node1.active.roots.empty () && it != node1.active.roots.end ()) + nano::lock_guard active_guard (node.active.mutex); + auto it (node.active.roots.begin ()); + while (!node.active.roots.empty () && it != node.active.roots.end ()) { (it->election)->confirm_once (); - it = node1.active.roots.begin (); + it = node.active.roots.begin (); } } - auto open1 (std::make_shared (key3.pub, 0, key3.pub, nano::xrb_ratio, send3->hash (), key3.prv, key3.pub, *system.work.generate (key3.pub))); - node1.process_active (open1); - auto open2 (std::make_shared (key4.pub, 0, key4.pub, nano::xrb_ratio, send4->hash (), key4.prv, key4.pub, *system.work.generate (key4.pub))); - node1.process_active (open2); - //none are dropped since none are long_unconfirmed + auto open1 (std::make_shared (key1.pub, 0, key1.pub, node.config.receive_minimum.number (), send1->hash (), key1.prv, key1.pub, *system.work.generate (key1.pub))); + node.process_active (open1); + node.active.start (open1); + auto open2 (std::make_shared (key2.pub, 0, key2.pub, node.config.receive_minimum.number (), send2->hash (), key2.prv, key2.pub, *system.work.generate (key2.pub))); + node.process_active (open2); + node.active.start (open2); + auto open3 (std::make_shared (key3.pub, 0, key3.pub, node.config.receive_minimum.number (), send3->hash (), key3.prv, key3.pub, *system.work.generate (key3.pub))); + node.process_active (open3); + node.active.start (open3); + ASSERT_EQ (3, node.active.size ()); system.deadline_set (10s); - while (node1.active.size () != 4) - { - ASSERT_NO_ERROR (system.poll ()); - } - auto send5 (wallet.send_action (nano::test_genesis_key.pub, key1.pub, node1.config.receive_minimum.number ())); - node1.active.start (send5); - //drop two lowest non-wallet managed active_transactions before inserting a new into active as all are long_unconfirmed - system.deadline_set (10s); - while (node1.active.size () != 3) + // bound elections, should drop after one loop + while (node.active.size () != node_config.active_elections_size) { ASSERT_NO_ERROR (system.poll ()); } @@ -301,7 +300,7 @@ TEST (active_transactions, prioritize_chains) nano::system system; nano::node_config node_config (24000, system.logging); node_config.enable_voting = false; - node_config.active_elections_size = 4; //bound to 3, wont drop wallet created transactions, but good to test dropping remote + node_config.active_elections_size = 4; //bound to 4, wont drop wallet created transactions, but good to test dropping remote // Disable frontier confirmation to allow the test to finish before node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; auto & node1 = *system.add_node (node_config); @@ -352,16 +351,6 @@ TEST (active_transactions, prioritize_chains) ASSERT_NO_ERROR (system.poll ()); } system.deadline_set (10s); - bool done (false); - //wait for all to be long_unconfirmed - while (!done) - { - { - nano::lock_guard guard (node1.active.mutex); - done = node1.active.long_unconfirmed_size == 4; - } - ASSERT_NO_ERROR (system.poll ()); - } std::this_thread::sleep_for (1s); node1.process_active (open2); system.deadline_set (10s); @@ -369,17 +358,6 @@ TEST (active_transactions, prioritize_chains) { ASSERT_NO_ERROR (system.poll ()); } - //wait for all to be long_unconfirmed - done = false; - system.deadline_set (10s); - while (!done) - { - { - nano::lock_guard guard (node1.active.mutex); - done = node1.active.long_unconfirmed_size == 4; - } - ASSERT_NO_ERROR (system.poll ()); - } size_t seen (0); { auto it (node1.active.roots.get<1> ().begin ()); diff --git a/nano/core_test/confirmation_height.cpp b/nano/core_test/confirmation_height.cpp index 542b85d2..7851e586 100644 --- a/nano/core_test/confirmation_height.cpp +++ b/nano/core_test/confirmation_height.cpp @@ -895,7 +895,7 @@ TEST (confirmation_height, prioritize_frontiers) transaction.refresh (); node->active.prioritize_frontiers_for_confirmation (transaction, std::chrono::seconds (1), std::chrono::seconds (1)); ASSERT_TRUE (priority_orders_match (node->active.priority_wallet_cementable_frontiers, std::array{ key3.pub, nano::genesis_account, key4.pub, key1.pub, key2.pub })); - node->active.confirm_frontiers (transaction); + node->active.search_frontiers (transaction); // Check that the active transactions roots contains the frontiers system.deadline_set (std::chrono::seconds (10)); diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index 135a0673..516adb33 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -205,7 +205,7 @@ TEST (node, node_receive_quorum) nano::lock_guard guard (system.nodes[0]->active.mutex); auto info (system.nodes[0]->active.roots.find (nano::qualified_root (previous, previous))); ASSERT_NE (system.nodes[0]->active.roots.end (), info); - done = info->election->confirmation_request_count > nano::active_transactions::minimum_confirmation_request_count; + done = info->election->confirmation_request_count > 2; } ASSERT_NO_ERROR (system.poll ()); } @@ -850,11 +850,21 @@ TEST (node_config, v17_v18_upgrade) auto upgraded (false); nano::node_config config; config.logging.init (path); + + // Initial values for configs that should be upgraded + config.active_elections_size = 50000; + config.vote_generator_delay = 500ms; + // These config options should not be present ASSERT_FALSE (tree.get_optional_child ("backup_before_upgrade")); ASSERT_FALSE (tree.get_optional_child ("work_watcher_period")); config.deserialize_json (upgraded, tree); + + // These configs should have been upgraded + ASSERT_EQ (100, tree.get ("vote_generator_delay")); + ASSERT_EQ (10000, tree.get ("active_elections_size")); + // The config options should be added after the upgrade ASSERT_TRUE (!!tree.get_optional_child ("backup_before_upgrade")); ASSERT_TRUE (!!tree.get_optional_child ("work_watcher_period")); @@ -878,6 +888,7 @@ TEST (node_config, v18_values) // Check config is correct { + tree.put ("active_elections_size", 10000); tree.put ("vote_generator_delay", 100); tree.put ("backup_before_upgrade", true); tree.put ("work_watcher_period", 5); @@ -885,11 +896,13 @@ TEST (node_config, v18_values) config.deserialize_json (upgraded, tree); ASSERT_FALSE (upgraded); + ASSERT_EQ (config.active_elections_size, 10000); ASSERT_EQ (config.vote_generator_delay.count (), 100); ASSERT_EQ (config.backup_before_upgrade, true); ASSERT_EQ (config.work_watcher_period.count (), 5); // Check config is correct with other values + tree.put ("active_elections_size", 5); tree.put ("vote_generator_delay", std::numeric_limits::max () - 100); tree.put ("backup_before_upgrade", false); tree.put ("work_watcher_period", 999); @@ -897,6 +910,7 @@ TEST (node_config, v18_values) upgraded = false; config.deserialize_json (upgraded, tree); ASSERT_FALSE (upgraded); + ASSERT_EQ (config.active_elections_size, 5); ASSERT_EQ (config.vote_generator_delay.count (), std::numeric_limits::max () - 100); ASSERT_EQ (config.backup_before_upgrade, false); ASSERT_EQ (config.work_watcher_period.count (), 999); @@ -2315,7 +2329,7 @@ TEST (node, confirm_quorum) nano::lock_guard guard (system.nodes[0]->active.mutex); auto info (system.nodes[0]->active.roots.find (nano::qualified_root (send1->hash (), send1->hash ()))); ASSERT_NE (system.nodes[0]->active.roots.end (), info); - done = info->election->confirmation_request_count > nano::active_transactions::minimum_confirmation_request_count; + done = info->election->confirmation_request_count > 2; } ASSERT_NO_ERROR (system.poll ()); } diff --git a/nano/lib/config.hpp b/nano/lib/config.hpp index 22585761..4c29c3a1 100644 --- a/nano/lib/config.hpp +++ b/nano/lib/config.hpp @@ -69,7 +69,7 @@ public: default_rpc_port = is_live_network () ? 7076 : is_beta_network () ? 55000 : 45000; default_ipc_port = is_live_network () ? 7077 : is_beta_network () ? 56000 : 46000; default_websocket_port = is_live_network () ? 7078 : is_beta_network () ? 57000 : 47000; - request_interval_ms = is_test_network () ? (is_sanitizer_build ? 100 : 20) : 16000; + request_interval_ms = is_test_network () ? (is_sanitizer_build ? 100 : 20) : 500; } /** Network work thresholds. ~5 seconds of work for the live network */ diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index b9647afd..bb4f38b0 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -5,12 +5,13 @@ #include -size_t constexpr nano::active_transactions::max_broadcast_queue; - using namespace std::chrono; nano::active_transactions::active_transactions (nano::node & node_a) : node (node_a), +long_election_threshold (node.network_params.network.is_test_network () ? 2s : 24s), +election_request_delay (node.network_params.network.is_test_network () ? 0s : 1s), +election_time_to_live (node.network_params.network.is_test_network () ? 0s : 10s), multipliers_cb (20, 1.), trended_active_difficulty (node.network_params.network.publish_threshold), next_frontier_check (steady_clock::now ()), @@ -28,27 +29,27 @@ nano::active_transactions::~active_transactions () stop (); } -void nano::active_transactions::confirm_frontiers (nano::transaction const & transaction_a) +void nano::active_transactions::search_frontiers (nano::transaction const & transaction_a) { // Limit maximum count of elections to start bool representative (node.config.enable_voting && node.wallets.reps_count > 0); bool half_princpal_representative (representative && node.wallets.half_principal_reps_count > 0); /* Check less frequently for regular nodes in auto mode */ bool agressive_mode (half_princpal_representative || node.config.frontiers_confirmation == nano::frontiers_confirmation_mode::always); - auto agressive_factor = agressive_mode ? 3min : 15min; + auto request_interval (std::chrono::milliseconds (node.network_params.network.request_interval_ms)); + auto agressive_factor = request_interval * (agressive_mode ? 20 : 100); // Decrease check time for test network auto is_test_network = node.network_params.network.is_test_network (); int test_network_factor = is_test_network ? 1000 : 1; auto roots_size = size (); - auto max_elections = (max_broadcast_queue / 4); nano::unique_lock lk (mutex); auto check_time_exceeded = std::chrono::steady_clock::now () >= next_frontier_check; lk.unlock (); + auto max_elections = (node.config.active_elections_size / 20); auto low_active_elections = roots_size < max_elections; bool wallets_check_required = (!skip_wallets || !priority_wallet_cementable_frontiers.empty ()) && !agressive_mode; - // To minimise dropping real-time transactions, set the maximum number of elections - // for cementing frontiers to half the total active election maximum. - const auto max_active = node.config.active_elections_size / 2; + // Minimise dropping real-time transactions, set the number of frontiers added to a factor of the total number of active elections + auto max_active = node.config.active_elections_size / 5; if (roots_size <= max_active && (check_time_exceeded || wallets_check_required || (!is_test_network && low_active_elections && agressive_mode))) { // When the number of active elections is low increase max number of elections for setting confirmation height. @@ -58,8 +59,8 @@ void nano::active_transactions::confirm_frontiers (nano::transaction const & tra } // Spend time prioritizing accounts to reduce voting traffic - auto time_spent_prioritizing_ledger_accounts = (std::chrono::seconds (2)); - auto time_spent_prioritizing_wallet_accounts = std::chrono::milliseconds (50); + auto time_spent_prioritizing_ledger_accounts = request_interval / 10; + auto time_spent_prioritizing_wallet_accounts = request_interval / 25; prioritize_frontiers_for_confirmation (transaction_a, is_test_network ? std::chrono::milliseconds (50) : time_spent_prioritizing_ledger_accounts, time_spent_prioritizing_wallet_accounts); size_t elections_count (0); @@ -82,7 +83,7 @@ void nano::active_transactions::confirm_frontiers (nano::transaction const & tra if (info.block_count > confirmation_height && !this->node.pending_confirmation_height.is_processing_block (info.head)) { auto block (this->node.store.block_get (transaction_a, info.head)); - if (!this->start (block)) + if (!this->start (block, true)) { ++elections_count; // Calculate votes for local representatives @@ -148,189 +149,258 @@ void nano::active_transactions::post_confirmation_height_set (nano::transaction } } -void nano::active_transactions::request_confirm (nano::unique_lock & lock_a) +void nano::active_transactions::election_escalate (std::shared_ptr & election_l, nano::transaction const & transaction_l, size_t const & roots_size_l) { - std::unordered_set inactive_l; - auto transaction_l (node.store.tx_begin_read ()); - unsigned unconfirmed_count_l (0); - unsigned unconfirmed_request_count_l (0); - unsigned could_fit_delay_l = node.network_params.network.is_test_network () ? high_confirmation_request_count - 1 : 1; - std::deque> blocks_bundle_l; - std::unordered_map, std::deque>> batched_confirm_req_bundle_l; - std::deque, std::shared_ptr>>>> single_confirm_req_bundle_l; - - /* Confirm frontiers when there aren't many confirmations already pending and node finished initial bootstrap - In auto mode start confirm only if node contains almost principal representative (half of required for principal weight) - The confirmation height processor works asynchronously, compressing several roots into one frontier, so probably_unconfirmed_frontiers is not always correct*/ - lock_a.unlock (); - auto pending_confirmation_height_size (node.pending_confirmation_height.size ()); - bool probably_unconfirmed_frontiers (node.ledger.block_count_cache > node.ledger.cemented_count + roots.size () + pending_confirmation_height_size); - bool bootstrap_weight_reached (node.ledger.block_count_cache >= node.ledger.bootstrap_weight_max_blocks); - if (node.config.frontiers_confirmation != nano::frontiers_confirmation_mode::disabled && bootstrap_weight_reached && probably_unconfirmed_frontiers && pending_confirmation_height_size < confirmed_frontiers_max_pending_cut_off) + static unsigned constexpr high_confirmation_request_count{ 16 }; + // Log votes for very long unconfirmed elections + if (election_l->confirmation_request_count % (4 * high_confirmation_request_count) == 1) { - confirm_frontiers (transaction_l); + auto tally_l (election_l->tally ()); + election_l->log_votes (tally_l); } - lock_a.lock (); - auto representatives_l (node.rep_crawler.representatives (std::numeric_limits::max ())); - auto roots_size_l (roots.size ()); - for (auto i (roots.get<1> ().begin ()), n (roots.get<1> ().end ()); i != n; ++i) + /* + * Escalation for long unconfirmed elections + * Start new elections for previous block & source if there are less than 100 active elections + */ + if (election_l->confirmation_request_count % high_confirmation_request_count == 1 && roots_size_l < 100 && !node.network_params.network.is_test_network ()) { - auto root_l (i->root); - auto election_l (i->election); - if ((election_l->confirmed || election_l->stopped) && election_l->confirmation_request_count >= minimum_confirmation_request_count - 1) + bool escalated_l (false); + std::shared_ptr previous_l; + auto previous_hash_l (election_l->status.winner->previous ()); + if (!previous_hash_l.is_zero ()) { - if (election_l->stopped) + previous_l = node.store.block_get (transaction_l, previous_hash_l); + if (previous_l != nullptr && blocks.find (previous_hash_l) == blocks.end () && !node.block_confirmed_or_being_confirmed (transaction_l, previous_hash_l)) { - inactive_l.insert (root_l); + add (std::move (previous_l), true); + escalated_l = true; } } - else - { - if (election_l->confirmation_request_count > high_confirmation_request_count) - { - ++unconfirmed_count_l; - unconfirmed_request_count_l += election_l->confirmation_request_count; - // Log votes for very long unconfirmed elections - if (election_l->confirmation_request_count % 50 == 1) - { - auto tally_l (election_l->tally ()); - election_l->log_votes (tally_l); - } - /* Escalation for long unconfirmed elections - Start new elections for previous block & source - if there are less than 100 active elections */ - if (election_l->confirmation_request_count % high_confirmation_request_count == 1 && roots_size_l < 100 && !node.network_params.network.is_test_network ()) - { - bool escalated_l (false); - std::shared_ptr previous_l; - auto previous_hash_l (election_l->status.winner->previous ()); - if (!previous_hash_l.is_zero ()) - { - previous_l = node.store.block_get (transaction_l, previous_hash_l); - if (previous_l != nullptr && blocks.find (previous_hash_l) == blocks.end () && !node.block_confirmed_or_being_confirmed (transaction_l, previous_hash_l)) - { - add (std::move (previous_l)); - escalated_l = true; - } - } - /* If previous block not existing/not commited yet, block_source can cause segfault for state blocks + /* If previous block not existing/not commited yet, block_source can cause segfault for state blocks So source check can be done only if previous != nullptr or previous is 0 (open account) */ - if (previous_hash_l.is_zero () || previous_l != nullptr) - { - auto source_hash (node.ledger.block_source (transaction_l, *election_l->status.winner)); - if (!source_hash.is_zero () && source_hash != previous_hash_l && blocks.find (source_hash) == blocks.end ()) - { - auto source (node.store.block_get (transaction_l, source_hash)); - if (source != nullptr && !node.block_confirmed_or_being_confirmed (transaction_l, source_hash)) - { - add (std::move (source)); - escalated_l = true; - } - } - } - if (escalated_l) - { - election_l->update_dependent (); - } + if (previous_hash_l.is_zero () || previous_l != nullptr) + { + auto source_hash_l (node.ledger.block_source (transaction_l, *election_l->status.winner)); + if (!source_hash_l.is_zero () && source_hash_l != previous_hash_l && blocks.find (source_hash_l) == blocks.end ()) + { + auto source_l (node.store.block_get (transaction_l, source_hash_l)); + if (source_l != nullptr && !node.block_confirmed_or_being_confirmed (transaction_l, source_hash_l)) + { + add (std::move (source_l), true); + escalated_l = true; } } - if (election_l->confirmation_request_count < high_confirmation_request_count || election_l->confirmation_request_count % high_confirmation_request_count == could_fit_delay_l) - { - if (node.ledger.could_fit (transaction_l, *election_l->status.winner)) - { - // Broadcast winner - if (blocks_bundle_l.size () < max_broadcast_queue) - { - blocks_bundle_l.push_back (election_l->status.winner); - } - } - else - { - if (election_l->confirmation_request_count != 0) - { - election_l->stop (); - inactive_l.insert (root_l); - } - } - } - std::unordered_set> rep_channels_missing_vote_l; - // Add all rep endpoints that haven't already voted - for (auto & rep : representatives_l) - { - if (election_l->last_votes.find (rep.account) == election_l->last_votes.end ()) - { - rep_channels_missing_vote_l.insert (rep.channel); + } + if (escalated_l) + { + election_l->update_dependent (); + } + } +} - if (node.config.logging.vote_logging ()) +void nano::active_transactions::election_broadcast (std::shared_ptr & election_l, nano::transaction const & transaction_l, std::deque> & blocks_bundle_l, std::unordered_set & inactive_l, nano::qualified_root & root_l) +{ + if (node.ledger.could_fit (transaction_l, *election_l->status.winner)) + { + // Broadcast current winner + if (blocks_bundle_l.size () < max_block_broadcasts) + { + blocks_bundle_l.push_back (election_l->status.winner); + } + } + else if (election_l->confirmation_request_count != 0) + { + election_l->stop (); + inactive_l.insert (root_l); + } +} + +bool nano::active_transactions::election_request_confirm (std::shared_ptr & election_l, std::vector const & representatives_l, size_t const & roots_size_l, +std::deque, std::shared_ptr>>>> & single_confirm_req_bundle_l, +std::unordered_map, std::deque>> & batched_confirm_req_bundle_l) +{ + bool inserted_into_any_bundle{ false }; + std::vector> rep_channels_missing_vote_l; + // Add all rep endpoints that haven't already voted + for (const auto & rep : representatives_l) + { + if (election_l->last_votes.find (rep.account) == election_l->last_votes.end ()) + { + rep_channels_missing_vote_l.push_back (rep.channel); + + if (node.config.logging.vote_logging () && election_l->confirmation_request_count > 0) + { + node.logger.try_log ("Representative did not respond to confirm_req, retrying: ", rep.account.to_account ()); + } + } + } + // Unique channels as there can be multiple reps per channel + rep_channels_missing_vote_l.erase (std::unique (rep_channels_missing_vote_l.begin (), rep_channels_missing_vote_l.end ()), rep_channels_missing_vote_l.end ()); + bool low_reps_weight (rep_channels_missing_vote_l.empty () || node.rep_crawler.total_weight () < node.config.online_weight_minimum.number ()); + if (low_reps_weight && roots_size_l <= 5 && !node.network_params.network.is_test_network ()) + { + // Spam mode + auto deque_l (node.network.udp_channels.random_set (100)); + auto vec (std::make_shared>> ()); + for (auto i : deque_l) + { + vec->push_back (i); + } + single_confirm_req_bundle_l.push_back (std::make_pair (election_l->status.winner, vec)); + inserted_into_any_bundle = true; + } + else + { + auto single_confirm_req_channels_l (std::make_shared>> ()); + for (auto & rep : rep_channels_missing_vote_l) + { + if (rep->get_network_version () >= node.network_params.protocol.tcp_realtime_protocol_version_min) + { + // Send batch request to peers supporting confirm_req by hash + root + auto rep_request_l (batched_confirm_req_bundle_l.find (rep)); + auto block_l (election_l->status.winner); + auto root_hash_l (std::make_pair (block_l->hash (), block_l->root ())); + if (rep_request_l == batched_confirm_req_bundle_l.end ()) + { + // Maximum number of representatives + if (batched_confirm_req_bundle_l.size () < max_confirm_representatives) { - node.logger.try_log ("Representative did not respond to confirm_req, retrying: ", rep.account.to_account ()); + std::deque> insert_root_hash = { root_hash_l }; + batched_confirm_req_bundle_l.insert (std::make_pair (rep, insert_root_hash)); + inserted_into_any_bundle = true; } } - } - bool low_reps_weight (rep_channels_missing_vote_l.empty () || node.rep_crawler.total_weight () < node.config.online_weight_minimum.number ()); - if (low_reps_weight && roots_size_l <= 5 && !node.network_params.network.is_test_network ()) - { - // Spam mode - auto deque_l (node.network.udp_channels.random_set (100)); - auto vec (std::make_shared>> ()); - for (auto i : deque_l) + // Maximum number of hashes + else if (rep_request_l->second.size () < max_confirm_req_batches * nano::network::confirm_req_hashes_max) { - vec->push_back (i); + rep_request_l->second.push_back (root_hash_l); + inserted_into_any_bundle = true; } - single_confirm_req_bundle_l.push_back (std::make_pair (election_l->status.winner, vec)); } else { - auto single_confirm_req_channels (std::make_shared>> ()); - for (auto & rep : rep_channels_missing_vote_l) - { - if (rep->get_network_version () >= node.network_params.protocol.tcp_realtime_protocol_version_min) - { - // Send batch request to peers supporting confirm_req by hash + root - auto rep_request (batched_confirm_req_bundle_l.find (rep)); - auto block (election_l->status.winner); - auto root_hash (std::make_pair (block->hash (), block->root ())); - if (rep_request == batched_confirm_req_bundle_l.end ()) - { - if (batched_confirm_req_bundle_l.size () < max_broadcast_queue) - { - std::deque> insert_root_hash = { root_hash }; - batched_confirm_req_bundle_l.insert (std::make_pair (rep, insert_root_hash)); - } - } - else if (rep_request->second.size () < max_broadcast_queue * nano::network::confirm_req_hashes_max) - { - rep_request->second.push_back (root_hash); - } - } - else - { - single_confirm_req_channels->push_back (rep); - } - } - // broadcast_confirm_req_base modifies reps, so we clone it once to avoid aliasing - if (single_confirm_req_bundle_l.size () < max_broadcast_queue && !single_confirm_req_channels->empty ()) - { - single_confirm_req_bundle_l.push_back (std::make_pair (election_l->status.winner, single_confirm_req_channels)); - } + single_confirm_req_channels_l->push_back (rep); + } + } + // broadcast_confirm_req_base modifies reps, so we clone it once to avoid aliasing + if (single_confirm_req_bundle_l.size () < max_confirm_req && !single_confirm_req_channels_l->empty ()) + { + single_confirm_req_bundle_l.push_back (std::make_pair (election_l->status.winner, single_confirm_req_channels_l)); + inserted_into_any_bundle = true; + } + } + return inserted_into_any_bundle; +} + +void nano::active_transactions::request_confirm (nano::unique_lock & lock_a) +{ + assert (!mutex.try_lock ()); + auto transaction_l (node.store.tx_begin_read ()); + std::unordered_set inactive_l; + std::deque> blocks_bundle_l; + std::unordered_map, std::deque>> batched_confirm_req_bundle_l; + std::deque, std::shared_ptr>>>> single_confirm_req_bundle_l; + lock_a.unlock (); + + /* + * Confirm frontiers when there aren't many confirmations already pending and node finished initial bootstrap + * In auto mode start confirm only if node contains almost principal representative (half of required for principal weight) + */ + + // Due to the confirmation height processor working asynchronously and compressing several roots into one frontier, probably_unconfirmed_frontiers can be wrong + { + auto pending_confirmation_height_size (node.pending_confirmation_height.size ()); + bool probably_unconfirmed_frontiers (node.ledger.block_count_cache > node.ledger.cemented_count + roots.size () + pending_confirmation_height_size); + bool bootstrap_weight_reached (node.ledger.block_count_cache >= node.ledger.bootstrap_weight_max_blocks); + if (node.config.frontiers_confirmation != nano::frontiers_confirmation_mode::disabled && bootstrap_weight_reached && probably_unconfirmed_frontiers && pending_confirmation_height_size < confirmed_frontiers_max_pending_cut_off) + { + search_frontiers (transaction_l); + } + } + lock_a.lock (); + + // Any new election started from process_live only gets requests after at least 1 second + auto cutoff_l (std::chrono::steady_clock::now () - election_request_delay); + // Elections taking too long get escalated + auto long_election_cutoff_l (std::chrono::steady_clock::now () - long_election_threshold); + // The lowest PoW difficulty elections have a maximum time to live if they are beyond the soft threshold size for the container + auto election_ttl_cutoff_l (std::chrono::steady_clock::now () - election_time_to_live); + + auto const representatives_l (node.rep_crawler.representatives (std::numeric_limits::max ())); + auto roots_size_l (roots.size ()); + auto & sorted_roots_l = roots.get<1> (); + size_t count_l{ 0 }; + + /* + * Loop through active elections in descending order of proof-of-work difficulty, requesting confirmation + * + * Only up to a certain amount of elections are queued for confirmation request and block rebroadcasting. The remaining elections can still be confirmed if votes arrive + * We avoid selecting the same elections repeatedly in the next loops, through a modulo on confirmation_request_count + * An election only gets confirmation_request_count increased after the first confirm_req; after that it is increased every loop unless they don't fit in the queues + * Elections extending the soft config.active_elections_size limit are flushed after a certain time-to-live cutoff + * Flushed elections are later re-activated via frontier confirmation + */ + for (auto i = sorted_roots_l.begin (), n = sorted_roots_l.end (); i != n; ++i, ++count_l) + { + auto election_l (i->election); + auto root_l (i->root); + // Erase finished elections + if ((election_l->confirmed || election_l->stopped)) + { + inactive_l.insert (root_l); + } + // Drop elections + else if (count_l >= node.config.active_elections_size && election_l->election_start < election_ttl_cutoff_l && !node.wallets.watcher->is_watched (root_l)) + { + election_l->stop (); + inactive_l.insert (root_l); + } + // Broadcast and request confirmation + else if (election_l->skip_delay || election_l->election_start < cutoff_l) + { + bool increment_counter_l{ true }; + // Escalate long election after a certain time and number of requests performed + if (election_l->confirmation_request_count > 4 && election_l->election_start < long_election_cutoff_l) + { + election_escalate (election_l, transaction_l, roots_size_l); + } + // Block broadcasting + if (election_l->confirmation_request_count % 8 == 1 || node.network_params.network.is_test_network ()) + { + election_broadcast (election_l, transaction_l, blocks_bundle_l, inactive_l, root_l); + } + // Confirmation requesting + else if (election_l->confirmation_request_count % 4 == 0) + { + // If failed to insert into any of the bundles (capped), don't increment the counter so that the same root is sent for confirmation in the next loop + if (!election_request_confirm (election_l, representatives_l, roots_size_l, single_confirm_req_bundle_l, batched_confirm_req_bundle_l)) + { + increment_counter_l = false; + } + } + if (increment_counter_l) + { + ++election_l->confirmation_request_count; } } - ++election_l->confirmation_request_count; } ongoing_broadcasts = !blocks_bundle_l.empty () + !batched_confirm_req_bundle_l.empty () + !single_confirm_req_bundle_l.empty (); lock_a.unlock (); + // Rebroadcast unconfirmed blocks if (!blocks_bundle_l.empty ()) { - node.network.flood_block_many (std::move (blocks_bundle_l), [this]() { + node.network.flood_block_many ( + std::move (blocks_bundle_l), [this]() { { nano::lock_guard guard_l (this->mutex); --this->ongoing_broadcasts; } this->condition.notify_all (); - }); + }, + 10); // 500ms / (10ms / 1 block) > 30 blocks } - // Batched confirmation requests + // Batch confirmation request if (!batched_confirm_req_bundle_l.empty ()) { node.network.broadcast_confirm_req_batched_many ( @@ -341,18 +411,20 @@ void nano::active_transactions::request_confirm (nano::unique_lock & } this->condition.notify_all (); }, - 50); + 20); // 500ms / (20ms / 5 batch size) > (20*7 = 140) batches } // Single confirmation requests if (!single_confirm_req_bundle_l.empty ()) { - node.network.broadcast_confirm_req_many (single_confirm_req_bundle_l, [this]() { + node.network.broadcast_confirm_req_many ( + single_confirm_req_bundle_l, [this]() { { nano::lock_guard guard_l (this->mutex); --this->ongoing_broadcasts; } this->condition.notify_all (); - }); + }, + 10); // 500ms / (10-20ms / 1 req) > 15 reqs } lock_a.lock (); // Erase inactive elections @@ -366,11 +438,6 @@ void nano::active_transactions::request_confirm (nano::unique_lock & roots.erase (root_it); } } - long_unconfirmed_size = unconfirmed_count_l; - if (unconfirmed_count_l > 0) - { - node.logger.try_log (boost::str (boost::format ("%1% blocks have been unconfirmed averaging %2% confirmation requests") % unconfirmed_count_l % (unconfirmed_request_count_l / unconfirmed_count_l))); - } } void nano::active_transactions::request_loop () @@ -388,12 +455,12 @@ void nano::active_transactions::request_loop () while (!stopped) { + // Account for the time spent in request_confirm by defining the wakeup point beforehand + const auto wakeup_l (std::chrono::steady_clock::now () + std::chrono::milliseconds (node.network_params.network.request_interval_ms)); + request_confirm (lock); update_active_difficulty (lock); - const auto extra_delay_l (std::min (roots.size (), max_broadcast_queue) * node.network.broadcast_interval_ms * 2); - const auto wakeup_l (std::chrono::steady_clock::now () + std::chrono::milliseconds (node.network_params.network.request_interval_ms + extra_delay_l)); - // Sleep until all broadcasts are done, plus the remaining loop time while (!stopped && ongoing_broadcasts) { @@ -583,13 +650,13 @@ void nano::active_transactions::stop () roots.clear (); } -bool nano::active_transactions::start (std::shared_ptr block_a, std::function)> const & confirmation_action_a) +bool nano::active_transactions::start (std::shared_ptr block_a, bool const skip_delay_a, std::function)> const & confirmation_action_a) { nano::lock_guard lock (mutex); - return add (block_a, confirmation_action_a); + return add (block_a, skip_delay_a, confirmation_action_a); } -bool nano::active_transactions::add (std::shared_ptr block_a, std::function)> const & confirmation_action_a) +bool nano::active_transactions::add (std::shared_ptr block_a, bool const skip_delay_a, std::function)> const & confirmation_action_a) { auto error (true); if (!stopped) @@ -599,7 +666,7 @@ bool nano::active_transactions::add (std::shared_ptr block_a, std:: if (existing == roots.end () && confirmed_set.get<1> ().find (root) == confirmed_set.get<1> ().end ()) { auto hash (block_a->hash ()); - auto election (nano::make_shared (node, block_a, confirmation_action_a)); + auto election (nano::make_shared (node, block_a, skip_delay_a, confirmation_action_a)); uint64_t difficulty (0); error = nano::work_validate (*block_a, &difficulty); release_assert (!error); @@ -608,10 +675,6 @@ bool nano::active_transactions::add (std::shared_ptr block_a, std:: adjust_difficulty (hash); election->insert_inactive_votes_cache (); } - if (roots.size () >= node.config.active_elections_size) - { - flush_lowest (); - } } return error; } @@ -801,8 +864,7 @@ void nano::active_transactions::update_active_difficulty (nano::unique_lock active_root_difficulties; active_root_difficulties.reserve (roots.size ()); - auto min_election_time (std::chrono::milliseconds (node.network_params.network.request_interval_ms)); - auto cutoff (std::chrono::steady_clock::now () - min_election_time); + auto cutoff (std::chrono::steady_clock::now () - election_request_delay - 1s); for (auto & root : roots) { if (!root.election->confirmed && !root.election->stopped && root.election->election_start < cutoff) @@ -886,36 +948,6 @@ void nano::active_transactions::erase (nano::block const & block_a) } } -void nano::active_transactions::flush_lowest () -{ - size_t count (0); - assert (!roots.empty ()); - auto & sorted_roots = roots.get<1> (); - for (auto it = sorted_roots.rbegin (); it != sorted_roots.rend ();) - { - if (count != 2) - { - auto election = it->election; - if (election->confirmation_request_count > high_confirmation_request_count && !election->confirmed && !election->stopped && !node.wallets.watcher->is_watched (it->root)) - { - it = decltype (it){ sorted_roots.erase (std::next (it).base ()) }; - election->stop (); - election->clear_blocks (); - election->clear_dependent (); - count++; - } - else - { - ++it; - } - } - else - { - break; - } - } -} - bool nano::active_transactions::empty () { nano::lock_guard lock (mutex); diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index 826db7a7..0ab34cc3 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include @@ -86,7 +88,7 @@ public: // Start an election for a block // Call action with confirmed block, may be different than what we started with // clang-format off - bool start (std::shared_ptr, std::function)> const & = [](std::shared_ptr) {}); + bool start (std::shared_ptr, bool const = false, std::function)> const & = [](std::shared_ptr) {}); // clang-format on // If this returns true, the vote is a replay // If this returns false, the vote may or may not be a replay @@ -101,8 +103,6 @@ public: uint64_t limited_active_difficulty (); std::deque> list_blocks (bool = false); void erase (nano::block const &); - //drop 2 from roots based on adjusted_difficulty - void flush_lowest (); bool empty (); size_t size (); void stop (); @@ -126,12 +126,15 @@ public: nano::gap_information find_inactive_votes_cache (nano::block_hash const &); nano::node & node; std::mutex mutex; - // Minimum number of confirmation requests - static unsigned constexpr minimum_confirmation_request_count = 2; - // Threshold for considering confirmation request count high - static unsigned constexpr high_confirmation_request_count = 2; - size_t long_unconfirmed_size = 0; - static size_t constexpr max_broadcast_queue = 1000; + std::chrono::seconds const long_election_threshold; + // Delay until requesting confirmation for an election + std::chrono::milliseconds const election_request_delay; + // Maximum time an election can be kept active if it is extending the container + std::chrono::seconds const election_time_to_live; + static size_t constexpr max_block_broadcasts = 30; + static size_t constexpr max_confirm_representatives = 30; + static size_t constexpr max_confirm_req_batches = 20; + static size_t constexpr max_confirm_req = 15; boost::circular_buffer multipliers_cb; uint64_t trended_active_difficulty; size_t priority_cementable_frontiers_size (); @@ -144,11 +147,16 @@ public: private: // Call action with confirmed block, may be different than what we started with // clang-format off - bool add (std::shared_ptr, std::function)> const & = [](std::shared_ptr) {}); + bool add (std::shared_ptr, bool const = false, std::function)> const & = [](std::shared_ptr) {}); // clang-format on void request_loop (); + void search_frontiers (nano::transaction const &); + void election_escalate (std::shared_ptr &, nano::transaction const &, size_t const &); + void election_broadcast (std::shared_ptr &, nano::transaction const &, std::deque> &, std::unordered_set &, nano::qualified_root &); + bool election_request_confirm (std::shared_ptr &, std::vector const &, size_t const &, + std::deque, std::shared_ptr>>>> & single_confirm_req_bundle_l, + std::unordered_map, std::deque>> & batched_confirm_req_bundle_l); void request_confirm (nano::unique_lock &); - void confirm_frontiers (nano::transaction const &); nano::account next_frontier_account{ 0 }; std::chrono::steady_clock::time_point next_frontier_check{ std::chrono::steady_clock::now () }; nano::condition_variable condition; diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index 98a2f5c7..4fe69255 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -369,7 +369,7 @@ void nano::block_processor::process_batch (nano::unique_lock & lock_ void nano::block_processor::process_live (nano::block_hash const & hash_a, std::shared_ptr block_a, const bool watch_work_a) { // Start collecting quorum on block - node.active.start (block_a); + node.active.start (block_a, false); //add block to watcher if desired after block has been added to active if (watch_work_a) { @@ -382,28 +382,6 @@ void nano::block_processor::process_live (nano::block_hash const & hash_a, std:: // Announce our weighted vote to the network generator.add (hash_a); } - // Request confirmation for new block with delay - std::weak_ptr node_w (node.shared ()); - node.alarm.add (std::chrono::steady_clock::now () + confirmation_request_delay, [node_w, block_a]() { - if (auto node_l = node_w.lock ()) - { - // Check if votes were already requested - bool send_request (false); - { - nano::lock_guard lock (node_l->active.mutex); - auto existing (node_l->active.blocks.find (block_a->hash ())); - if (existing != node_l->active.blocks.end () && !existing->second->confirmed && !existing->second->stopped && existing->second->confirmation_request_count == 0) - { - send_request = true; - } - } - // Request votes - if (send_request) - { - node_l->network.broadcast_confirm_req (block_a); - } - } - }); } nano::process_return nano::block_processor::process_one (nano::write_transaction const & transaction_a, nano::unchecked_info info_a, const bool watch_work_a) diff --git a/nano/node/election.cpp b/nano/node/election.cpp index 3a72c8bb..c4c519b9 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -7,14 +7,14 @@ nano::election_vote_result::election_vote_result (bool replay_a, bool processed_ processed = processed_a; } -nano::election::election (nano::node & node_a, std::shared_ptr block_a, std::function)> const & confirmation_action_a) : +nano::election::election (nano::node & node_a, std::shared_ptr block_a, bool const skip_delay_a, std::function)> const & confirmation_action_a) : confirmation_action (confirmation_action_a), node (node_a), election_start (std::chrono::steady_clock::now ()), status ({ block_a, 0, std::chrono::duration_cast (std::chrono::system_clock::now ().time_since_epoch ()), std::chrono::duration_values::zero (), 0, nano::election_status_type::ongoing }), +skip_delay (skip_delay_a), confirmed (false), -stopped (false), -confirmation_request_count (0) +stopped (false) { last_votes.insert (std::make_pair (node.network_params.random.not_an_account, nano::vote_info{ std::chrono::steady_clock::now (), 0, block_a->hash () })); blocks.insert (std::make_pair (block_a->hash (), block_a)); @@ -47,10 +47,6 @@ void nano::election::confirm_once (nano::election_status_type type_a) node_l->process_confirmed (status_l); confirmation_action_l (status_l.winner); }); - if (confirmation_request_count > node.active.high_confirmation_request_count) - { - --node.active.long_unconfirmed_size; - } auto root (status.winner->qualified_root ()); node.active.pending_conf_height.emplace (status.winner->hash (), shared_from_this ()); clear_blocks (); diff --git a/nano/node/election.hpp b/nano/node/election.hpp index 5a5f37f1..85581c01 100644 --- a/nano/node/election.hpp +++ b/nano/node/election.hpp @@ -34,7 +34,7 @@ class election final : public std::enable_shared_from_this std::function)> confirmation_action; public: - election (nano::node &, std::shared_ptr, std::function)> const &); + election (nano::node &, std::shared_ptr, bool const, std::function)> const &); nano::election_vote_result vote (nano::account, uint64_t, nano::block_hash); nano::tally_t tally (); // Check if we have vote quorum @@ -57,10 +57,11 @@ public: std::unordered_map> blocks; std::chrono::steady_clock::time_point election_start; nano::election_status status; + bool skip_delay; std::atomic confirmed; bool stopped; std::unordered_map last_tally; - unsigned confirmation_request_count; + unsigned confirmation_request_count{ 0 }; std::unordered_set dependent_blocks; std::chrono::seconds late_blocks_delay{ 5 }; }; diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 99c954a5..4adf25e5 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -1911,7 +1911,7 @@ void nano::json_handler::confirmation_quorum () if (request.get ("peer_details", false)) { boost::property_tree::ptree peers; - for (auto & peer : node.rep_crawler.representatives_by_weight ()) + for (auto & peer : node.rep_crawler.representatives ()) { boost::property_tree::ptree peer_node; peer_node.put ("account", peer.account.to_account ()); diff --git a/nano/node/network.cpp b/nano/node/network.cpp index b58a1039..81a0523e 100644 --- a/nano/node/network.cpp +++ b/nano/node/network.cpp @@ -325,20 +325,19 @@ void nano::network::broadcast_confirm_req_base (std::shared_ptr blo } } -void nano::network::broadcast_confirm_req_batched_many (std::unordered_map, std::deque>> request_bundle_a, std::function callback_a, unsigned delay_a, bool resumption) +void nano::network::broadcast_confirm_req_batched_many (std::unordered_map, std::deque>> request_bundle_a, std::function callback_a, unsigned delay_a, bool resumption_a) { - const size_t max_reps = 50; - if (!resumption && node.config.logging.network_logging ()) + const size_t burst_size_l{ 5 }; + if (!resumption_a && node.config.logging.network_logging ()) { node.logger.try_log (boost::str (boost::format ("Broadcasting batch confirm req to %1% representatives") % request_bundle_a.size ())); } - auto count_l (0); - while (!request_bundle_a.empty () && count_l < max_reps) + + for (size_t count_l (0); !request_bundle_a.empty () && count_l < burst_size_l; ++count_l) { auto j (request_bundle_a.begin ()); while (j != request_bundle_a.end ()) { - ++count_l; std::vector> roots_hashes_l; // Limit max request size hash + root to 7 pairs while (roots_hashes_l.size () < confirm_req_hashes_max && !j->second.empty ()) diff --git a/nano/node/node.cpp b/nano/node/node.cpp index e2d09b48..6f9d457a 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -557,7 +557,7 @@ void nano::node::process_fork (nano::transaction const & transaction_a, std::sha if (ledger_block && !block_confirmed_or_being_confirmed (transaction_a, ledger_block->hash ())) { std::weak_ptr this_w (shared_from_this ()); - if (!active.start (ledger_block, [this_w, root](std::shared_ptr) { + if (!active.start (ledger_block, false, [this_w, root](std::shared_ptr) { if (auto this_l = this_w.lock ()) { auto attempt (this_l->bootstrap_initiator.current_attempt ()); @@ -1042,7 +1042,7 @@ void nano::node::add_initial_peers () void nano::node::block_confirm (std::shared_ptr block_a) { - active.start (block_a); + active.start (block_a, false); network.broadcast_confirm_req (block_a); // Calculate votes for local representatives if (config.enable_voting && active.active (*block_a)) diff --git a/nano/node/nodeconfig.cpp b/nano/node/nodeconfig.cpp index 6fec0a23..dbbb5e5b 100644 --- a/nano/node/nodeconfig.cpp +++ b/nano/node/nodeconfig.cpp @@ -587,7 +587,8 @@ bool nano::node_config::upgrade_json (unsigned version_a, nano::jsonconfig & jso } case 17: { - json.put ("vote_generator_delay", vote_generator_delay.count ()); // Update value + json.put ("active_elections_size", 10000); // Update value + json.put ("vote_generator_delay", 100); // Update value json.put ("backup_before_upgrade", backup_before_upgrade); json.put ("work_watcher_period", work_watcher_period.count ()); } diff --git a/nano/node/nodeconfig.hpp b/nano/node/nodeconfig.hpp index f44c166a..8316a25c 100644 --- a/nano/node/nodeconfig.hpp +++ b/nano/node/nodeconfig.hpp @@ -80,7 +80,7 @@ public: /** Timeout for initiated async operations */ std::chrono::seconds tcp_io_timeout{ (network_params.network.is_test_network () && !is_sanitizer_build) ? std::chrono::seconds (5) : std::chrono::seconds (15) }; std::chrono::nanoseconds pow_sleep_interval{ 0 }; - size_t active_elections_size{ 50000 }; + size_t active_elections_size{ 10000 }; /** Default maximum incoming TCP connections, including realtime network & bootstrap */ unsigned tcp_incoming_connections_max{ 1024 }; bool use_memory_pools{ true }; diff --git a/nano/node/repcrawler.cpp b/nano/node/repcrawler.cpp index 4b90d32e..650b7877 100644 --- a/nano/node/repcrawler.cpp +++ b/nano/node/repcrawler.cpp @@ -163,18 +163,6 @@ nano::uint128_t nano::rep_crawler::total_weight () const return result; } -std::vector nano::rep_crawler::representatives_by_weight () -{ - std::vector result; - nano::lock_guard lock (probable_reps_mutex); - for (auto i (probable_reps.get ().begin ()), n (probable_reps.get ().end ()); i != n; ++i) - { - assert (i->weight.number () > 0); - result.push_back (*i); - } - return result; -} - void nano::rep_crawler::on_rep_request (std::shared_ptr channel_a) { nano::lock_guard lock (probable_reps_mutex); @@ -257,7 +245,6 @@ void nano::rep_crawler::update_weights () std::vector nano::rep_crawler::representatives (size_t count_a) { std::vector result; - result.reserve (std::min (count_a, size_t (16))); nano::lock_guard lock (probable_reps_mutex); for (auto i (probable_reps.get ().begin ()), n (probable_reps.get ().end ()); i != n && result.size () < count_a; ++i) { diff --git a/nano/node/repcrawler.hpp b/nano/node/repcrawler.hpp index 2cbfa004..34c218cb 100644 --- a/nano/node/repcrawler.hpp +++ b/nano/node/repcrawler.hpp @@ -100,15 +100,12 @@ public: /** Get total available weight from representatives */ nano::uint128_t total_weight () const; - /** Request a list of the top \p count_a known representatives. The maximum number of reps returned is 16. */ - std::vector representatives (size_t count_a); + /** Request a list of the top \p count_a known representatives in descending order of weight. */ + std::vector representatives (size_t count_a = std::numeric_limits::max ()); - /** Request a list of the top \p count_a known representative endpoints. The maximum number of reps returned is 16. */ + /** Request a list of the top \p count_a known representative endpoints. */ std::vector> representative_endpoints (size_t count_a); - /** Returns all representatives registered with weight in descending order */ - std::vector representatives_by_weight (); - /** Total number of representatives */ size_t representative_count ();