Election state refactor (#2535)

Converting the election class into a state machine instead of working on rebroadcast iterations.
This commit is contained in:
clemahieu 2020-03-02 10:36:13 +01:00 committed by GitHub
commit 5dac531c91
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 551 additions and 263 deletions

View file

@ -13,6 +13,7 @@ add_executable (core_test
conflicts.cpp
difficulty.cpp
distributed_work.cpp
election.cpp
entry.cpp
epochs.cpp
gap_cache.cpp

View file

@ -7,7 +7,7 @@
using namespace std::chrono_literals;
TEST (active_transactions, confirm_one)
TEST (active_transactions, confirm_active)
{
nano::system system (1);
auto & node1 = *system.nodes[0];
@ -15,7 +15,7 @@ TEST (active_transactions, confirm_one)
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
auto send (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ()));
system.deadline_set (5s);
while (!node1.active.empty () && !node1.block_confirmed_or_being_confirmed (node1.store.tx_begin_read (), send->hash ()))
while (!node1.active.empty () || !node1.block_confirmed_or_being_confirmed (node1.store.tx_begin_read (), send->hash ()))
{
ASSERT_NO_ERROR (system.poll ());
}
@ -27,12 +27,58 @@ TEST (active_transactions, confirm_one)
node1.network.flood_block (send, nano::buffer_drop_policy::no_limiter_drop);
ASSERT_NO_ERROR (system.poll ());
}
while (node2.ledger.cache.cemented_count < 2)
while (node2.ledger.cache.cemented_count < 2 || !node2.active.empty ())
{
ASSERT_NO_ERROR (system.poll ());
}
}
TEST (active_transactions, confirm_frontier)
{
nano::system system (1);
auto & node1 = *system.nodes[0];
// Send and vote for a block before peering with node2
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
auto send (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ()));
system.deadline_set (5s);
while (!node1.active.empty () || !node1.block_confirmed_or_being_confirmed (node1.store.tx_begin_read (), send->hash ()))
{
ASSERT_NO_ERROR (system.poll ());
}
auto & node2 = *system.add_node (nano::node_config (nano::get_available_port (), system.logging));
ASSERT_EQ (nano::process_result::progress, node2.process (*send).code);
system.deadline_set (5s);
while (node2.ledger.cache.cemented_count < 2 || !node2.active.empty ())
{
ASSERT_NO_ERROR (system.poll ());
}
}
TEST (active_transactions, confirm_dependent)
{
nano::system system;
nano::node_flags node_flags;
node_flags.disable_request_loop = true;
auto & node1 = *system.add_node (node_flags);
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
auto send1 (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ()));
auto send2 (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ()));
auto send3 (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ()));
nano::node_config node_config;
node_config.peering_port = nano::get_available_port ();
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
auto & node2 = *system.add_node (node_config);
node2.process_local (send1);
node2.process_local (send2);
node2.process_active (send3);
system.deadline_set (5s);
while (!node2.active.empty ())
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (4, node2.ledger.cache.cemented_count);
}
TEST (active_transactions, adjusted_difficulty_priority)
{
nano::system system;
@ -76,7 +122,7 @@ TEST (active_transactions, adjusted_difficulty_priority)
}
}
system.deadline_set (10s);
while (node1.ledger.cache.cemented_count < 5)
while (node1.ledger.cache.cemented_count < 5 || !node1.active.empty ())
{
ASSERT_NO_ERROR (system.poll ());
}
@ -400,7 +446,7 @@ TEST (active_transactions, inactive_votes_cache_fork)
while (!confirmed)
{
auto transaction (node.store.tx_begin_read ());
confirmed = node.block (send1->hash ()) != nullptr && node.ledger.block_confirmed (transaction, send1->hash ());
confirmed = node.block (send1->hash ()) != nullptr && node.ledger.block_confirmed (transaction, send1->hash ()) && node.active.empty ();
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (1, node.stats.count (nano::stat::type::election, nano::stat::detail::vote_cached));
@ -557,9 +603,11 @@ TEST (active_transactions, update_difficulty)
ASSERT_NE (existing3, node2.active.roots.end ());
auto const existing4 (node2.active.roots.find (send2->qualified_root ()));
ASSERT_NE (existing4, node2.active.roots.end ());
auto updated = (existing1->difficulty > difficulty1) && (existing2->difficulty > difficulty2);
auto propogated = (existing3->difficulty > difficulty1) && (existing4->difficulty > difficulty2);
done = updated && propogated;
auto updated1 = existing1->difficulty > difficulty1;
auto updated2 = existing2->difficulty > difficulty2;
auto propogated1 = existing3->difficulty > difficulty1;
auto propogated2 = existing4->difficulty > difficulty2;
done = updated1 && updated2 && propogated1 && propogated2;
}
ASSERT_NO_ERROR (system.poll ());
}
@ -583,15 +631,28 @@ TEST (active_transactions, vote_replays)
node.process_active (open1);
node.block_processor.flush ();
ASSERT_EQ (2, node.active.size ());
// First vote is not a replay and confirms the election, second vote should be indeterminate since the election no longer exists
// First vote is not a replay and confirms the election, second vote should be a replay since the election has confirmed but not yet removed
auto vote_send1 (std::make_shared<nano::vote> (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 0, send1));
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote_send1));
ASSERT_EQ (1, node.active.size ());
ASSERT_EQ (2, node.active.size ());
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_send1));
// Wait until the election is removed, at which point the vote should be indeterminate
system.deadline_set (3s);
while (node.active.size () != 1)
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote_send1));
// Open new account
auto vote_open1 (std::make_shared<nano::vote> (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 0, open1));
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote_open1));
ASSERT_TRUE (node.active.empty ());
ASSERT_EQ (1, node.active.size ());
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_open1));
system.deadline_set (3s);
while (!node.active.empty ())
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote_open1));
ASSERT_EQ (nano::Gxrb_ratio, node.ledger.weight (key.pub));
@ -607,7 +668,76 @@ TEST (active_transactions, vote_replays)
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote2_send2));
ASSERT_EQ (1, node.active.size ());
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote1_send2));
ASSERT_EQ (1, node.active.size ());
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote1_send2));
while (!node.active.empty ())
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (0, node.active.size ());
ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote1_send2));
ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote2_send2));
}
TEST (active_transactions, activate_dependencies)
{
// Ensure that we attempt to backtrack if an election isn't getting confirmed and there are more uncemented blocks to start elections for
nano::system system;
nano::node_config config (nano::get_available_port (), system.logging);
config.enable_voting = true;
nano::node_flags flags;
flags.disable_bootstrap_listener = true;
config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
auto node1 (system.add_node (config, flags));
config.peering_port = nano::get_available_port ();
auto node2 (system.add_node (config, flags));
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
nano::genesis genesis;
nano::block_builder builder;
system.deadline_set (std::chrono::seconds (15));
std::shared_ptr<nano::block> block0 = builder.state ()
.account (nano::test_genesis_key.pub)
.previous (genesis.hash ())
.representative (nano::test_genesis_key.pub)
.balance (nano::genesis_amount - nano::Gxrb_ratio)
.link (0)
.sign (nano::test_genesis_key.prv, nano::test_genesis_key.pub)
.work (node1->work_generate_blocking (genesis.hash ()).value ())
.build ();
// Establish a representative
node2->process_active (block0);
node2->block_processor.flush ();
while (node1->block (block0->hash ()) == nullptr)
{
ASSERT_NO_ERROR (system.poll ());
}
auto block1 = builder.state ()
.account (nano::test_genesis_key.pub)
.previous (block0->hash ())
.representative (nano::test_genesis_key.pub)
.balance (nano::genesis_amount - nano::Gxrb_ratio)
.link (0)
.sign (nano::test_genesis_key.prv, nano::test_genesis_key.pub)
.work (node1->work_generate_blocking (block0->hash ()).value ())
.build ();
{
auto transaction = node2->store.tx_begin_write ();
ASSERT_EQ (nano::process_result::progress, node2->ledger.process (transaction, *block1).code);
}
std::shared_ptr<nano::block> block2 = builder.state ()
.account (nano::test_genesis_key.pub)
.previous (block1->hash ())
.representative (nano::test_genesis_key.pub)
.balance (nano::genesis_amount - 2 * nano::Gxrb_ratio)
.link (0)
.sign (nano::test_genesis_key.prv, nano::test_genesis_key.pub)
.work (node1->work_generate_blocking (block1->hash ()).value ())
.build ();
node2->process_active (block2);
node2->block_processor.flush ();
while (node1->block (block2->hash ()) == nullptr)
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_NE (nullptr, node1->block (block2->hash ()));
}

