diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index 4daf82e091..5be7c22de3 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -122,6 +122,7 @@ TEST (active_transactions, adjusted_difficulty_priority) // Check adjusted difficulty { nano::lock_guard active_guard (node1.active.mutex); + node1.active.update_adjusted_difficulty (); ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), send1->hash ()); ASSERT_LT (node1.active.roots.find (send2->qualified_root ())->adjusted_difficulty, node1.active.roots.find (send1->qualified_root ())->adjusted_difficulty); ASSERT_LT (node1.active.roots.find (open1->qualified_root ())->adjusted_difficulty, node1.active.roots.find (send1->qualified_root ())->adjusted_difficulty); @@ -168,6 +169,7 @@ TEST (active_transactions, adjusted_difficulty_priority) // Check adjusted difficulty nano::lock_guard lock (node1.active.mutex); + node1.active.update_adjusted_difficulty (); uint64_t last_adjusted (0); for (auto i (node1.active.roots.get<1> ().begin ()), n (node1.active.roots.get<1> ().end ()); i != n; ++i) { @@ -223,7 +225,8 @@ TEST (active_transactions, adjusted_difficulty_overflow_max) modify_difficulty (send2_root); modify_difficulty (open1_root); modify_difficulty (open2_root); - node1.active.adjust_difficulty (send2->hash ()); + node1.active.add_adjust_difficulty (send2->hash ()); + node1.active.update_adjusted_difficulty (); // Test overflow ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), send1->hash ()); ASSERT_EQ (send1_root->adjusted_difficulty, std::numeric_limits::max ()); @@ -277,7 +280,8 @@ TEST (active_transactions, adjusted_difficulty_overflow_min) modify_difficulty (open1_root); modify_difficulty (open2_root); modify_difficulty (send3_root); - node1.active.adjust_difficulty (send1->hash ()); + node1.active.add_adjust_difficulty (send1->hash ()); + node1.active.update_adjusted_difficulty (); // Test overflow ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), send1->hash ()); ASSERT_EQ (send1_root->adjusted_difficulty, std::numeric_limits::min () + 3); @@ -399,6 +403,8 @@ TEST (active_transactions, prioritize_chains) } size_t seen (0); { + nano::lock_guard active_guard (node1.active.mutex); + node1.active.update_adjusted_difficulty (); auto it (node1.active.roots.get<1> ().begin ()); while (!node1.active.roots.empty () && it != node1.active.roots.get<1> ().end ()) { diff --git a/nano/core_test/conflicts.cpp b/nano/core_test/conflicts.cpp index 3309435524..1cf633a39b 100644 --- a/nano/core_test/conflicts.cpp +++ b/nano/core_test/conflicts.cpp @@ -253,6 +253,7 @@ TEST (conflicts, adjusted_difficulty) std::unordered_map adjusted_difficulties; { nano::lock_guard guard (node1.active.mutex); + node1.active.update_adjusted_difficulty (); ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), send1->hash ()); for (auto i (node1.active.roots.get<1> ().begin ()), n (node1.active.roots.get<1> ().end ()); i != n; ++i) { @@ -285,6 +286,7 @@ TEST (conflicts, adjusted_difficulty) } { nano::lock_guard guard (node1.active.mutex); + node1.active.update_adjusted_difficulty (); ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), open_epoch2->hash ()); } } diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index a1fc3298e7..6161501d49 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -222,14 +222,13 @@ void nano::active_transactions::request_confirm (nano::unique_lock & lock_a.unlock (); search_frontiers (transaction_l); lock_a.lock (); + update_adjusted_difficulty (); // New roots sorting } } // Only representatives ready to receive batched confirm_req - lock_a.unlock (); nano::confirmation_solicitor solicitor (node.network, node.network_params.network); solicitor.prepare (node.rep_crawler.representatives (node.network_params.protocol.tcp_realtime_protocol_version_min)); - lock_a.lock (); auto election_ttl_cutoff_l (std::chrono::steady_clock::now () - election_time_to_live); auto roots_size_l (roots.size ()); @@ -281,6 +280,7 @@ void nano::active_transactions::request_loop () // 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)); + update_adjusted_difficulty (); update_active_difficulty (lock); request_confirm (lock); @@ -484,7 +484,7 @@ std::pair, bool> nano::active_transactions::inse auto difficulty (block_a->difficulty ()); roots.get ().emplace (nano::conflict_info{ root, difficulty, difficulty, result.first }); blocks.emplace (hash, result.first); - adjust_difficulty (hash); + add_adjust_difficulty (hash); result.first->insert_inactive_votes_cache (hash); } } @@ -601,97 +601,111 @@ void nano::active_transactions::update_difficulty (std::shared_ptr info_a.difficulty = difficulty; }); existing_election->election->publish (block_a); - adjust_difficulty (block_a->hash ()); + add_adjust_difficulty (block_a->hash ()); } } } -void nano::active_transactions::adjust_difficulty (nano::block_hash const & hash_a) +void nano::active_transactions::add_adjust_difficulty (nano::block_hash const & hash_a) +{ + debug_assert (!mutex.try_lock ()); + adjust_difficulty_list.push_back (hash_a); +} + +void nano::active_transactions::update_adjusted_difficulty () { debug_assert (!mutex.try_lock ()); - std::deque> remaining_blocks; - remaining_blocks.emplace_back (hash_a, 0); std::unordered_set processed_blocks; - std::vector> elections_list; - double sum (0.); - int64_t highest_level (0); - int64_t lowest_level (0); - while (!remaining_blocks.empty ()) - { - auto const & item (remaining_blocks.front ()); - auto hash (item.first); - auto level (item.second); - if (processed_blocks.find (hash) == processed_blocks.end ()) + while (!adjust_difficulty_list.empty ()) + { + auto const & adjust_difficulty_item (adjust_difficulty_list.front ()); + std::deque> remaining_blocks; + remaining_blocks.emplace_back (adjust_difficulty_item, 0); + adjust_difficulty_list.pop_front (); + std::vector> elections_list; + double sum (0.); + int64_t highest_level (0); + int64_t lowest_level (0); + while (!remaining_blocks.empty ()) { - auto existing (blocks.find (hash)); - if (existing != blocks.end () && !existing->second->confirmed () && existing->second->status.winner->hash () == hash) + auto const & item (remaining_blocks.front ()); + auto hash (item.first); + auto level (item.second); + if (processed_blocks.find (hash) == processed_blocks.end ()) { - auto previous (existing->second->status.winner->previous ()); - if (!previous.is_zero ()) - { - remaining_blocks.emplace_back (previous, level + 1); - } - auto source (existing->second->status.winner->source ()); - if (!source.is_zero () && source != previous) - { - remaining_blocks.emplace_back (source, level + 1); - } - auto link (existing->second->status.winner->link ()); - if (!link.is_zero () && !node.ledger.is_epoch_link (link) && link != previous) + auto existing (blocks.find (hash)); + if (existing != blocks.end () && !existing->second->confirmed () && existing->second->status.winner->hash () == hash) { - remaining_blocks.emplace_back (link, level + 1); - } - for (auto & dependent_block : existing->second->dependent_blocks) - { - remaining_blocks.emplace_back (dependent_block, level - 1); - } - processed_blocks.insert (hash); - nano::qualified_root root (previous, existing->second->status.winner->root ()); - auto existing_root (roots.get ().find (root)); - if (existing_root != roots.get ().end ()) - { - sum += nano::difficulty::to_multiplier (existing_root->difficulty, node.network_params.network.publish_threshold); - elections_list.emplace_back (root, level); - if (level > highest_level) + auto previous (existing->second->status.winner->previous ()); + if (!previous.is_zero ()) + { + remaining_blocks.emplace_back (previous, level + 1); + } + auto source (existing->second->status.winner->source ()); + if (!source.is_zero () && source != previous) + { + remaining_blocks.emplace_back (source, level + 1); + } + auto link (existing->second->status.winner->link ()); + if (!link.is_zero () && !node.ledger.is_epoch_link (link) && link != previous) { - highest_level = level; + remaining_blocks.emplace_back (link, level + 1); } - else if (level < lowest_level) + for (auto & dependent_block : existing->second->dependent_blocks) { - lowest_level = level; + remaining_blocks.emplace_back (dependent_block, level - 1); + } + processed_blocks.insert (hash); + nano::qualified_root root (previous, existing->second->status.winner->root ()); + auto existing_root (roots.get ().find (root)); + if (existing_root != roots.get ().end ()) + { + sum += nano::difficulty::to_multiplier (existing_root->difficulty, node.network_params.network.publish_threshold); + elections_list.emplace_back (root, level); + if (level > highest_level) + { + highest_level = level; + } + else if (level < lowest_level) + { + lowest_level = level; + } } } } + remaining_blocks.pop_front (); } - remaining_blocks.pop_front (); - } - if (!elections_list.empty ()) - { - double multiplier = sum / elections_list.size (); - uint64_t average = nano::difficulty::from_multiplier (multiplier, node.network_params.network.publish_threshold); - // Prevent overflow - int64_t limiter (0); - if (std::numeric_limits::max () - average < static_cast (highest_level)) - { - // Highest adjusted difficulty value should be std::numeric_limits::max () - limiter = std::numeric_limits::max () - average + highest_level; - debug_assert (std::numeric_limits::max () == average + highest_level - limiter); - } - else if (average < std::numeric_limits::min () - lowest_level) + if (!elections_list.empty ()) { - // Lowest adjusted difficulty value should be std::numeric_limits::min () - limiter = std::numeric_limits::min () - average + lowest_level; - debug_assert (std::numeric_limits::min () == average + lowest_level - limiter); - } + double multiplier = sum / elections_list.size (); + uint64_t average = nano::difficulty::from_multiplier (multiplier, node.network_params.network.publish_threshold); + // Prevent overflow + int64_t limiter (0); + if (std::numeric_limits::max () - average < static_cast (highest_level)) + { + // Highest adjusted difficulty value should be std::numeric_limits::max () + limiter = std::numeric_limits::max () - average + highest_level; + debug_assert (std::numeric_limits::max () == average + highest_level - limiter); + } + else if (average < std::numeric_limits::min () - lowest_level) + { + // Lowest adjusted difficulty value should be std::numeric_limits::min () + limiter = std::numeric_limits::min () - average + lowest_level; + debug_assert (std::numeric_limits::min () == average + lowest_level - limiter); + } - // Set adjusted difficulty - for (auto & item : elections_list) - { - auto existing_root (roots.get ().find (item.first)); - uint64_t difficulty_a = average + item.second - limiter; - roots.get ().modify (existing_root, [difficulty_a](nano::conflict_info & info_a) { - info_a.adjusted_difficulty = difficulty_a; - }); + // Set adjusted difficulty + for (auto & item : elections_list) + { + auto existing_root (roots.get ().find (item.first)); + uint64_t difficulty_a = average + item.second - limiter; + if (existing_root->adjusted_difficulty != difficulty_a) + { + roots.get ().modify (existing_root, [difficulty_a](nano::conflict_info & info_a) { + info_a.adjusted_difficulty = difficulty_a; + }); + } + } } } } @@ -778,6 +792,7 @@ void nano::active_transactions::erase (nano::block const & block_a) if (root_it != roots.get ().end ()) { root_it->election->clear_blocks (); + root_it->election->adjust_dependent_difficulty (); roots.get ().erase (root_it); node.logger.try_log (boost::str (boost::format ("Election erased for block block %1% root %2%") % block_a.hash ().to_string () % block_a.root ().to_string ())); } diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index 6a5b58c0ce..d1ffcb3d09 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -95,7 +95,8 @@ class active_transactions final bool active (nano::qualified_root const &); std::shared_ptr election (nano::qualified_root const &) const; void update_difficulty (std::shared_ptr); - void adjust_difficulty (nano::block_hash const &); + void add_adjust_difficulty (nano::block_hash const &); + void update_adjusted_difficulty (); void update_active_difficulty (nano::unique_lock &); uint64_t active_difficulty (); uint64_t limited_active_difficulty (); @@ -181,6 +182,7 @@ class active_transactions final void prioritize_account_for_confirmation (prioritize_num_uncemented &, size_t &, nano::account const &, nano::account_info const &, uint64_t); static size_t constexpr max_priority_cementable_frontiers{ 100000 }; static size_t constexpr confirmed_frontiers_max_pending_cut_off{ 1000 }; + std::deque adjust_difficulty_list; // clang-format off using ordered_cache = boost::multi_index_containerprocess_confirmed (status_l, this_l); confirmation_action_l (status_l.winner); }); + adjust_dependent_difficulty (); } } @@ -367,7 +368,7 @@ void nano::election::confirm_if_quorum () node.block_processor.force (block_l); status.winner = block_l; update_dependent (); - node.active.adjust_difficulty (winner_hash_l); + node.active.add_adjust_difficulty (winner_hash_l); } if (have_quorum (tally_l, sum)) { @@ -522,6 +523,14 @@ void nano::election::update_dependent () } } +void nano::election::adjust_dependent_difficulty () +{ + for (auto & dependent_block : dependent_blocks) + { + node.active.add_adjust_difficulty (dependent_block); + } +} + void nano::election::clear_blocks () { auto winner_hash (status.winner->hash ()); diff --git a/nano/node/election.hpp b/nano/node/election.hpp index 9339cc2b01..6ea73c11ce 100644 --- a/nano/node/election.hpp +++ b/nano/node/election.hpp @@ -78,6 +78,7 @@ class election final : public std::enable_shared_from_this bool publish (std::shared_ptr block_a); size_t last_votes_size (); void update_dependent (); + void adjust_dependent_difficulty (); void clear_blocks (); void insert_inactive_votes_cache (nano::block_hash const &);