Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update adjusted difficulty in batches #2604

Merged
10 changes: 8 additions & 2 deletions nano/core_test/active_transactions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ TEST (active_transactions, adjusted_difficulty_priority)
// Check adjusted difficulty
{
nano::lock_guard<std::mutex> 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);
Expand Down Expand Up @@ -151,6 +152,7 @@ TEST (active_transactions, adjusted_difficulty_priority)

// Check adjusted difficulty
nano::lock_guard<std::mutex> 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)
{
Expand Down Expand Up @@ -206,7 +208,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<std::uint64_t>::max ());
Expand Down Expand Up @@ -260,7 +263,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<std::uint64_t>::min () + 3);
Expand Down Expand Up @@ -382,6 +386,8 @@ TEST (active_transactions, prioritize_chains)
}
size_t seen (0);
{
nano::lock_guard<std::mutex> 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 ())
{
Expand Down
2 changes: 2 additions & 0 deletions nano/core_test/conflicts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ TEST (conflicts, adjusted_difficulty)
std::unordered_map<nano::block_hash, uint64_t> adjusted_difficulties;
{
nano::lock_guard<std::mutex> 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)
{
Expand Down Expand Up @@ -285,6 +286,7 @@ TEST (conflicts, adjusted_difficulty)
}
{
nano::lock_guard<std::mutex> guard (node1.active.mutex);
node1.active.update_adjusted_difficulty ();
ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), open_epoch2->hash ());
}
}
165 changes: 90 additions & 75 deletions nano/node/active_transactions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,12 @@ void nano::active_transactions::request_confirm (nano::unique_lock<std::mutex> &
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 ();
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 ());
Expand Down Expand Up @@ -277,6 +276,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);

Expand Down Expand Up @@ -480,7 +480,7 @@ std::pair<std::shared_ptr<nano::election>, bool> nano::active_transactions::inse
auto difficulty (block_a->difficulty ());
roots.get<tag_root> ().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);
}
}
Expand Down Expand Up @@ -597,97 +597,111 @@ void nano::active_transactions::update_difficulty (std::shared_ptr<nano::block>
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<std::pair<nano::block_hash, int64_t>> remaining_blocks;
remaining_blocks.emplace_back (hash_a, 0);
std::unordered_set<nano::block_hash> processed_blocks;
std::vector<std::pair<nano::qualified_root, int64_t>> 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<std::pair<nano::block_hash, int64_t>> remaining_blocks;
remaining_blocks.emplace_back (adjust_difficulty_item, 0);
adjust_difficulty_list.pop_front ();
std::vector<std::pair<nano::qualified_root, int64_t>> 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<tag_root> ().find (root));
if (existing_root != roots.get<tag_root> ().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<tag_root> ().find (root));
if (existing_root != roots.get<tag_root> ().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<std::uint64_t>::max () - average < static_cast<uint64_t> (highest_level))
{
// Highest adjusted difficulty value should be std::numeric_limits<std::uint64_t>::max ()
limiter = std::numeric_limits<std::uint64_t>::max () - average + highest_level;
debug_assert (std::numeric_limits<std::uint64_t>::max () == average + highest_level - limiter);
}
else if (average < std::numeric_limits<std::uint64_t>::min () - lowest_level)
if (!elections_list.empty ())
{
// Lowest adjusted difficulty value should be std::numeric_limits<std::uint64_t>::min ()
limiter = std::numeric_limits<std::uint64_t>::min () - average + lowest_level;
debug_assert (std::numeric_limits<std::uint64_t>::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<std::uint64_t>::max () - average < static_cast<uint64_t> (highest_level))
{
// Highest adjusted difficulty value should be std::numeric_limits<std::uint64_t>::max ()
limiter = std::numeric_limits<std::uint64_t>::max () - average + highest_level;
debug_assert (std::numeric_limits<std::uint64_t>::max () == average + highest_level - limiter);
}
else if (average < std::numeric_limits<std::uint64_t>::min () - lowest_level)
{
// Lowest adjusted difficulty value should be std::numeric_limits<std::uint64_t>::min ()
limiter = std::numeric_limits<std::uint64_t>::min () - average + lowest_level;
debug_assert (std::numeric_limits<std::uint64_t>::min () == average + lowest_level - limiter);
}

// Set adjusted difficulty
for (auto & item : elections_list)
{
auto existing_root (roots.get<tag_root> ().find (item.first));
uint64_t difficulty_a = average + item.second - limiter;
roots.get<tag_root> ().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<tag_root> ().find (item.first));
uint64_t difficulty_a = average + item.second - limiter;
if (existing_root->adjusted_difficulty != difficulty_a)
{
roots.get<tag_root> ().modify (existing_root, [difficulty_a](nano::conflict_info & info_a) {
info_a.adjusted_difficulty = difficulty_a;
});
}
}
}
}
}
Expand Down Expand Up @@ -774,6 +788,7 @@ void nano::active_transactions::erase (nano::block const & block_a)
if (root_it != roots.get<tag_root> ().end ())
{
root_it->election->clear_blocks ();
root_it->election->adjust_dependent_difficulty ();
roots.get<tag_root> ().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 ()));
}
Expand Down
4 changes: 3 additions & 1 deletion nano/node/active_transactions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ class active_transactions final
bool active (nano::qualified_root const &);
std::shared_ptr<nano::election> election (nano::qualified_root const &) const;
void update_difficulty (std::shared_ptr<nano::block>);
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<std::mutex> &);
uint64_t active_difficulty ();
uint64_t limited_active_difficulty ();
Expand Down Expand Up @@ -188,6 +189,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<nano::block_hash> adjust_difficulty_list;
// clang-format off
using ordered_cache = boost::multi_index_container<nano::inactive_cache_information,
mi::indexed_by<
Expand Down
11 changes: 10 additions & 1 deletion nano/node/election.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ void nano::election::confirm_once (nano::election_status_type type_a)
node_l->process_confirmed (status_l, this_l);
confirmation_action_l (status_l.winner);
});
adjust_dependent_difficulty ();
}
}

Expand Down Expand Up @@ -365,7 +366,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))
{
Expand Down Expand Up @@ -520,6 +521,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 ());
Expand Down
1 change: 1 addition & 0 deletions nano/node/election.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class election final : public std::enable_shared_from_this<nano::election>
bool publish (std::shared_ptr<nano::block> 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 &);

Expand Down