View file

@ -1187,13 +1187,8 @@ TEST (confirmation_height, dependent_election)
add_callback_stats (*node);
// Wait until it has been processed
// Start an election and vote, should confirm the block
node->block_confirm (send2);
system.deadline_set (10s);
while (node->active.size () > 0)
{
ASSERT_NO_ERROR (system.poll ());
}
{
// The write guard prevents the confirmation height processor doing any writes.
@ -1426,17 +1421,6 @@ TEST (confirmation_height, election_winner_details_clearing)
add_callback_stats (*node);
node->block_confirm (send1);
system.deadline_set (10s);
while (node->active.size () > 0)
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (0, node->active.list_confirmed ().size ());
{
nano::lock_guard<std::mutex> guard (node->active.mutex);
ASSERT_EQ (0, node->active.blocks.size ());
}
system.deadline_set (10s);
while (node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out) != 2)

View file

@ -34,12 +34,12 @@ TEST (confirmation_solicitor, batches)
auto send (std::make_shared<nano::send_block> (nano::genesis_hash, nano::keypair ().pub, nano::genesis_amount - 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (nano::genesis_hash)));
for (size_t i (0); i < nano::network::confirm_req_hashes_max; ++i)
{
auto election (std::make_shared<nano::election> (node2, send, false, nullptr));
auto election (std::make_shared<nano::election> (node2, send, nullptr));
ASSERT_FALSE (node2.active.solicitor.add (*election));
}
ASSERT_EQ (1, node2.active.solicitor.max_confirm_req_batches);
// Reached the maximum amount of requests for the channel
auto election (std::make_shared<nano::election> (node2, send, false, nullptr));
auto election (std::make_shared<nano::election> (node2, send, nullptr));
ASSERT_TRUE (node2.active.solicitor.add (*election));
// Broadcasting should be immediate
ASSERT_EQ (0, node2.stats.count (nano::stat::type::message, nano::stat::detail::publish, nano::stat::dir::out));

View file

@ -0,0 +1,18 @@
#include <nano/core_test/testutil.hpp>
#include <nano/node/election.hpp>
#include <nano/node/testing.hpp>
#include <gtest/gtest.h>
TEST (election, construction)
{
nano::system system (1);
nano::genesis genesis;
auto & node = *system.nodes[0];
auto election = node.active.insert (genesis.open).first;
ASSERT_TRUE (election->idle ());
election->transition_active ();
ASSERT_FALSE (election->idle ());
election->transition_passive ();
ASSERT_FALSE (election->idle ());
}

View file

@ -768,7 +768,7 @@ TEST (votes, add_one)
auto vote1 (std::make_shared<nano::vote> (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 1, send1));
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1));
auto vote2 (std::make_shared<nano::vote> (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 2, send1));
ASSERT_EQ (nano::vote_code::indeterminate, node1.active.vote (vote2));
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2));
lock.lock ();
ASSERT_EQ (2, election1.first->last_votes.size ());
auto existing1 (election1.first->last_votes.find (nano::test_genesis_key.pub));

View file

@ -2134,7 +2134,6 @@ TEST (node, rep_weight)
auto vote0 = std::make_shared<nano::vote> (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 0, genesis.open);
auto vote1 = std::make_shared<nano::vote> (keypair1.pub, keypair1.prv, 0, genesis.open);
auto vote2 = std::make_shared<nano::vote> (keypair2.pub, keypair2.prv, 0, genesis.open);
node.rep_crawler.add (genesis.open->hash ());
node.rep_crawler.response (channel0, vote0);
node.rep_crawler.response (channel1, vote1);
node.rep_crawler.response (channel2, vote2);
@ -2217,7 +2216,6 @@ TEST (node, rep_remove)
nano::amount amount100 (100);
node.network.udp_channels.insert (endpoint0, node.network_params.protocol.protocol_version);
auto vote1 = std::make_shared<nano::vote> (keypair1.pub, keypair1.prv, 0, genesis.open);
node.rep_crawler.add (genesis.hash ());
node.rep_crawler.response (channel0, vote1);
system.deadline_set (5s);
while (node.rep_crawler.representative_count () != 1)

View file

@ -17,12 +17,7 @@ node (node_a),
multipliers_cb (20, 1.),
trended_active_difficulty (node_a.network_params.network.publish_threshold),
solicitor (node_a.network, node_a.network_params.network),
long_election_threshold (node_a.network_params.network.is_test_network () ? 2s : 24s),
election_request_delay (node_a.network_params.network.is_test_network () ? 0s : 1s),
election_time_to_live (node_a.network_params.network.is_test_network () ? 0s : 10s),
min_time_between_requests (node_a.network_params.network.is_test_network () ? 25ms : 3s),
min_time_between_floods (node_a.network_params.network.is_test_network () ? 50ms : 6s),
min_request_count_flood (node_a.network_params.network.is_test_network () ? 0 : 2),
election_time_to_live (node_a.network_params.network.is_test_network () ? 0s : 2s),
thread ([this]() {
nano::thread_role::set (nano::thread_role::name::request_loop);
request_loop ();
@ -38,8 +33,6 @@ thread ([this]() {
this->block_already_cemented_callback (hash_a);
});
debug_assert (min_time_between_requests > std::chrono::milliseconds (node.network_params.network.request_interval_ms));
debug_assert (min_time_between_floods > std::chrono::milliseconds (node.network_params.network.request_interval_ms));
nano::unique_lock<std::mutex> lock (mutex);
condition.wait (lock, [& started = started] { return started; });
}
@ -104,8 +97,10 @@ void nano::active_transactions::search_frontiers (nano::transaction const & tran
if (info.block_count > confirmation_height_info.height && !this->confirmation_height_processor.is_processing_block (info.head))
{
auto block (this->node.store.block_get (transaction_a, info.head));
if (this->insert (block, true).first)
auto election = this->insert (block);
if (election.second)
{
election.first->transition_active ();
++elections_count;
// Calculate votes for local representatives
if (representative)
@ -163,7 +158,7 @@ void nano::active_transactions::block_cemented_callback (std::shared_ptr<nano::b
// Make sure mutex is held before election usage so we know that confirm_once has
// finished removing the root from active to avoid any data race.
nano::unique_lock<std::mutex> lk (mutex);
if (election->confirmed () && !election->stopped && election->status.winner->hash () == hash)
if (election->confirmed () && election->status.winner->hash () == hash)
{
add_confirmed (election->status, block_a->qualified_root ());
lk.unlock ();
@ -206,60 +201,10 @@ void nano::active_transactions::block_already_cemented_callback (nano::block_has
election_winner_details.erase (hash_a);
}
void nano::active_transactions::election_escalate (std::shared_ptr<nano::election> & election_l, nano::transaction const & transaction_l, size_t const & roots_size_l)
{
constexpr unsigned high_confirmation_request_count{ 128 };
// Log votes for very long unconfirmed elections
if (election_l->confirmation_request_count % (4 * high_confirmation_request_count) == 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<nano::block> 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))
{
insert_impl (std::move (previous_l), true);
escalated_l = true;
}
}
/* 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_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))
{
insert_impl (std::move (source_l), true);
escalated_l = true;
}
}
}
if (escalated_l)
{
election_l->update_dependent ();
}
}
}
void nano::active_transactions::request_confirm (nano::unique_lock<std::mutex> & lock_a)
{
debug_assert (!mutex.try_lock ());
auto transaction_l (node.store.tx_begin_read ());
std::unordered_set<nano::qualified_root> inactive_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)
@ -283,19 +228,9 @@ void nano::active_transactions::request_confirm (nano::unique_lock<std::mutex> &
solicitor.prepare (node.rep_crawler.representatives (node.network_params.protocol.tcp_realtime_protocol_version_min));
lock_a.lock ();
auto const now (std::chrono::steady_clock::now ());
// Any new election started from process_live only gets requests after at least 1 second
auto cutoff_l (now - election_request_delay);
// Elections taking too long get escalated
auto long_election_cutoff_l (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 (now - election_time_to_live);
// Rate-limitting floods
auto const flood_cutoff (now - min_time_between_floods);
// Rate-limitting confirmation requests
auto const request_cutoff (now - min_time_between_requests);
auto election_ttl_cutoff_l (std::chrono::steady_clock::now () - election_time_to_live);
auto roots_size_l (roots.size ());
bool saturated_l (roots_size_l > node.config.active_elections_size / 2);
auto & sorted_roots_l = roots.get<tag_difficulty> ();
size_t count_l{ 0 };
@ -306,60 +241,22 @@ void nano::active_transactions::request_confirm (nano::unique_lock<std::mutex> &
* 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)
for (auto i = sorted_roots_l.begin (), n = sorted_roots_l.end (); i != n; ++count_l)
{
auto election_l (i->election);
auto root_l (i->root);
if (election_l->confirmed () || (election_l->confirmation_request_count != 0 && !node.ledger.could_fit (transaction_l, *election_l->status.winner)))
auto & election_l (i->election);
if ((count_l >= node.config.active_elections_size && election_l->election_start < election_ttl_cutoff_l && !node.wallets.watcher->is_watched (i->root)) || election_l->transition_time (saturated_l))
{
election_l->stop ();
election_l->clear_blocks ();
i = sorted_roots_l.erase (i);
}
// Erase finished elections
if ((election_l->stopped))
else
{
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);
}
// Attempt obtaining votes
else if (election_l->skip_delay || election_l->election_start < cutoff_l)
{
// Broadcast the winner when elections are taking longer to confirm
if (election_l->confirmation_request_count >= min_request_count_flood && election_l->last_broadcast < flood_cutoff && !solicitor.broadcast (*election_l))
{
election_l->last_broadcast = now;
}
// Rate-limited requests for confirmation
else if (election_l->last_request < request_cutoff && !solicitor.add (*election_l))
{
++election_l->confirmation_request_count;
election_l->last_request = now;
}
// 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);
}
++i;
}
}
lock_a.unlock ();
solicitor.flush ();
lock_a.lock ();
// Erase inactive elections
for (auto i (inactive_l.begin ()), n (inactive_l.end ()); i != n; ++i)
{
auto root_it (roots.get<tag_root> ().find (*i));
if (root_it != roots.get<tag_root> ().end ())
{
root_it->election->clear_blocks ();
root_it->election->clear_dependent ();
roots.get<tag_root> ().erase (root_it);
}
}
}
void nano::active_transactions::request_loop ()
@ -566,7 +463,7 @@ void nano::active_transactions::stop ()
roots.clear ();
}
std::pair<std::shared_ptr<nano::election>, bool> nano::active_transactions::insert_impl (std::shared_ptr<nano::block> block_a, bool const skip_delay_a, std::function<void(std::shared_ptr<nano::block>)> const & confirmation_action_a)
std::pair<std::shared_ptr<nano::election>, bool> nano::active_transactions::insert_impl (std::shared_ptr<nano::block> block_a, std::function<void(std::shared_ptr<nano::block>)> const & confirmation_action_a)
{
std::pair<std::shared_ptr<nano::election>, bool> result = { nullptr, false };
if (!stopped)
@ -579,7 +476,7 @@ std::pair<std::shared_ptr<nano::election>, bool> nano::active_transactions::inse
{
result.second = true;
auto hash (block_a->hash ());
result.first = nano::make_shared<nano::election> (node, block_a, skip_delay_a, confirmation_action_a);
result.first = nano::make_shared<nano::election> (node, block_a, confirmation_action_a);
auto difficulty (block_a->difficulty ());
roots.get<tag_root> ().emplace (nano::conflict_info{ root, difficulty, difficulty, result.first });
blocks.emplace (hash, result.first);
@ -595,10 +492,10 @@ std::pair<std::shared_ptr<nano::election>, bool> nano::active_transactions::inse
return result;
}
std::pair<std::shared_ptr<nano::election>, bool> nano::active_transactions::insert (std::shared_ptr<nano::block> block_a, bool const skip_delay_a, std::function<void(std::shared_ptr<nano::block>)> const & confirmation_action_a)
std::pair<std::shared_ptr<nano::election>, bool> nano::active_transactions::insert (std::shared_ptr<nano::block> block_a, std::function<void(std::shared_ptr<nano::block>)> const & confirmation_action_a)
{
nano::lock_guard<std::mutex> lock (mutex);
return insert_impl (block_a, skip_delay_a, confirmation_action_a);
return insert_impl (block_a, confirmation_action_a);
}
// Validate a vote and apply it to the current election if one exists
@ -671,6 +568,18 @@ bool nano::active_transactions::active (nano::block const & block_a)
return active (block_a.qualified_root ());
}
std::shared_ptr<nano::election> nano::active_transactions::election (nano::qualified_root const & root_a) const
{
std::shared_ptr<nano::election> result;
nano::lock_guard<std::mutex> lock (mutex);
auto existing = roots.get<tag_root> ().find (root_a);
if (existing != roots.get<tag_root> ().end ())
{
result = existing->election;
}
return result;
}
void nano::active_transactions::update_difficulty (std::shared_ptr<nano::block> block_a)
{
nano::unique_lock<std::mutex> lock (mutex);
@ -711,7 +620,7 @@ void nano::active_transactions::adjust_difficulty (nano::block_hash const & hash
if (processed_blocks.find (hash) == processed_blocks.end ())
{
auto existing (blocks.find (hash));
if (existing != blocks.end () && !existing->second->confirmed () && !existing->second->stopped && existing->second->status.winner->hash () == hash)
if (existing != blocks.end () && !existing->second->confirmed () && existing->second->status.winner->hash () == hash)
{
auto previous (existing->second->status.winner->previous ());
if (!previous.is_zero ())
@ -793,10 +702,9 @@ void nano::active_transactions::update_active_difficulty (nano::unique_lock<std:
std::vector<uint64_t> active_root_difficulties;
active_root_difficulties.reserve (std::min (sorted_roots.size (), node.config.active_elections_size));
size_t count (0);
auto cutoff (std::chrono::steady_clock::now () - election_request_delay - 1s);
for (auto it (sorted_roots.begin ()), end (sorted_roots.end ()); it != end && count++ < node.config.active_elections_size; ++it)
{
if (!it->election->confirmed () && !it->election->stopped && it->election->election_start < cutoff)
if (!it->election->confirmed () && !it->election->idle ())
{
active_root_difficulties.push_back (it->adjusted_difficulty);
}
@ -865,9 +773,7 @@ void nano::active_transactions::erase (nano::block const & block_a)
auto root_it (roots.get<tag_root> ().find (block_a.qualified_root ()));
if (root_it != roots.get<tag_root> ().end ())
{
root_it->election->stop ();
root_it->election->clear_blocks ();
root_it->election->clear_dependent ();
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 ()));
}
@ -894,7 +800,7 @@ bool nano::active_transactions::publish (std::shared_ptr<nano::block> block_a)
{
auto election (existing->election);
result = election->publish (block_a);
if (!result && !election->confirmed ())
if (!result)
{
blocks.emplace (block_a->hash (), election);
}
@ -910,7 +816,7 @@ boost::optional<nano::election_status_type> nano::active_transactions::confirm_b
auto existing (blocks.find (hash));
if (existing != blocks.end ())
{
if (!existing->second->confirmed () && !existing->second->stopped && existing->second->status.winner->hash () == hash)
if (!existing->second->confirmed () && existing->second->status.winner->hash () == hash)
{
existing->second->confirm_once (nano::election_status_type::active_confirmation_height);
return nano::election_status_type::active_confirmation_height;

View file

@ -74,6 +74,8 @@ public:
// Holds all active blocks i.e. recently added blocks that need confirmation
class active_transactions final
{
friend class nano::election;
// clang-format off
class tag_account {};
class tag_difficulty {};
@ -90,13 +92,14 @@ public:
// Start an election for a block
// Call action with confirmed block, may be different than what we started with
// clang-format off
std::pair<std::shared_ptr<nano::election>, bool> insert (std::shared_ptr<nano::block>, bool const = false, std::function<void(std::shared_ptr<nano::block>)> const & = [](std::shared_ptr<nano::block>) {});
std::pair<std::shared_ptr<nano::election>, bool> insert (std::shared_ptr<nano::block>, std::function<void(std::shared_ptr<nano::block>)> const & = [](std::shared_ptr<nano::block>) {});
// clang-format on
// Distinguishes replay votes, cannot be determined if the block is not in any election
nano::vote_code vote (std::shared_ptr<nano::vote>);
// Is the root of this block in the roots container
bool active (nano::block const &);
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 update_active_difficulty (nano::unique_lock<std::mutex> &);
@ -130,7 +133,7 @@ public:
void erase_inactive_votes_cache (nano::block_hash const &);
nano::confirmation_height_processor & confirmation_height_processor;
nano::node & node;
std::mutex mutex;
mutable std::mutex mutex;
boost::circular_buffer<double> multipliers_cb;
uint64_t trended_active_difficulty;
size_t priority_cementable_frontiers_size ();
@ -147,11 +150,10 @@ private:
// Call action with confirmed block, may be different than what we started with
// clang-format off
std::pair<std::shared_ptr<nano::election>, bool> insert_impl (std::shared_ptr<nano::block>, bool const = false, std::function<void(std::shared_ptr<nano::block>)> const & = [](std::shared_ptr<nano::block>) {});
std::pair<std::shared_ptr<nano::election>, bool> insert_impl (std::shared_ptr<nano::block>, std::function<void(std::shared_ptr<nano::block>)> const & = [](std::shared_ptr<nano::block>) {});
// clang-format on
void request_loop ();
void search_frontiers (nano::transaction const &);
void election_escalate (std::shared_ptr<nano::election> &, nano::transaction const &, size_t const &);
void request_confirm (nano::unique_lock<std::mutex> &);
nano::account next_frontier_account{ 0 };
std::chrono::steady_clock::time_point next_frontier_check{ std::chrono::steady_clock::now () };
@ -159,18 +161,8 @@ private:
bool started{ false };
std::atomic<bool> stopped{ false };
// Minimum time an election must be active before escalation
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;
// Minimum time between confirmation requests for an election
std::chrono::milliseconds const min_time_between_requests;
// Minimum time between broadcasts of the current winner of an election, as a backup to requesting confirmations
std::chrono::milliseconds const min_time_between_floods;
// Minimum election request count to start broadcasting blocks, as a backup to requesting confirmations
size_t const min_request_count_flood;
// clang-format off
boost::multi_index_container<nano::qualified_root,
@ -206,7 +198,6 @@ private:
ordered_cache inactive_votes_cache;
// clang-format on
bool inactive_votes_bootstrap_check (std::vector<nano::account> const &, nano::block_hash const &, bool &);
static size_t constexpr dropped_elections_cache_max{ 32 * 1024 };
boost::thread thread;
friend class confirmation_height_prioritize_frontiers_Test;

View file

@ -372,7 +372,11 @@ void nano::block_processor::process_live (nano::block_hash const & hash_a, std::
}
// Start collecting quorum on block
node.active.insert (block_a, false);
auto election = node.active.insert (block_a);
if (election.second)
{
election.first->transition_passive ();
}
// Announce block contents to the network
if (initial_publish_a)

View file

@ -3,20 +3,29 @@
#include <boost/format.hpp>
using namespace std::chrono;
int constexpr nano::election::passive_duration_factor;
int constexpr nano::election::active_duration_factor;
int constexpr nano::election::confirmed_duration_factor;
int constexpr nano::election::confirmed_duration_factor_saturated;
std::chrono::milliseconds nano::election::base_latency () const
{
return node.network_params.network.is_test_network () ? 25ms : 1000ms;
}
nano::election_vote_result::election_vote_result (bool replay_a, bool processed_a)
{
replay = replay_a;
processed = processed_a;
}
nano::election::election (nano::node & node_a, std::shared_ptr<nano::block> block_a, bool const skip_delay_a, std::function<void(std::shared_ptr<nano::block>)> const & confirmation_action_a) :
nano::election::election (nano::node & node_a, std::shared_ptr<nano::block> block_a, std::function<void(std::shared_ptr<nano::block>)> const & confirmation_action_a) :
confirmation_action (confirmation_action_a),
confirmed_m (false),
state_start (std::chrono::steady_clock::now ()),
node (node_a),
election_start (std::chrono::steady_clock::now ()),
status ({ block_a, 0, std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::system_clock::now ().time_since_epoch ()), std::chrono::duration_values<std::chrono::milliseconds>::zero (), 0, 1, 0, nano::election_status_type::ongoing }),
skip_delay (skip_delay_a),
stopped (false)
status ({ block_a, 0, std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::system_clock::now ().time_since_epoch ()), std::chrono::duration_values<std::chrono::milliseconds>::zero (), 0, 1, 0, nano::election_status_type::ongoing })
{
last_votes.emplace (node.network_params.random.not_an_account, nano::vote_info{ std::chrono::steady_clock::now (), 0, block_a->hash () });
blocks.emplace (block_a->hash (), block_a);
@ -26,7 +35,7 @@ stopped (false)
void nano::election::confirm_once (nano::election_status_type type_a)
{
debug_assert (!node.active.mutex.try_lock ());
if (!confirmed_m.exchange (true))
if (state_m.exchange (nano::election::state_t::confirmed) != nano::election::state_t::confirmed)
{
status.election_end = std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::system_clock::now ().time_since_epoch ());
status.election_duration = std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::steady_clock::now () - election_start);
@ -48,30 +57,254 @@ 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);
});
clear_blocks ();
clear_dependent ();
node.active.roots.erase (status.winner->qualified_root ());
}
}
void nano::election::stop ()
bool nano::election::valid_change (nano::election::state_t expected_a, nano::election::state_t desired_a) const
{
bool result = false;
switch (expected_a)
{
case nano::election::state_t::idle:
switch (desired_a)
{
case nano::election::state_t::passive:
case nano::election::state_t::active:
result = true;
break;
default:
break;
}
break;
case nano::election::state_t::passive:
switch (desired_a)
{
case nano::election::state_t::idle:
case nano::election::state_t::active:
case nano::election::state_t::confirmed:
case nano::election::state_t::expired_unconfirmed:
result = true;
break;
default:
break;
}
break;
case nano::election::state_t::active:
switch (desired_a)
{
case nano::election::state_t::idle:
case nano::election::state_t::backtracking:
case nano::election::state_t::confirmed:
case nano::election::state_t::expired_unconfirmed:
result = true;
break;
default:
break;
}
case nano::election::state_t::backtracking:
switch (desired_a)
{
case nano::election::state_t::idle:
case nano::election::state_t::confirmed:
case nano::election::state_t::expired_unconfirmed:
result = true;
break;
default:
break;
}
case nano::election::state_t::confirmed:
switch (desired_a)
{
case nano::election::state_t::expired_confirmed:
result = true;
break;
default:
break;
}
case nano::election::state_t::expired_unconfirmed:
break;
case nano::election::state_t::expired_confirmed:
break;
}
return result;
}
bool nano::election::state_change (nano::election::state_t expected_a, nano::election::state_t desired_a)
{
debug_assert (!timepoints_mutex.try_lock ());
bool result = true;
if (valid_change (expected_a, desired_a))
{
if (state_m.compare_exchange_strong (expected_a, desired_a))
{
state_start = std::chrono::steady_clock::now ();
result = false;
}
}
else
{
debug_assert (false);
}
return result;
}
void nano::election::send_confirm_req ()
{
if (last_req + std::chrono::seconds (15) < std::chrono::steady_clock::now ())
{
if (!node.active.solicitor.add (*this))
{
last_req = std::chrono::steady_clock::now ();
}
}
}
void nano::election::transition_passive ()
{
nano::lock_guard<std::mutex> guard (timepoints_mutex);
transition_passive_impl ();
}
void nano::election::transition_passive_impl ()
{
state_change (nano::election::state_t::idle, nano::election::state_t::passive);
}
void nano::election::transition_active ()
{
nano::lock_guard<std::mutex> guard (timepoints_mutex);
transition_active_impl ();
}
void nano::election::transition_active_impl ()
{
if (!state_change (nano::election::state_t::idle, nano::election::state_t::active))
{
if (base_latency () * 5 < std::chrono::steady_clock::now () - last_block)
{
last_block = std::chrono::steady_clock::now ();
node.network.flood_block (status.winner);
}
}
}
bool nano::election::idle () const
{
return state_m == nano::election::state_t::idle;
}
bool nano::election::confirmed () const
{
return state_m == nano::election::state_t::confirmed || state_m == nano::election::state_t::expired_confirmed;
}
void nano::election::activate_dependencies ()
{
auto transaction = node.store.tx_begin_read ();
bool escalated_l (false);
std::shared_ptr<nano::block> previous_l;
auto previous_hash_l (status.winner->previous ());
if (!previous_hash_l.is_zero () && node.active.blocks.find (previous_hash_l) == node.active.blocks.end ())
{
previous_l = node.store.block_get (transaction, previous_hash_l);
if (previous_l != nullptr && !node.block_confirmed_or_being_confirmed (transaction, previous_hash_l))
{
auto election = node.active.insert_impl (previous_l);
if (election.second)
{
election.first->transition_active ();
escalated_l = true;
}
}
}
/* 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_l (node.ledger.block_source (transaction, *status.winner));
if (!source_hash_l.is_zero () && source_hash_l != previous_hash_l && node.active.blocks.find (source_hash_l) == node.active.blocks.end ())
{
auto source_l (node.store.block_get (transaction, source_hash_l));
if (source_l != nullptr && !node.block_confirmed_or_being_confirmed (transaction, source_hash_l))
{
auto election = node.active.insert_impl (source_l);
if (election.second)
{
election.first->transition_active ();
escalated_l = true;
}
}
}
}
if (escalated_l)
{
update_dependent ();
}
}
void nano::election::broadcast_block ()
{
if (base_latency () * 5 < std::chrono::steady_clock::now () - last_block)
{
if (!node.active.solicitor.broadcast (*this))
{
last_block = std::chrono::steady_clock::now ();
}
}
}
bool nano::election::transition_time (bool const saturated_a)
{
debug_assert (!node.active.mutex.try_lock ());
if (!stopped && !confirmed ())
nano::unique_lock<std::mutex> lock (timepoints_mutex);
bool result = false;
switch (state_m)
{
stopped = true;
status.election_end = std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::system_clock::now ().time_since_epoch ());
status.election_duration = std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::steady_clock::now () - election_start);
status.confirmation_request_count = confirmation_request_count;
status.block_count = nano::narrow_cast<decltype (status.block_count)> (blocks.size ());
status.voter_count = nano::narrow_cast<decltype (status.voter_count)> (last_votes.size ());
status.type = nano::election_status_type::stopped;
case nano::election::state_t::idle:
break;
case nano::election::state_t::passive:
{
if (base_latency () * passive_duration_factor < std::chrono::steady_clock::now () - state_start)
{
state_change (nano::election::state_t::passive, nano::election::state_t::active);
}
break;
}
case nano::election::state_t::active:
broadcast_block ();
send_confirm_req ();
if (base_latency () * active_duration_factor < std::chrono::steady_clock::now () - state_start)
{
state_change (nano::election::state_t::active, nano::election::state_t::backtracking);
lock.unlock ();
activate_dependencies ();
}
break;
case nano::election::state_t::backtracking:
broadcast_block ();
send_confirm_req ();
break;
case nano::election::state_t::confirmed:
if (base_latency () * (saturated_a ? confirmed_duration_factor_saturated : confirmed_duration_factor) < std::chrono::steady_clock::now () - state_start)
{
result = true;
state_change (nano::election::state_t::confirmed, nano::election::state_t::expired_confirmed);
}
break;
case nano::election::state_t::expired_unconfirmed:
case nano::election::state_t::expired_confirmed:
debug_assert (false);
break;
}
}
bool nano::election::confirmed ()
{
return confirmed_m;
// Note: lock (timepoints_mutex) is at an unknown state here - possibly unlocked before activate_dependencies
if (!confirmed () && std::chrono::minutes (5) < std::chrono::steady_clock::now () - election_start)
{
result = true;
state_change (state_m.load (), nano::election::state_t::expired_unconfirmed);
status.type = nano::election_status_type::stopped;
log_votes (tally ());
}
return result;
}
bool nano::election::have_quorum (nano::tally_t const & tally_a, nano::uint128_t tally_sum) const
@ -189,10 +422,10 @@ nano::election_vote_result nano::election::vote (nano::account rep, uint64_t seq
}
else
{
auto last_vote (last_vote_it->second);
if (last_vote.sequence < sequence || (last_vote.sequence == sequence && last_vote.hash < block_hash))
auto last_vote_l (last_vote_it->second);
if (last_vote_l.sequence < sequence || (last_vote_l.sequence == sequence && last_vote_l.hash < block_hash))
{
if (last_vote.time <= std::chrono::steady_clock::now () - std::chrono::seconds (cooldown))
if (last_vote_l.time <= std::chrono::steady_clock::now () - std::chrono::seconds (cooldown))
{
should_process = true;
}
@ -217,8 +450,9 @@ nano::election_vote_result nano::election::vote (nano::account rep, uint64_t seq
bool nano::election::publish (std::shared_ptr<nano::block> block_a)
{
auto result (false);
if (blocks.size () >= 10)
// Do not insert new blocks if already confirmed
auto result (confirmed ());
if (!result && blocks.size () >= 10)
{
if (last_tally[block_a->hash ()] < node.online_reps.online_stake () / 10)
{
@ -276,7 +510,7 @@ void nano::election::update_dependent ()
for (auto & block_search : blocks_search)
{
auto existing (node.active.blocks.find (block_search));
if (existing != node.active.blocks.end () && !existing->second->confirmed () && !existing->second->stopped)
if (existing != node.active.blocks.end () && !existing->second->confirmed ())
{
if (existing->second->dependent_blocks.find (hash) == existing->second->dependent_blocks.end ())
{
@ -286,27 +520,18 @@ void nano::election::update_dependent ()
}
}
void nano::election::clear_dependent ()
{
for (auto & dependent_block : dependent_blocks)
{
node.active.adjust_difficulty (dependent_block);
}
}
void nano::election::clear_blocks ()
{
auto winner_hash (status.winner->hash ());
for (auto & block : blocks)
for (auto const & block : blocks)
{
auto & hash (block.first);
auto erased (node.active.blocks.erase (hash));
(void)erased;
// clear_blocks () can be called in active_transactions::publish () before blocks insertion if election was confirmed
debug_assert (erased == 1 || confirmed ());
debug_assert (erased == 1);
node.active.erase_inactive_votes_cache (hash);
// Notify observers about dropped elections & blocks lost confirmed elections
if (stopped || hash != winner_hash)
if (!confirmed () || hash != winner_hash)
{
node.observers.active_stopped.notify (hash);
}

View file

@ -50,11 +50,42 @@ public:
};
class election final : public std::enable_shared_from_this<nano::election>
{
// Minimum time between broadcasts of the current winner of an election, as a backup to requesting confirmations
std::chrono::milliseconds base_latency () const;
std::function<void(std::shared_ptr<nano::block>)> confirmation_action;
std::atomic<bool> confirmed_m;
private: // State management
enum class state_t
{
idle,
passive,
active,
backtracking,
confirmed,
expired_confirmed,
expired_unconfirmed
};
static int constexpr passive_duration_factor = 5;
static int constexpr active_duration_factor = 20;
static int constexpr confirmed_duration_factor = 10;
static int constexpr confirmed_duration_factor_saturated = 1;
std::atomic<nano::election::state_t> state_m = { state_t::idle };
// Protects state_start, last_vote and last_block
std::mutex timepoints_mutex;
std::chrono::steady_clock::time_point state_start = { std::chrono::steady_clock::now () };
std::chrono::steady_clock::time_point last_vote = { std::chrono::steady_clock::time_point () };
std::chrono::steady_clock::time_point last_block = { std::chrono::steady_clock::time_point () };
std::chrono::steady_clock::time_point last_req = { std::chrono::steady_clock::time_point () };
bool valid_change (nano::election::state_t, nano::election::state_t) const;
bool state_change (nano::election::state_t, nano::election::state_t);
void broadcast_block ();
void send_confirm_req ();
void activate_dependencies ();
public:
election (nano::node &, std::shared_ptr<nano::block>, bool const, std::function<void(std::shared_ptr<nano::block>)> const &);
election (nano::node &, std::shared_ptr<nano::block>, std::function<void(std::shared_ptr<nano::block>)> const &);
nano::election_vote_result vote (nano::account, uint64_t, nano::block_hash);
nano::tally_t tally ();
// Check if we have vote quorum
@ -66,22 +97,28 @@ public:
bool publish (std::shared_ptr<nano::block> block_a);
size_t last_votes_size ();
void update_dependent ();
void clear_dependent ();
void clear_blocks ();
void insert_inactive_votes_cache (nano::block_hash const &);
void stop ();
bool confirmed ();
public: // State transitions
bool transition_time (bool const saturated);
void transition_passive ();
void transition_active ();
private:
void transition_passive_impl ();
void transition_active_impl ();
public:
bool idle () const;
bool confirmed () const;
nano::node & node;
std::unordered_map<nano::account, nano::vote_info> last_votes;
std::unordered_map<nano::block_hash, std::shared_ptr<nano::block>> blocks;
std::chrono::steady_clock::time_point election_start;
std::chrono::steady_clock::time_point election_start = { std::chrono::steady_clock::now () };
nano::election_status status;
bool skip_delay;
bool stopped;
std::unordered_map<nano::block_hash, nano::uint128_t> last_tally;
unsigned confirmation_request_count{ 0 };
std::chrono::steady_clock::time_point last_broadcast;
std::chrono::steady_clock::time_point last_request;
std::unordered_map<nano::block_hash, nano::uint128_t> last_tally;
std::unordered_set<nano::block_hash> dependent_blocks;
std::chrono::seconds late_blocks_delay{ 5 };
};

View file

@ -1766,7 +1766,7 @@ void nano::json_handler::confirmation_active ()
nano::lock_guard<std::mutex> lock (node.active.mutex);
for (auto i (node.active.roots.begin ()), n (node.active.roots.end ()); i != n; ++i)
{
if (i->election->confirmation_request_count >= announcements && !i->election->confirmed () && !i->election->stopped)
if (i->election->confirmation_request_count >= announcements && !i->election->confirmed ())
{
boost::property_tree::ptree entry;
entry.put ("", i->root.to_string ());

View file

@ -549,29 +549,29 @@ 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<nano::node> this_w (shared_from_this ());
if (active.insert (ledger_block, false, [this_w, root](std::shared_ptr<nano::block>) {
if (auto this_l = this_w.lock ())
{
auto attempt (this_l->bootstrap_initiator.current_attempt ());
if (attempt && attempt->mode == nano::bootstrap_mode::legacy)
{
auto transaction (this_l->store.tx_begin_read ());
auto account (this_l->ledger.store.frontier_get (transaction, root));
if (!account.is_zero ())
{
this_l->bootstrap_initiator.connections->requeue_pull (nano::pull_info (account, root, root, attempt->incremental_id));
}
else if (this_l->ledger.store.account_exists (transaction, root))
{
this_l->bootstrap_initiator.connections->requeue_pull (nano::pull_info (root, nano::block_hash (0), nano::block_hash (0), attempt->incremental_id));
}
}
}
})
.first)
auto election = active.insert (ledger_block, [this_w, root](std::shared_ptr<nano::block>) {
if (auto this_l = this_w.lock ())
{
auto attempt (this_l->bootstrap_initiator.current_attempt ());
if (attempt && attempt->mode == nano::bootstrap_mode::legacy)
{
auto transaction (this_l->store.tx_begin_read ());
auto account (this_l->ledger.store.frontier_get (transaction, root));
if (!account.is_zero ())
{
this_l->bootstrap_initiator.connections->requeue_pull (nano::pull_info (account, root, root, attempt->incremental_id));
}
else if (this_l->ledger.store.account_exists (transaction, root))
{
this_l->bootstrap_initiator.connections->requeue_pull (nano::pull_info (root, nano::block_hash (0), nano::block_hash (0), attempt->incremental_id));
}
}
}
});
if (election.second)
{
logger.always_log (boost::str (boost::format ("Resolving fork between our block: %1% and block %2% both with root %3%") % ledger_block->hash ().to_string () % block_a->hash ().to_string () % block_a->root ().to_string ()));
network.broadcast_confirm_req (ledger_block);
election.first->transition_active ();
}
}
}
@ -1101,8 +1101,11 @@ void nano::node::add_initial_peers ()
void nano::node::block_confirm (std::shared_ptr<nano::block> block_a)
{
active.insert (block_a, false);
network.broadcast_confirm_req (block_a);
auto election = active.insert (block_a);
if (election.second)
{
election.first->transition_active ();
}
// Calculate votes for local representatives
if (config.enable_voting && wallets.rep_counts ().voting > 0 && active.active (*block_a))
{

View file

@ -14,12 +14,6 @@ node (node_a)
}
}
void nano::rep_crawler::add (nano::block_hash const & hash_a)
{
nano::lock_guard<std::mutex> lock (active_mutex);
active.insert (hash_a);
}
void nano::rep_crawler::remove (nano::block_hash const & hash_a)
{
nano::lock_guard<std::mutex> lock (active_mutex);

View file

@ -80,9 +80,6 @@ public:
/** Start crawling */
void start ();
/** Add block hash to list of active rep queries */
void add (nano::block_hash const &);
/** Remove block hash from list of active rep queries */
void remove (nano::block_hash const &);