Election encapsulation (#2961)

* Making election time factors unsigned

And with C++17, no need to declare them in the source file

* Making more election members private

* Renaming election_behavior -> behavior

* Using const ref to avoid some copies

* Encapsulating with election::force_confirm

This calls the now private method confirm_once and can only be used in tests.

Simplifies many tests and ensures `confirm_once` isn't called where it shouldn't be.

* Encapsulating last_votes in election::votes()

* Encapsulation status.winner in election::winner()

* Encapsulating last_blocks in election::blocks()

* Removing unused active_transactions::list_blocks()

* Moving election cleanup responsibility to active_transactions::cleanup_election

Election now provides cleanup_info (copies blocks)
This commit is contained in:
Guilherme Lawless 2020-09-22 09:17:48 +01:00 committed by GitHub
commit 1e7be31727
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 464 additions and 663 deletions

View file

@ -55,7 +55,7 @@ TEST (active_transactions, confirm_active)
// At least one confirmation request
ASSERT_GT (election->confirmation_request_count, 0u);
// Blocks were cleared (except for not_an_account)
ASSERT_EQ (1, election->blocks.size ());
ASSERT_EQ (1, election->blocks ().size ());
}
}
@ -122,14 +122,13 @@ TEST (active_transactions, keep_local)
// should not drop wallet created transactions
ASSERT_TIMELY (5s, node.active.size () == 6);
ASSERT_EQ (0, node.active.recently_dropped.size ());
while (!node.active.empty ())
for (auto const & block : { send1, send2, send3, send4, send5, send6 })
{
nano::lock_guard<std::mutex> active_guard (node.active.mutex);
if (!node.active.roots.empty ())
{
node.active.roots.begin ()->election->confirm_once ();
}
auto election = node.active.election (block->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
}
ASSERT_TIMELY (5s, node.active.empty ());
nano::state_block_builder builder;
auto open1 = builder.make_block ()
.account (key1.pub)
@ -260,38 +259,28 @@ TEST (active_transactions, inactive_votes_cache_existing_vote)
node.block_processor.add (open);
node.block_processor.flush ();
ASSERT_TIMELY (5s, node.active.size () == 1);
std::shared_ptr<nano::election> election;
{
nano::lock_guard<std::mutex> active_guard (node.active.mutex);
auto it (node.active.roots.begin ());
ASSERT_NE (node.active.roots.end (), it);
election = it->election;
}
auto election (node.active.election (send->qualified_root ()));
ASSERT_NE (nullptr, election);
ASSERT_GT (node.weight (key.pub), node.minimum_principal_weight ());
// Insert vote
auto vote1 (std::make_shared<nano::vote> (key.pub, key.prv, 1, std::vector<nano::block_hash> (1, send->hash ())));
node.vote_processor.vote (vote1, std::make_shared<nano::transport::channel_udp> (node.network.udp_channels, node.network.endpoint (), node.network_params.protocol.protocol_version));
system.deadline_set (5s);
bool done (false);
while (!done)
{
nano::unique_lock<std::mutex> active_lock (node.active.mutex);
done = (election->last_votes.size () == 2);
active_lock.unlock ();
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_TIMELY (5s, election->votes ().size () == 2)
ASSERT_EQ (1, node.stats.count (nano::stat::type::election, nano::stat::detail::vote_new));
nano::lock_guard<std::mutex> active_guard (node.active.mutex);
auto last_vote1 (election->last_votes[key.pub]);
auto last_vote1 (election->votes ()[key.pub]);
ASSERT_EQ (send->hash (), last_vote1.hash);
ASSERT_EQ (1, last_vote1.sequence);
// Attempt to change vote with inactive_votes_cache
node.active.add_inactive_votes_cache (send->hash (), key.pub);
ASSERT_EQ (1, node.active.find_inactive_votes_cache (send->hash ()).voters.size ());
election->insert_inactive_votes_cache (send->hash ());
{
nano::lock_guard<std::mutex> active_guard (node.active.mutex);
node.active.add_inactive_votes_cache (send->hash (), key.pub);
ASSERT_EQ (1, node.active.find_inactive_votes_cache (send->hash ()).voters.size ());
election->insert_inactive_votes_cache (send->hash ());
}
// Check that election data is not changed
ASSERT_EQ (2, election->last_votes.size ());
auto last_vote2 (election->last_votes[key.pub]);
ASSERT_EQ (2, election->votes ().size ());
auto last_vote2 (election->votes ()[key.pub]);
ASSERT_EQ (last_vote1.hash, last_vote2.hash);
ASSERT_EQ (last_vote1.sequence, last_vote2.sequence);
ASSERT_EQ (last_vote1.time, last_vote2.time);
@ -354,12 +343,8 @@ TEST (active_transactions, inactive_votes_cache_multiple_votes)
ASSERT_EQ (1, node.active.inactive_votes_cache_size ());
// Start election
node.active.insert (send1);
{
nano::lock_guard<std::mutex> active_guard (node.active.mutex);
auto it (node.active.roots.begin ());
ASSERT_NE (node.active.roots.end (), it);
ASSERT_EQ (3, it->election->last_votes.size ()); // 2 votes and 1 default not_an_acount
}
auto election = node.active.insert (send1).election;
ASSERT_EQ (3, election->votes ().size ()); // 2 votes and 1 default not_an_acount
ASSERT_EQ (2, node.stats.count (nano::stat::type::election, nano::stat::detail::vote_cached));
}
@ -725,10 +710,7 @@ TEST (active_transactions, dropped_cleanup)
// Now simulate dropping the election, which performs a cleanup in the background using the node worker
ASSERT_FALSE (election->confirmed ());
{
nano::lock_guard<std::mutex> guard (node.active.mutex);
election->cleanup ();
}
node.active.erase (*block);
// Push a worker task to ensure the cleanup is already performed
std::atomic<bool> flag{ false };
@ -1056,12 +1038,9 @@ TEST (active_transactions, election_difficulty_update_fork)
for (auto block : { open1, send2 })
{
node.block_confirm (block);
{
auto election = node.active.election (block->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node.active.mutex);
election->confirm_once ();
}
auto election = node.active.election (block->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (2s, node.block_confirmed (block->hash ()));
node.active.erase (*block);
}
@ -1324,25 +1303,19 @@ TEST (active_transactions, activate_account_chain)
auto result = node.active.activate (nano::dev_genesis_key.pub);
ASSERT_TRUE (result.inserted);
ASSERT_EQ (1, node.active.size ());
ASSERT_EQ (1, result.election->blocks.count (send->hash ()));
ASSERT_EQ (1, result.election->blocks ().count (send->hash ()));
auto result2 = node.active.activate (nano::dev_genesis_key.pub);
ASSERT_FALSE (result2.inserted);
ASSERT_EQ (result2.election, result.election);
{
nano::lock_guard<std::mutex> guard (node.active.mutex);
result.election->confirm_once ();
}
result.election->force_confirm ();
ASSERT_TIMELY (3s, node.block_confirmed (send->hash ()));
// On cementing, the next election is started
ASSERT_TIMELY (3s, node.active.active (send2->qualified_root ()));
auto result3 = node.active.activate (nano::dev_genesis_key.pub);
ASSERT_FALSE (result3.inserted);
ASSERT_NE (nullptr, result3.election);
ASSERT_EQ (1, result3.election->blocks.count (send2->hash ()));
{
nano::lock_guard<std::mutex> guard (node.active.mutex);
result3.election->confirm_once ();
}
ASSERT_EQ (1, result3.election->blocks ().count (send2->hash ()));
result3.election->force_confirm ();
ASSERT_TIMELY (3s, node.block_confirmed (send2->hash ()));
// On cementing, the next election is started
ASSERT_TIMELY (3s, node.active.active (open->qualified_root ()));
@ -1350,25 +1323,19 @@ TEST (active_transactions, activate_account_chain)
auto result4 = node.active.activate (nano::dev_genesis_key.pub);
ASSERT_FALSE (result4.inserted);
ASSERT_NE (nullptr, result4.election);
ASSERT_EQ (1, result4.election->blocks.count (send3->hash ()));
ASSERT_EQ (1, result4.election->blocks ().count (send3->hash ()));
auto result5 = node.active.activate (key.pub);
ASSERT_FALSE (result5.inserted);
ASSERT_NE (nullptr, result5.election);
ASSERT_EQ (1, result5.election->blocks.count (open->hash ()));
{
nano::lock_guard<std::mutex> guard (node.active.mutex);
result5.election->confirm_once ();
}
ASSERT_EQ (1, result5.election->blocks ().count (open->hash ()));
result5.election->force_confirm ();
ASSERT_TIMELY (3s, node.block_confirmed (open->hash ()));
// Until send3 is also confirmed, the receive block should not activate
std::this_thread::sleep_for (200ms);
auto result6 = node.active.activate (key.pub);
ASSERT_FALSE (result6.inserted);
ASSERT_EQ (nullptr, result6.election);
{
nano::lock_guard<std::mutex> guard (node.active.mutex);
result4.election->confirm_once ();
}
result4.election->force_confirm ();
ASSERT_TIMELY (3s, node.block_confirmed (send3->hash ()));
ASSERT_TIMELY (3s, node.active.active (receive->qualified_root ()));
}
@ -1416,12 +1383,9 @@ TEST (active_transactions, activate_inactive)
ASSERT_EQ (nano::process_result::progress, node.process (*open).code);
node.block_confirm (send2);
{
auto election = node.active.election (send2->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node.active.mutex);
election->confirm_once ();
}
auto election = node.active.election (send2->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (3s, !node.confirmation_height_processor.is_processing_added_block (send2->hash ()));
ASSERT_TRUE (node.block_confirmed (send2->hash ()));
@ -1523,13 +1487,10 @@ TEST (active_transactions, pessimistic_elections)
ASSERT_EQ (2, node.active.expired_optimistic_election_infos.size ());
ASSERT_EQ (node.active.expired_optimistic_election_infos_size, node.active.expired_optimistic_election_infos.size ());
{
ASSERT_EQ (1, node.active.size ());
auto election = node.active.election (send->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node.active.mutex);
election->confirm_once ();
}
ASSERT_EQ (1, node.active.size ());
auto election = node.active.election (send->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (3s, node.block_confirmed (send->hash ()) && !node.confirmation_height_processor.is_processing_added_block (send->hash ()));
@ -1551,12 +1512,9 @@ TEST (active_transactions, pessimistic_elections)
ASSERT_EQ (2, node.active.expired_optimistic_election_infos.size ());
// Confirm it
{
auto election = node.active.election (send2->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node.active.mutex);
election->confirm_once ();
}
election = node.active.election (send2->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (3s, node.block_confirmed (send2->hash ()));
@ -1578,12 +1536,9 @@ TEST (active_transactions, pessimistic_elections)
ASSERT_EQ (2, node.active.expired_optimistic_election_infos.size ());
node.active.confirm_expired_frontiers_pessimistically (node.store.tx_begin_read (), 100, election_count);
{
auto election = node.active.election (open->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node.active.mutex);
election->confirm_once ();
}
election = node.active.election (open->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (3s, node.block_confirmed (open->hash ()));

View file

@ -879,12 +879,9 @@ TEST (bootstrap_processor, bootstrap_fork)
ASSERT_EQ (nano::process_result::progress, node0->process (*send).code);
// Confirm send block to vote later
node0->block_confirm (send);
{
auto election = node0->active.election (send->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node0->active.mutex);
election->confirm_once ();
}
auto election = node0->active.election (send->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (2s, node0->block_confirmed (send->hash ()));
node0->active.erase (*send);
auto open_work (*system.work.generate (key.pub));

View file

@ -151,12 +151,9 @@ TEST (confirmation_height, multiple_accounts)
node->process_active (receive3);
node->block_processor.flush ();
node->block_confirm (receive3);
{
auto election = node->active.election (receive3->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node->active.mutex);
election->confirm_once ();
}
auto election = node->active.election (receive3->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (10s, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out) == 10);
@ -435,12 +432,9 @@ TEST (confirmation_height, send_receive_between_2_accounts)
node->process_active (receive4);
node->block_processor.flush ();
node->block_confirm (receive4);
{
auto election = node->active.election (receive4->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node->active.mutex);
election->confirm_once ();
}
auto election = node->active.election (receive4->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (10s, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out) == 10);
@ -509,12 +503,9 @@ TEST (confirmation_height, send_receive_self)
add_callback_stats (*node);
node->block_confirm (receive3);
{
auto election = node->active.election (receive3->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node->active.mutex);
election->confirm_once ();
}
auto election = node->active.election (receive3->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (10s, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out) == 6);
@ -609,12 +600,9 @@ TEST (confirmation_height, all_block_types)
add_callback_stats (*node);
node->block_confirm (state_send2);
{
auto election = node->active.election (state_send2->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node->active.mutex);
election->confirm_once ();
}
auto election = node->active.election (state_send2->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (10s, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out) == 15);
@ -685,13 +673,9 @@ TEST (confirmation_height, conflict_rollback_cemented)
node1->block_processor.flush ();
node2->network.process_message (publish1, channel2);
node2->block_processor.flush ();
nano::unique_lock<std::mutex> lock (node2->active.mutex);
auto conflict (node2->active.roots.find (nano::qualified_root (genesis.hash (), genesis.hash ())));
ASSERT_NE (node2->active.roots.end (), conflict);
auto votes1 (conflict->election);
ASSERT_NE (nullptr, votes1);
ASSERT_EQ (1, votes1->last_votes.size ());
lock.unlock ();
auto election (node2->active.election (nano::qualified_root (genesis.hash (), genesis.hash ())));
ASSERT_NE (nullptr, election);
ASSERT_EQ (1, election->votes ().size ());
// Force blocks to be cemented on both nodes
{
auto transaction (node1->store.tx_begin_write ());
@ -714,8 +698,8 @@ TEST (confirmation_height, conflict_rollback_cemented)
}
auto transaction1 (node1->store.tx_begin_read ());
auto transaction2 (node2->store.tx_begin_read ());
lock.lock ();
auto winner (*votes1->tally ().begin ());
nano::unique_lock<std::mutex> lock (node2->active.mutex);
auto winner (*election->tally ().begin ());
ASSERT_EQ (*publish1.block, *winner.second);
ASSERT_EQ (nano::genesis_amount - 100, winner.first);
ASSERT_TRUE (node1->store.block_exists (transaction1, publish1.block->hash ()));
@ -1025,12 +1009,9 @@ TEST (confirmation_height, callback_confirmed_history)
auto write_guard = node->write_database_queue.wait (nano::writer::testing);
// Confirm send1
{
auto election = node->active.election (send1->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node->active.mutex);
election->confirm_once ();
}
auto election = node->active.election (send1->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (10s, node->active.size () == 0);
ASSERT_EQ (0, node->active.list_recently_cemented ().size ());
{
@ -1104,12 +1085,9 @@ TEST (confirmation_height, dependent_election)
node->block_confirm (send1);
// Start an election and confirm it
node->block_confirm (send2);
{
auto election = node->active.election (send2->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node->active.mutex);
election->confirm_once ();
}
auto election = node->active.election (send2->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (10s, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out) == 3);
@ -1186,12 +1164,9 @@ TEST (confirmation_height, cemented_gap_below_receive)
add_callback_stats (*node, &observer_order, &mutex);
node->block_confirm (open1);
{
auto election = node->active.election (open1->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node->active.mutex);
election->confirm_once ();
}
auto election = node->active.election (open1->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (10s, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out) == 10);
auto transaction = node->store.tx_begin_read ();
@ -1279,12 +1254,9 @@ TEST (confirmation_height, cemented_gap_below_no_cache)
add_callback_stats (*node);
node->block_confirm (open1);
{
auto election = node->active.election (open1->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node->active.mutex);
election->confirm_once ();
}
auto election = node->active.election (open1->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (10s, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out) == 6);
auto transaction = node->store.tx_begin_read ();
@ -1329,23 +1301,17 @@ TEST (confirmation_height, election_winner_details_clearing)
add_callback_stats (*node);
node->block_confirm (send1);
{
auto election = node->active.election (send1->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node->active.mutex);
election->confirm_once ();
}
auto election = node->active.election (send1->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (10s, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out) == 2);
ASSERT_EQ (0, node->active.election_winner_details_size ());
node->block_confirm (send);
{
auto election = node->active.election (send->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node->active.mutex);
election->confirm_once ();
}
election = node->active.election (send->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
// Wait until this block is confirmed
ASSERT_TIMELY (10s, node->active.election_winner_details_size () == 1 || node->confirmation_height_processor.current ().is_zero ());
@ -1353,12 +1319,9 @@ TEST (confirmation_height, election_winner_details_clearing)
ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::inactive_conf_height, nano::stat::dir::out));
node->block_confirm (send2);
{
auto election = node->active.election (send2->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node->active.mutex);
election->confirm_once ();
}
election = node->active.election (send2->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (10s, node->stats.count (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out) == 3);

View file

@ -51,6 +51,8 @@ TEST (confirmation_solicitor, batches)
ASSERT_EQ (1, node2.stats.count (nano::stat::type::message, nano::stat::detail::confirm_req, nano::stat::dir::out));
}
namespace nano
{
TEST (confirmation_solicitor, different_hash)
{
nano::system system;
@ -73,15 +75,12 @@ TEST (confirmation_solicitor, different_hash)
ASSERT_TIMELY (3s, node2.network.size () == 1);
auto send (std::make_shared<nano::send_block> (nano::genesis_hash, nano::keypair ().pub, nano::genesis_amount - 100, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (nano::genesis_hash)));
send->sideband_set ({});
{
nano::lock_guard<std::mutex> guard (node2.active.mutex);
auto election (std::make_shared<nano::election> (node2, send, nullptr, false, nano::election_behavior::normal));
// Add a vote for something else, not the winner
election->last_votes[representative.account] = { std::chrono::steady_clock::now (), 1, 1 };
// Ensure the request and broadcast goes through
ASSERT_FALSE (solicitor.add (*election));
ASSERT_FALSE (solicitor.broadcast (*election));
}
auto election (std::make_shared<nano::election> (node2, send, nullptr, false, nano::election_behavior::normal));
// Add a vote for something else, not the winner
election->last_votes[representative.account] = { std::chrono::steady_clock::now (), 1, 1 };
// Ensure the request and broadcast goes through
ASSERT_FALSE (solicitor.add (*election));
ASSERT_FALSE (solicitor.broadcast (*election));
// One publish through directed broadcasting and another through random flooding
ASSERT_EQ (2, node2.stats.count (nano::stat::type::message, nano::stat::detail::publish, nano::stat::dir::out));
solicitor.flush ();
@ -112,32 +111,26 @@ TEST (confirmation_solicitor, bypass_max_requests_cap)
ASSERT_TIMELY (3s, node2.network.size () == 1);
auto send (std::make_shared<nano::send_block> (nano::genesis_hash, nano::keypair ().pub, nano::genesis_amount - 100, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (nano::genesis_hash)));
send->sideband_set ({});
auto election (std::make_shared<nano::election> (node2, send, nullptr, false, nano::election_behavior::normal));
// Add a vote for something else, not the winner
for (auto const & rep : representatives)
{
nano::lock_guard<std::mutex> guard (node2.active.mutex);
auto election (std::make_shared<nano::election> (node2, send, nullptr, false, nano::election_behavior::normal));
// Add a vote for something else, not the winner
for (auto const & rep : representatives)
{
election->last_votes[rep.account] = { std::chrono::steady_clock::now (), 1, 1 };
}
ASSERT_FALSE (solicitor.add (*election));
ASSERT_FALSE (solicitor.broadcast (*election));
nano::lock_guard<std::mutex> guard (node1.active.mutex);
election->last_votes[rep.account] = { std::chrono::steady_clock::now (), 1, 1 };
}
ASSERT_FALSE (solicitor.add (*election));
ASSERT_FALSE (solicitor.broadcast (*election));
solicitor.flush ();
// All requests went through, the last one would normally not go through due to the cap but a vote for a different hash does not count towards the cap
ASSERT_EQ (max_representatives + 1, node2.stats.count (nano::stat::type::message, nano::stat::detail::confirm_req, nano::stat::dir::out));
solicitor.prepare (representatives);
{
nano::lock_guard<std::mutex> guard (node2.active.mutex);
auto election (std::make_shared<nano::election> (node2, send, nullptr, false, nano::election_behavior::normal));
// Erase all votes
election->last_votes.clear ();
ASSERT_FALSE (solicitor.add (*election));
ASSERT_FALSE (solicitor.broadcast (*election));
}
auto election2 (std::make_shared<nano::election> (node2, send, nullptr, false, nano::election_behavior::normal));
ASSERT_FALSE (solicitor.add (*election2));
ASSERT_FALSE (solicitor.broadcast (*election2));
solicitor.flush ();
// All requests but one went through, due to the cap
ASSERT_EQ (2 * max_representatives + 1, node2.stats.count (nano::stat::type::message, nano::stat::detail::confirm_req, nano::stat::dir::out));
}
}

View file

@ -20,11 +20,8 @@ TEST (conflicts, start_stop)
ASSERT_EQ (0, node1.active.size ());
auto election1 = node1.active.insert (send1);
ASSERT_EQ (1, node1.active.size ());
{
nano::lock_guard<std::mutex> guard (node1.active.mutex);
ASSERT_NE (nullptr, election1.election);
ASSERT_EQ (1, election1.election->last_votes.size ());
}
ASSERT_NE (nullptr, election1.election);
ASSERT_EQ (1, election1.election->votes ().size ());
}
TEST (conflicts, add_existing)
@ -44,13 +41,10 @@ TEST (conflicts, add_existing)
ASSERT_EQ (1, node1.active.size ());
auto vote1 (std::make_shared<nano::vote> (key2.pub, key2.prv, 0, send2));
node1.active.vote (vote1);
ASSERT_EQ (1, node1.active.size ());
{
nano::lock_guard<std::mutex> guard (node1.active.mutex);
ASSERT_NE (nullptr, election1.election);
ASSERT_EQ (2, election1.election->last_votes.size ());
ASSERT_NE (election1.election->last_votes.end (), election1.election->last_votes.find (key2.pub));
}
ASSERT_NE (nullptr, election1.election);
ASSERT_EQ (2, election1.election->votes ().size ());
auto votes (election1.election->votes ());
ASSERT_NE (votes.end (), votes.find (key2.pub));
}
TEST (conflicts, add_two)

View file

@ -73,12 +73,9 @@ TEST (gap_cache, gap_bootstrap)
ASSERT_EQ (nano::genesis_amount, node2.balance (nano::genesis_account));
// Confirm send block, allowing voting on the upcoming block
node1.block_confirm (send);
{
auto election = node1.active.election (send->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node1.active.mutex);
election->confirm_once ();
}
auto election = node1.active.election (send->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (2s, node1.block_confirmed (send->hash ()));
node1.active.erase (*send);
system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv);

View file

@ -767,10 +767,7 @@ TEST (votes, check_signature)
ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *send1).code);
}
auto election1 = node1.active.insert (send1);
{
nano::lock_guard<std::mutex> lock (node1.active.mutex);
ASSERT_EQ (1, election1.election->last_votes.size ());
}
ASSERT_EQ (1, election1.election->votes ().size ());
auto vote1 (std::make_shared<nano::vote> (nano::dev_genesis_key.pub, nano::dev_genesis_key.prv, 1, send1));
vote1->signature.bytes[0] ^= 1;
ASSERT_EQ (nano::vote_code::invalid, node1.vote_processor.vote_blocking (vote1, std::make_shared<nano::transport::channel_udp> (node1.network.udp_channels, nano::endpoint (boost::asio::ip::address_v6 (), 0), node1.network_params.protocol.protocol_version)));
@ -790,18 +787,17 @@ TEST (votes, add_one)
auto transaction (node1.store.tx_begin_write ());
ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *send1).code);
auto election1 = node1.active.insert (send1);
nano::unique_lock<std::mutex> lock (node1.active.mutex);
ASSERT_EQ (1, election1.election->last_votes.size ());
lock.unlock ();
ASSERT_EQ (1, election1.election->votes ().size ());
auto vote1 (std::make_shared<nano::vote> (nano::dev_genesis_key.pub, nano::dev_genesis_key.prv, 1, send1));
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1));
auto vote2 (std::make_shared<nano::vote> (nano::dev_genesis_key.pub, nano::dev_genesis_key.prv, 2, send1));
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2));
lock.lock ();
ASSERT_EQ (2, election1.election->last_votes.size ());
auto existing1 (election1.election->last_votes.find (nano::dev_genesis_key.pub));
ASSERT_NE (election1.election->last_votes.end (), existing1);
ASSERT_EQ (2, election1.election->votes ().size ());
auto votes1 (election1.election->votes ());
auto existing1 (votes1.find (nano::dev_genesis_key.pub));
ASSERT_NE (votes1.end (), existing1);
ASSERT_EQ (send1->hash (), existing1->second.hash);
nano::lock_guard<std::mutex> guard (node1.active.mutex);
auto winner (*election1.election->tally ().begin ());
ASSERT_EQ (*send1, *winner.second);
ASSERT_EQ (nano::genesis_amount - 100, winner.first);
@ -818,24 +814,23 @@ TEST (votes, add_two)
auto transaction (node1.store.tx_begin_write ());
ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *send1).code);
auto election1 = node1.active.insert (send1);
nano::unique_lock<std::mutex> lock (node1.active.mutex);
lock.unlock ();
nano::keypair key2;
auto send2 (std::make_shared<nano::send_block> (genesis.hash (), key2.pub, 0, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, 0));
auto vote2 (std::make_shared<nano::vote> (key2.pub, key2.prv, 1, send2));
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2));
auto vote1 (std::make_shared<nano::vote> (nano::dev_genesis_key.pub, nano::dev_genesis_key.prv, 1, send1));
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1));
lock.lock ();
ASSERT_EQ (3, election1.election->last_votes.size ());
ASSERT_NE (election1.election->last_votes.end (), election1.election->last_votes.find (nano::dev_genesis_key.pub));
ASSERT_EQ (send1->hash (), election1.election->last_votes[nano::dev_genesis_key.pub].hash);
ASSERT_NE (election1.election->last_votes.end (), election1.election->last_votes.find (key2.pub));
ASSERT_EQ (send2->hash (), election1.election->last_votes[key2.pub].hash);
auto winner (*election1.election->tally ().begin ());
ASSERT_EQ (*send1, *winner.second);
ASSERT_EQ (3, election1.election->votes ().size ());
auto votes1 (election1.election->votes ());
ASSERT_NE (votes1.end (), votes1.find (nano::dev_genesis_key.pub));
ASSERT_EQ (send1->hash (), votes1[nano::dev_genesis_key.pub].hash);
ASSERT_NE (votes1.end (), votes1.find (key2.pub));
ASSERT_EQ (send2->hash (), votes1[key2.pub].hash);
ASSERT_EQ (*send1, *election1.election->winner ());
}
namespace nano
{
// Higher sequence numbers change the vote
TEST (votes, add_existing)
{
@ -857,33 +852,30 @@ TEST (votes, add_existing)
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1));
// Block is already processed from vote
ASSERT_TRUE (node1.active.publish (send1));
nano::unique_lock<std::mutex> lock (node1.active.mutex);
ASSERT_EQ (1, election1.election->last_votes[nano::dev_genesis_key.pub].sequence);
ASSERT_EQ (1, election1.election->votes ()[nano::dev_genesis_key.pub].sequence);
nano::keypair key2;
auto send2 (std::make_shared<nano::send_block> (genesis.hash (), key2.pub, nano::genesis_amount - nano::Gxrb_ratio, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, 0));
node1.work_generate_blocking (*send2);
auto vote2 (std::make_shared<nano::vote> (nano::dev_genesis_key.pub, nano::dev_genesis_key.prv, 2, send2));
// Pretend we've waited the timeout
nano::unique_lock<std::mutex> lock (node1.active.mutex);
election1.election->last_votes[nano::dev_genesis_key.pub].time = std::chrono::steady_clock::now () - std::chrono::seconds (20);
lock.unlock ();
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2));
ASSERT_FALSE (node1.active.publish (send2));
lock.lock ();
ASSERT_EQ (2, election1.election->last_votes[nano::dev_genesis_key.pub].sequence);
ASSERT_EQ (2, election1.election->votes ()[nano::dev_genesis_key.pub].sequence);
// Also resend the old vote, and see if we respect the sequence number
lock.lock ();
election1.election->last_votes[nano::dev_genesis_key.pub].time = std::chrono::steady_clock::now () - std::chrono::seconds (20);
lock.unlock ();
ASSERT_EQ (nano::vote_code::replay, node1.active.vote (vote1));
ASSERT_EQ (2, election1.election->votes ()[nano::dev_genesis_key.pub].sequence);
auto votes (election1.election->votes ());
ASSERT_EQ (2, votes.size ());
ASSERT_NE (votes.end (), votes.find (nano::dev_genesis_key.pub));
ASSERT_EQ (send2->hash (), votes[nano::dev_genesis_key.pub].hash);
lock.lock ();
ASSERT_EQ (2, election1.election->last_votes[nano::dev_genesis_key.pub].sequence);
ASSERT_EQ (2, election1.election->last_votes.size ());
ASSERT_NE (election1.election->last_votes.end (), election1.election->last_votes.find (nano::dev_genesis_key.pub));
ASSERT_EQ (send2->hash (), election1.election->last_votes[nano::dev_genesis_key.pub].hash);
{
auto transaction (node1.store.tx_begin_read ());
auto winner (*election1.election->tally ().begin ());
ASSERT_EQ (*send2, *winner.second);
}
ASSERT_EQ (*send2, *election1.election->tally ().begin ()->second);
}
// Lower sequence numbers are ignored
@ -910,12 +902,12 @@ TEST (votes, add_old)
election1.election->last_votes[nano::dev_genesis_key.pub].time = std::chrono::steady_clock::now () - std::chrono::seconds (20);
}
node1.vote_processor.vote_blocking (vote2, channel);
ASSERT_EQ (2, election1.election->last_votes_size ());
nano::lock_guard<std::mutex> lock (node1.active.mutex);
ASSERT_NE (election1.election->last_votes.end (), election1.election->last_votes.find (nano::dev_genesis_key.pub));
ASSERT_EQ (send1->hash (), election1.election->last_votes[nano::dev_genesis_key.pub].hash);
auto winner (*election1.election->tally ().begin ());
ASSERT_EQ (*send1, *winner.second);
ASSERT_EQ (2, election1.election->votes ().size ());
auto votes (election1.election->votes ());
ASSERT_NE (votes.end (), votes.find (nano::dev_genesis_key.pub));
ASSERT_EQ (send1->hash (), votes[nano::dev_genesis_key.pub].hash);
ASSERT_EQ (*send1, *election1.election->winner ());
}
}
// Lower sequence numbers are accepted for different accounts
@ -936,28 +928,27 @@ TEST (votes, add_old_different_account)
ASSERT_NE (nullptr, election1);
auto election2 = node1.active.election (send2->qualified_root ());
ASSERT_NE (nullptr, election2);
ASSERT_EQ (1, election1->last_votes_size ());
ASSERT_EQ (1, election2->last_votes_size ());
ASSERT_EQ (1, election1->votes ().size ());
ASSERT_EQ (1, election2->votes ().size ());
auto vote1 (std::make_shared<nano::vote> (nano::dev_genesis_key.pub, nano::dev_genesis_key.prv, 2, send1));
auto channel (std::make_shared<nano::transport::channel_udp> (node1.network.udp_channels, node1.network.endpoint (), node1.network_params.protocol.protocol_version));
auto vote_result1 (node1.vote_processor.vote_blocking (vote1, channel));
ASSERT_EQ (nano::vote_code::vote, vote_result1);
ASSERT_EQ (2, election1->last_votes_size ());
ASSERT_EQ (1, election2->last_votes_size ());
ASSERT_EQ (2, election1->votes ().size ());
ASSERT_EQ (1, election2->votes ().size ());
auto vote2 (std::make_shared<nano::vote> (nano::dev_genesis_key.pub, nano::dev_genesis_key.prv, 1, send2));
auto vote_result2 (node1.vote_processor.vote_blocking (vote2, channel));
ASSERT_EQ (nano::vote_code::vote, vote_result2);
ASSERT_EQ (2, election1->last_votes_size ());
ASSERT_EQ (2, election2->last_votes_size ());
nano::unique_lock<std::mutex> lock (node1.active.mutex);
ASSERT_NE (election1->last_votes.end (), election1->last_votes.find (nano::dev_genesis_key.pub));
ASSERT_NE (election2->last_votes.end (), election2->last_votes.find (nano::dev_genesis_key.pub));
ASSERT_EQ (send1->hash (), election1->last_votes[nano::dev_genesis_key.pub].hash);
ASSERT_EQ (send2->hash (), election2->last_votes[nano::dev_genesis_key.pub].hash);
auto winner1 (*election1->tally ().begin ());
ASSERT_EQ (*send1, *winner1.second);
auto winner2 (*election2->tally ().begin ());
ASSERT_EQ (*send2, *winner2.second);
ASSERT_EQ (2, election1->votes ().size ());
ASSERT_EQ (2, election2->votes ().size ());
auto votes1 (election1->votes ());
auto votes2 (election2->votes ());
ASSERT_NE (votes1.end (), votes1.find (nano::dev_genesis_key.pub));
ASSERT_NE (votes2.end (), votes2.find (nano::dev_genesis_key.pub));
ASSERT_EQ (send1->hash (), votes1[nano::dev_genesis_key.pub].hash);
ASSERT_EQ (send2->hash (), votes2[nano::dev_genesis_key.pub].hash);
ASSERT_EQ (*send1, *election1->winner ());
ASSERT_EQ (*send2, *election2->winner ());
}
// The voting cooldown is respected
@ -980,12 +971,11 @@ TEST (votes, add_cooldown)
node1.work_generate_blocking (*send2);
auto vote2 (std::make_shared<nano::vote> (nano::dev_genesis_key.pub, nano::dev_genesis_key.prv, 2, send2));
node1.vote_processor.vote_blocking (vote2, channel);
nano::unique_lock<std::mutex> lock (node1.active.mutex);
ASSERT_EQ (2, election1.election->last_votes.size ());
ASSERT_NE (election1.election->last_votes.end (), election1.election->last_votes.find (nano::dev_genesis_key.pub));
ASSERT_EQ (send1->hash (), election1.election->last_votes[nano::dev_genesis_key.pub].hash);
auto winner (*election1.election->tally ().begin ());
ASSERT_EQ (*send1, *winner.second);
ASSERT_EQ (2, election1.election->votes ().size ());
auto votes (election1.election->votes ());
ASSERT_NE (votes.end (), votes.find (nano::dev_genesis_key.pub));
ASSERT_EQ (send1->hash (), votes[nano::dev_genesis_key.pub].hash);
ASSERT_EQ (*send1, *election1.election->winner ());
}
// Query for block successor
@ -2718,15 +2708,14 @@ TEST (ledger, block_hash_account_conflict)
ASSERT_NE (nullptr, election3);
auto election4 = node1.active.election (open_epoch1->qualified_root ());
ASSERT_NE (nullptr, election4);
nano::lock_guard<std::mutex> lock (node1.active.mutex);
auto winner1 (*election1->tally ().begin ());
auto winner2 (*election2->tally ().begin ());
auto winner3 (*election3->tally ().begin ());
auto winner4 (*election4->tally ().begin ());
ASSERT_EQ (*send1, *winner1.second);
ASSERT_EQ (*receive1, *winner2.second);
ASSERT_EQ (*send2, *winner3.second);
ASSERT_EQ (*open_epoch1, *winner4.second);
auto winner1 (election1->winner ());
auto winner2 (election2->winner ());
auto winner3 (election3->winner ());
auto winner4 (election4->winner ());
ASSERT_EQ (*send1, *winner1);
ASSERT_EQ (*receive1, *winner2);
ASSERT_EQ (*send2, *winner3);
ASSERT_EQ (*open_epoch1, *winner4);
}
TEST (ledger, could_fit)

View file

@ -256,13 +256,10 @@ TEST (node, node_receive_quorum)
.build_shared ();
node1.process_active (send);
ASSERT_TIMELY (10s, node1.ledger.block_exists (send->hash ()));
{
nano::lock_guard<std::mutex> guard (node1.active.mutex);
auto info (node1.active.roots.find (nano::qualified_root (previous, previous)));
ASSERT_NE (node1.active.roots.end (), info);
ASSERT_FALSE (info->election->confirmed ());
ASSERT_EQ (1, info->election->last_votes.size ());
}
auto election (node1.active.election (nano::qualified_root (previous, previous)));
ASSERT_NE (nullptr, election);
ASSERT_FALSE (election->confirmed ());
ASSERT_EQ (1, election->votes ().size ());
nano::system system2;
system2.add_node (node_flags);
@ -1023,25 +1020,17 @@ TEST (node, fork_publish)
node1.process_active (send1);
node1.block_processor.flush ();
ASSERT_EQ (1, node1.active.size ());
nano::unique_lock<std::mutex> lock (node1.active.mutex);
auto existing (node1.active.roots.find (send1->qualified_root ()));
ASSERT_NE (node1.active.roots.end (), existing);
auto election (existing->election);
lock.unlock ();
system.deadline_set (1s);
auto election (node1.active.election (send1->qualified_root ()));
ASSERT_NE (nullptr, election);
// Wait until the genesis rep activated & makes vote
while (election->last_votes_size () != 2)
{
node1.vote_processor.flush ();
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_TIMELY (1s, election->votes ().size () == 2);
node1.process_active (send2);
node1.block_processor.flush ();
lock.lock ();
auto existing1 (election->last_votes.find (nano::dev_genesis_key.pub));
ASSERT_NE (election->last_votes.end (), existing1);
auto votes1 (election->votes ());
auto existing1 (votes1.find (nano::dev_genesis_key.pub));
ASSERT_NE (votes1.end (), existing1);
ASSERT_EQ (send1->hash (), existing1->second.hash);
auto transaction (node1.store.tx_begin_read ());
nano::lock_guard<std::mutex> guard (node1.active.mutex);
auto winner (*election->tally ().begin ());
ASSERT_EQ (*send1, *winner.second);
ASSERT_EQ (nano::genesis_amount - 100, winner.first);
@ -1076,14 +1065,11 @@ TEST (node, fork_publish_inactive)
ASSERT_EQ (nano::process_result::fork, node.process_local (send2).code);
auto election (node.active.election (send1->qualified_root ()));
ASSERT_NE (election, nullptr);
{
nano::lock_guard<std::mutex> guard (node.active.mutex);
auto & blocks (election->blocks);
ASSERT_NE (blocks.end (), blocks.find (send1->hash ()));
ASSERT_NE (blocks.end (), blocks.find (send2->hash ()));
ASSERT_NE (election->status.winner, send1);
ASSERT_NE (election->status.winner, send2);
}
auto blocks (election->blocks ());
ASSERT_NE (blocks.end (), blocks.find (send1->hash ()));
ASSERT_NE (blocks.end (), blocks.find (send2->hash ()));
ASSERT_NE (election->winner (), send1);
ASSERT_NE (election->winner (), send2);
}
TEST (node, fork_keep)
@ -1122,26 +1108,18 @@ TEST (node, fork_keep)
node1.block_processor.flush ();
node2.process_active (send2);
node2.block_processor.flush ();
nano::unique_lock<std::mutex> lock (node2.active.mutex);
auto conflict (node2.active.roots.find (nano::qualified_root (genesis.hash (), genesis.hash ())));
ASSERT_NE (node2.active.roots.end (), conflict);
auto votes1 (conflict->election);
ASSERT_NE (nullptr, votes1);
ASSERT_EQ (1, votes1->last_votes.size ());
lock.unlock ();
{
auto transaction0 (node1.store.tx_begin_read ());
auto transaction1 (node2.store.tx_begin_read ());
ASSERT_TRUE (node1.store.block_exists (transaction0, send1->hash ()));
ASSERT_TRUE (node2.store.block_exists (transaction1, send1->hash ()));
}
auto election1 (node2.active.election (nano::qualified_root (genesis.hash (), genesis.hash ())));
ASSERT_NE (nullptr, election1);
ASSERT_EQ (1, election1->votes ().size ());
ASSERT_TRUE (node1.ledger.block_exists (send1->hash ()));
ASSERT_TRUE (node2.ledger.block_exists (send1->hash ()));
// Wait until the genesis rep makes a vote
ASSERT_TIMELY (1.5min, votes1->last_votes_size () != 1);
ASSERT_TIMELY (1.5min, election1->votes ().size () != 1);
auto transaction0 (node1.store.tx_begin_read ());
auto transaction1 (node2.store.tx_begin_read ());
// The vote should be in agreement with what we already have.
lock.lock ();
auto winner (*votes1->tally ().begin ());
nano::lock_guard<std::mutex> guard (node2.active.mutex);
auto winner (*election1->tally ().begin ());
ASSERT_EQ (*send1, *winner.second);
ASSERT_EQ (nano::genesis_amount - 100, winner.first);
ASSERT_TRUE (node1.store.block_exists (transaction0, send1->hash ()));
@ -1187,37 +1165,19 @@ TEST (node, fork_flip)
node1.block_processor.flush ();
node2.network.process_message (publish1, channel2);
node2.block_processor.flush ();
auto election1 (node2.active.election (nano::qualified_root (genesis.hash (), genesis.hash ())));
ASSERT_NE (nullptr, election1);
ASSERT_EQ (1, election1->votes ().size ());
ASSERT_NE (nullptr, node1.block (publish1.block->hash ()));
ASSERT_NE (nullptr, node2.block (publish2.block->hash ()));
ASSERT_TIMELY (10s, node2.ledger.block_exists (publish1.block->hash ()));
nano::unique_lock<std::mutex> lock (node2.active.mutex);
auto conflict (node2.active.roots.find (nano::qualified_root (genesis.hash (), genesis.hash ())));
ASSERT_NE (node2.active.roots.end (), conflict);
auto votes1 (conflict->election);
ASSERT_NE (nullptr, votes1);
ASSERT_EQ (1, votes1->last_votes.size ());
lock.unlock ();
{
auto transaction (node1.store.tx_begin_read ());
ASSERT_TRUE (node1.store.block_exists (transaction, publish1.block->hash ()));
}
{
auto transaction (node2.store.tx_begin_read ());
ASSERT_TRUE (node2.store.block_exists (transaction, publish2.block->hash ()));
}
system.deadline_set (10s);
auto done (false);
while (!done)
{
ASSERT_NO_ERROR (system.poll ());
done = node2.ledger.block_exists (publish1.block->hash ());
}
auto transaction1 (node1.store.tx_begin_read ());
auto transaction2 (node2.store.tx_begin_read ());
lock.lock ();
auto winner (*votes1->tally ().begin ());
auto winner (*election1->tally ().begin ());
ASSERT_EQ (*publish1.block, *winner.second);
ASSERT_EQ (nano::genesis_amount - 100, winner.first);
ASSERT_TRUE (node1.store.block_exists (transaction1, publish1.block->hash ()));
ASSERT_TRUE (node2.store.block_exists (transaction2, publish1.block->hash ()));
ASSERT_FALSE (node2.store.block_exists (transaction2, publish2.block->hash ()));
ASSERT_TRUE (node1.ledger.block_exists (publish1.block->hash ()));
ASSERT_TRUE (node2.ledger.block_exists (publish1.block->hash ()));
ASSERT_FALSE (node2.ledger.block_exists (publish2.block->hash ()));
}
TEST (node, fork_multi_flip)
@ -1280,39 +1240,21 @@ TEST (node, fork_multi_flip)
node1.block_processor.flush ();
node2.network.process_message (publish1, node2.network.udp_channels.create (node2.network.endpoint ()));
node2.block_processor.flush ();
auto election1 (node2.active.election (nano::qualified_root (genesis.hash (), genesis.hash ())));
ASSERT_NE (nullptr, election1);
ASSERT_EQ (1, election1->votes ().size ());
ASSERT_TRUE (node1.ledger.block_exists (publish1.block->hash ()));
ASSERT_TRUE (node2.ledger.block_exists (publish2.block->hash ()));
ASSERT_TRUE (node2.ledger.block_exists (publish3.block->hash ()));
ASSERT_TIMELY (10s, node2.ledger.block_exists (publish1.block->hash ()));
nano::unique_lock<std::mutex> lock (node2.active.mutex);
auto conflict (node2.active.roots.find (nano::qualified_root (genesis.hash (), genesis.hash ())));
ASSERT_NE (node2.active.roots.end (), conflict);
auto votes1 (conflict->election);
ASSERT_NE (nullptr, votes1);
ASSERT_EQ (1, votes1->last_votes.size ());
lock.unlock ();
{
auto transaction (node1.store.tx_begin_read ());
ASSERT_TRUE (node1.store.block_exists (transaction, publish1.block->hash ()));
}
{
auto transaction (node2.store.tx_begin_read ());
ASSERT_TRUE (node2.store.block_exists (transaction, publish2.block->hash ()));
ASSERT_TRUE (node2.store.block_exists (transaction, publish3.block->hash ()));
}
system.deadline_set (10s);
auto done (false);
while (!done)
{
ASSERT_NO_ERROR (system.poll ());
done = node2.ledger.block_exists (publish1.block->hash ());
}
auto transaction1 (node1.store.tx_begin_read ());
auto transaction2 (node2.store.tx_begin_read ());
lock.lock ();
auto winner (*votes1->tally ().begin ());
auto winner (*election1->tally ().begin ());
ASSERT_EQ (*publish1.block, *winner.second);
ASSERT_EQ (nano::genesis_amount - 100, winner.first);
ASSERT_TRUE (node1.store.block_exists (transaction1, publish1.block->hash ()));
ASSERT_TRUE (node2.store.block_exists (transaction2, publish1.block->hash ()));
ASSERT_FALSE (node2.store.block_exists (transaction2, publish2.block->hash ()));
ASSERT_FALSE (node2.store.block_exists (transaction2, publish3.block->hash ()));
ASSERT_TRUE (node1.ledger.block_exists (publish1.block->hash ()));
ASSERT_TRUE (node2.ledger.block_exists (publish1.block->hash ()));
ASSERT_FALSE (node2.ledger.block_exists (publish2.block->hash ()));
ASSERT_FALSE (node2.ledger.block_exists (publish3.block->hash ()));
}
}
@ -1397,11 +1339,8 @@ TEST (node, fork_open)
auto channel1 (node1.network.udp_channels.create (node1.network.endpoint ()));
node1.network.process_message (publish1, channel1);
node1.block_processor.flush ();
{
auto election = node1.active.election (publish1.block->qualified_root ());
nano::lock_guard<std::mutex> guard (node1.active.mutex);
election->confirm_once ();
}
auto election = node1.active.election (publish1.block->qualified_root ());
election->force_confirm ();
ASSERT_TIMELY (3s, node1.active.empty () && node1.block_confirmed (publish1.block->hash ()));
nano::open_block_builder builder;
auto open1 = builder.make_block ()
@ -1426,13 +1365,10 @@ TEST (node, fork_open)
system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv);
node1.network.process_message (publish3, channel1);
node1.block_processor.flush ();
{
auto election = node1.active.election (publish3.block->qualified_root ());
nano::lock_guard<std::mutex> guard (node1.active.mutex);
ASSERT_EQ (2, election->blocks.size ());
ASSERT_EQ (publish2.block->hash (), election->status.winner->hash ());
ASSERT_FALSE (election->confirmed ());
}
election = node1.active.election (publish3.block->qualified_root ());
ASSERT_EQ (2, election->blocks ().size ());
ASSERT_EQ (publish2.block->hash (), election->winner ()->hash ());
ASSERT_FALSE (election->confirmed ());
ASSERT_TRUE (node1.block (publish2.block->hash ()));
ASSERT_FALSE (node1.block (publish3.block->hash ()));
}
@ -1492,13 +1428,9 @@ TEST (node, fork_open_flip)
node1.block_processor.flush ();
node2.process_active (open1);
node2.block_processor.flush ();
nano::unique_lock<std::mutex> lock (node2.active.mutex);
auto conflict (node2.active.roots.find (open1->qualified_root ()));
ASSERT_NE (node2.active.roots.end (), conflict);
auto votes1 (conflict->election);
ASSERT_NE (nullptr, votes1);
ASSERT_EQ (1, votes1->last_votes.size ());
lock.unlock ();
auto election1 (node2.active.election (open1->qualified_root ()));
ASSERT_NE (nullptr, election1);
ASSERT_EQ (1, election1->votes ().size ());
ASSERT_TRUE (node1.block (open1->hash ()) != nullptr);
ASSERT_TRUE (node2.block (open2->hash ()) != nullptr);
// Node2 should eventually settle on open1
@ -1506,8 +1438,8 @@ TEST (node, fork_open_flip)
node2.block_processor.flush ();
auto transaction1 (node1.store.tx_begin_read ());
auto transaction2 (node2.store.tx_begin_read ());
lock.lock ();
auto winner (*votes1->tally ().begin ());
nano::lock_guard<std::mutex> guard (node1.active.mutex);
auto winner (*election1->tally ().begin ());
ASSERT_EQ (*open1, *winner.second);
ASSERT_EQ (nano::genesis_amount - 1, winner.first);
ASSERT_TRUE (node1.store.block_exists (transaction1, open1->hash ()));
@ -1810,10 +1742,7 @@ TEST (node, broadcast_elected)
node->block_confirm (block);
auto election (node->active.election (block->qualified_root ()));
ASSERT_NE (nullptr, election);
{
nano::lock_guard<std::mutex> guard (node->active.mutex);
election->confirm_once ();
}
election->force_confirm ();
ASSERT_TIMELY (5s, 4 == node->ledger.cache.cemented_count)
}
@ -1880,12 +1809,9 @@ TEST (node, rep_self_vote)
ASSERT_EQ (nano::process_result::progress, node0->process (open_big).code);
// Confirm both blocks, allowing voting on the upcoming block
node0->block_confirm (node0->block (open_big.hash ()));
{
auto election = node0->active.election (open_big.qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node0->active.mutex);
election->confirm_once ();
}
auto election = node0->active.election (open_big.qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
system.wallet (0)->insert_adhoc (rep_big.prv);
system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv);
@ -1901,9 +1827,8 @@ TEST (node, rep_self_vote)
auto & active (node0->active);
auto election1 = active.insert (block0);
// Wait until representatives are activated & make vote
ASSERT_TIMELY (1s, election1.election->last_votes_size () == 3);
nano::unique_lock<std::mutex> lock (active.mutex);
auto & rep_votes (election1.election->last_votes);
ASSERT_TIMELY (1s, election1.election->votes ().size () == 3);
auto rep_votes (election1.election->votes ());
ASSERT_NE (rep_votes.end (), rep_votes.find (nano::dev_genesis_key.pub));
ASSERT_NE (rep_votes.end (), rep_votes.find (rep_big.pub));
}
@ -2012,12 +1937,9 @@ TEST (node, bootstrap_fork_open)
for (auto node : system.nodes)
{
node->block_confirm (node->block (node->latest (nano::dev_genesis_key.pub)));
{
auto election = node->active.election (send0.qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node->active.mutex);
election->confirm_once ();
}
auto election = node->active.election (send0.qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (2s, node->active.empty ());
}
ASSERT_TIMELY (3s, node0->block_confirmed (send0.hash ()));
@ -2541,12 +2463,9 @@ TEST (node, block_confirm)
ASSERT_TRUE (node2.ledger.block_exists (send1_copy->hash ()));
// Confirm send1 on node2 so it can vote for send2
node2.block_confirm (send1_copy);
{
auto election = node2.active.election (send1_copy->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node2.active.mutex);
election->confirm_once ();
}
auto election = node2.active.election (send1_copy->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (3s, node2.block_confirmed (send1_copy->hash ()) && node2.active.empty ());
system.wallet (1)->insert_adhoc (nano::dev_genesis_key.prv);
auto send2 (std::make_shared<nano::state_block> (nano::dev_genesis_key.pub, send1->hash (), nano::dev_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio * 2, key.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *node1.work_generate_blocking (send1->hash ())));
@ -2631,11 +2550,10 @@ TEST (node, confirm_quorum)
ASSERT_EQ (nano::process_result::progress, node1.process (*send1).code);
system.wallet (0)->send_action (nano::dev_genesis_key.pub, nano::dev_genesis_key.pub, new_balance.number ());
ASSERT_TIMELY (10s, !node1.active.empty ());
nano::lock_guard<std::mutex> guard (node1.active.mutex);
auto info (node1.active.roots.find (nano::qualified_root (send1->hash (), send1->hash ())));
ASSERT_NE (node1.active.roots.end (), info);
ASSERT_FALSE (info->election->confirmed ());
ASSERT_EQ (1, info->election->last_votes.size ());
auto election (node1.active.election (nano::qualified_root (send1->hash (), send1->hash ())));
ASSERT_NE (nullptr, election);
ASSERT_FALSE (election->confirmed ());
ASSERT_EQ (1, election->votes ().size ());
ASSERT_EQ (0, node1.balance (nano::dev_genesis_key.pub));
}
@ -2682,12 +2600,9 @@ TEST (node, local_votes_cache)
}
// Confirm blocks to allow voting
node.block_confirm (send2);
{
auto election = node.active.election (send2->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node.active.mutex);
election->confirm_once ();
}
auto election = node.active.election (send2->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (3s, node.ledger.cache.cemented_count == 3);
system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv);
nano::confirm_req message1 (send1);
@ -3066,12 +2981,9 @@ TEST (node, epoch_conflict_confirm)
ASSERT_EQ (nano::process_result::progress, node1->process (*open).code);
// Confirm block in node1 to allow generating votes
node1->block_confirm (open);
{
auto election (node1->active.election (open->qualified_root ()));
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node1->active.mutex);
election->confirm_once ();
}
auto election (node1->active.election (open->qualified_root ()));
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (3s, node1->block_confirmed (open->hash ()));
{
nano::block_post_events events;
@ -3181,31 +3093,14 @@ TEST (node, fork_election_invalid_block_signature)
.build_shared ();
auto channel1 (node1.network.udp_channels.create (node1.network.endpoint ()));
node1.network.process_message (nano::publish (send1), channel1);
system.deadline_set (5s);
std::shared_ptr<nano::election> election;
while (election == nullptr)
{
ASSERT_NO_ERROR (system.poll ());
nano::lock_guard<std::mutex> lock (node1.active.mutex);
auto existing = node1.active.blocks.find (send1->hash ());
if (existing != node1.active.blocks.end ())
{
election = existing->second;
}
}
nano::unique_lock<std::mutex> lock (node1.active.mutex);
ASSERT_EQ (1, election->blocks.size ());
lock.unlock ();
ASSERT_TIMELY (5s, node1.active.active (send1->qualified_root ()));
auto election (node1.active.election (send1->qualified_root ()));
ASSERT_NE (nullptr, election);
ASSERT_EQ (1, election->blocks ().size ());
node1.network.process_message (nano::publish (send3), channel1);
node1.network.process_message (nano::publish (send2), channel1);
lock.lock ();
while (election->blocks.size () == 1)
{
lock.unlock ();
ASSERT_NO_ERROR (system.poll ());
lock.lock ();
}
ASSERT_EQ (election->blocks[send2->hash ()]->block_signature (), send2->block_signature ());
ASSERT_TIMELY (3s, election->blocks ().size () > 1);
ASSERT_EQ (election->blocks ()[send2->hash ()]->block_signature (), send2->block_signature ());
}
TEST (node, block_processor_signatures)
@ -3977,33 +3872,31 @@ TEST (node, rollback_vote_self)
ASSERT_EQ (nano::process_result::progress, node.process (*open).code);
// Confirm blocks to allow voting
node.block_confirm (open);
{
auto election = node.active.election (open->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node.active.mutex);
election->confirm_once ();
}
auto election = node.active.election (open->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (5s, node.ledger.cache.cemented_count == 3);
ASSERT_EQ (weight, node.weight (key.pub));
node.process_active (send2);
node.process_active (fork);
node.block_processor.flush ();
auto election = node.active.election (send2->qualified_root ());
election = node.active.election (send2->qualified_root ());
ASSERT_NE (nullptr, election);
ASSERT_EQ (2, election->blocks.size ());
ASSERT_EQ (2, election->blocks ().size ());
// Insert genesis key in the wallet
system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv);
{
// The write guard prevents the block processor from performing the rollback
auto write_guard = node.write_database_queue.wait (nano::writer::testing);
{
nano::lock_guard<std::mutex> guard (node.active.mutex);
ASSERT_EQ (1, election->last_votes.size ());
ASSERT_EQ (1, election->votes ().size ());
nano::unique_lock<std::mutex> lock (node.active.mutex);
// Vote with key to switch the winner
election->vote (key.pub, 0, fork->hash ());
ASSERT_EQ (2, election->last_votes.size ());
lock.unlock ();
ASSERT_EQ (2, election->votes ().size ());
// The winner changed
ASSERT_EQ (election->status.winner, fork);
ASSERT_EQ (election->winner (), fork);
}
// Even without the rollback being finished, the aggregator must reply with a vote for the new winner, not the old one
ASSERT_TRUE (node.history.votes (send2->root (), send2->hash ()).empty ());
@ -4017,9 +3910,10 @@ TEST (node, rollback_vote_self)
// Going out of the scope allows the rollback to complete
}
// A vote is eventually generated from the local representative
ASSERT_TIMELY (5s, 3 == election->last_votes_size ());
auto vote (election->last_votes.find (nano::dev_genesis_key.pub));
ASSERT_NE (election->last_votes.end (), vote);
ASSERT_TIMELY (5s, 3 == election->votes ().size ());
auto votes (election->votes ());
auto vote (votes.find (nano::dev_genesis_key.pub));
ASSERT_NE (votes.end (), vote);
ASSERT_EQ (fork->hash (), vote->second.hash);
}
@ -4488,10 +4382,7 @@ TEST (node, deferred_dependent_elections)
ASSERT_FALSE (node.active.active (send2->qualified_root ()));
// Confirming send1 will automatically start elections for the dependents
{
nano::lock_guard<std::mutex> guard (node.active.mutex);
election_send1->confirm_once ();
}
election_send1->force_confirm ();
ASSERT_TIMELY (2s, node.block_confirmed (send1->hash ()));
ASSERT_TIMELY (2s, node.active.active (open->qualified_root ()) && node.active.active (send2->qualified_root ()));
auto election_open = node.active.election (open->qualified_root ());
@ -4502,10 +4393,7 @@ TEST (node, deferred_dependent_elections)
// Confirm one of the dependents of the receive but not the other, to ensure both have to be confirmed to start an election on processing
ASSERT_EQ (nano::process_result::progress, node.process (*receive).code);
ASSERT_FALSE (node.active.active (receive->qualified_root ()));
{
nano::lock_guard<std::mutex> guard (node.active.mutex);
election_open->confirm_once ();
}
election_open->force_confirm ();
ASSERT_TIMELY (2s, node.block_confirmed (open->hash ()));
ASSERT_FALSE (node.ledger.dependents_confirmed (node.store.tx_begin_read (), *receive));
std::this_thread::sleep_for (500ms);
@ -4524,10 +4412,7 @@ TEST (node, deferred_dependent_elections)
ASSERT_FALSE (node.active.active (receive->qualified_root ()));
// Confirming the other dependency allows starting an election from a fork
{
nano::lock_guard<std::mutex> guard (node.active.mutex);
election_send2->confirm_once ();
}
election_send2->force_confirm ();
ASSERT_TIMELY (2s, node.block_confirmed (send2->hash ()));
ASSERT_TIMELY (2s, node.active.active (receive->qualified_root ()));
node.active.erase (*receive);

View file

@ -185,12 +185,9 @@ TEST (request_aggregator, split)
}
// Confirm all blocks
node.block_confirm (blocks.back ());
{
auto election (node.active.election (blocks.back ()->qualified_root ()));
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node.active.mutex);
election->confirm_once ();
}
auto election (node.active.election (blocks.back ()->qualified_root ()));
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (5s, max_vbh + 2 == node.ledger.cache.cemented_count);
ASSERT_EQ (max_vbh + 1, request.size ());
auto channel (node.network.udp_channels.create (node.network.endpoint ()));
@ -364,12 +361,9 @@ TEST (request_aggregator, cannot_vote)
// Confirm send1
node.block_confirm (send1);
{
auto election (node.active.election (send1->qualified_root ()));
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node.active.mutex);
election->confirm_once ();
}
auto election (node.active.election (send1->qualified_root ()));
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (3s, node.ledger.dependents_confirmed (node.store.tx_begin_read (), *send2));
node.aggregator.add (channel, request);
ASSERT_EQ (1, node.aggregator.size ());

View file

@ -80,13 +80,13 @@ TEST (vote_processor, invalid_signature)
genesis.open->sideband_set (nano::block_sideband (nano::genesis_account, 0, nano::genesis_amount, 1, nano::seconds_since_epoch (), nano::epoch::epoch_0, false, false, false, nano::epoch::epoch_0));
auto election (node.active.insert (genesis.open));
ASSERT_TRUE (election.election && election.inserted);
ASSERT_EQ (1, election.election->last_votes.size ());
ASSERT_EQ (1, election.election->votes ().size ());
node.vote_processor.vote (vote_invalid, channel);
node.vote_processor.flush ();
ASSERT_EQ (1, election.election->last_votes.size ());
ASSERT_EQ (1, election.election->votes ().size ());
node.vote_processor.vote (vote, channel);
node.vote_processor.flush ();
ASSERT_EQ (2, election.election->last_votes.size ());
ASSERT_EQ (2, election.election->votes ().size ());
}
TEST (vote_processor, no_capacity)
@ -213,8 +213,9 @@ TEST (vote_processor, no_broadcast_local)
// Make sure the vote was processed
auto election (node.active.election (send->qualified_root ()));
ASSERT_NE (nullptr, election);
auto existing (election->last_votes.find (nano::dev_genesis_key.pub));
ASSERT_NE (election->last_votes.end (), existing);
auto votes (election->votes ());
auto existing (votes.find (nano::dev_genesis_key.pub));
ASSERT_NE (votes.end (), existing);
ASSERT_EQ (vote->sequence, existing->second.sequence);
// Ensure the vote, from a local representative, was not broadcast on processing - it should be flooded on generation instead
ASSERT_EQ (0, node.stats.count (nano::stat::type::message, nano::stat::detail::confirm_ack, nano::stat::dir::out));
@ -245,8 +246,9 @@ TEST (vote_processor, no_broadcast_local)
// Make sure the vote was processed
auto election2 (node.active.election (send2->qualified_root ()));
ASSERT_NE (nullptr, election2);
auto existing2 (election2->last_votes.find (nano::dev_genesis_key.pub));
ASSERT_NE (election2->last_votes.end (), existing2);
auto votes2 (election2->votes ());
auto existing2 (votes2.find (nano::dev_genesis_key.pub));
ASSERT_NE (votes2.end (), existing2);
ASSERT_EQ (vote2->sequence, existing2->second.sequence);
// Ensure the vote was broadcast
ASSERT_EQ (1, node.stats.count (nano::stat::type::message, nano::stat::detail::confirm_ack, nano::stat::dir::out));
@ -278,8 +280,9 @@ TEST (vote_processor, no_broadcast_local)
// Make sure the vote was processed
auto election3 (node.active.election (open->qualified_root ()));
ASSERT_NE (nullptr, election3);
auto existing3 (election3->last_votes.find (nano::dev_genesis_key.pub));
ASSERT_NE (election3->last_votes.end (), existing3);
auto votes3 (election3->votes ());
auto existing3 (votes3.find (nano::dev_genesis_key.pub));
ASSERT_NE (votes3.end (), existing3);
ASSERT_EQ (vote3->sequence, existing3->second.sequence);
// Ensure the vote wass not broadcasst
ASSERT_EQ (1, node.stats.count (nano::stat::type::message, nano::stat::detail::confirm_ack, nano::stat::dir::out));

View file

@ -1251,11 +1251,10 @@ TEST (work_watcher, confirm_while_generating)
notified = true;
});
// Confirm the block
{
nano::lock_guard<std::mutex> guard (node.active.mutex);
ASSERT_EQ (1, node.active.roots.size ());
node.active.roots.begin ()->election->confirm_once ();
}
ASSERT_EQ (1, node.active.size ());
auto election (node.active.election (block1->qualified_root ()));
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (5s, node.block_confirmed (block1->hash ()));
ASSERT_EQ (0, node.work.size ());
ASSERT_TRUE (notified);
@ -1485,10 +1484,7 @@ TEST (wallet, search_pending)
wallet.store.erase (node.wallets.tx_begin_write (), nano::genesis_account);
// Now confirm the election
{
nano::lock_guard<std::mutex> guard (node.active.mutex);
election->confirm_once ();
}
election->force_confirm ();
ASSERT_TIMELY (5s, node.block_confirmed (send->hash ()) && node.active.empty ());

View file

@ -324,7 +324,7 @@ void nano::active_transactions::request_confirm (nano::unique_lock<std::mutex> &
--optimistic_elections_count;
}
election_l->cleanup ();
cleanup_election (election_l->cleanup_info ());
i = sorted_roots_l.erase (i);
}
else
@ -348,6 +348,37 @@ void nano::active_transactions::request_confirm (nano::unique_lock<std::mutex> &
}
}
void nano::active_transactions::cleanup_election (nano::election_cleanup_info const & info_a)
{
debug_assert (!mutex.try_lock ());
for (auto const & [hash, block] : info_a.blocks)
{
auto erased (blocks.erase (hash));
(void)erased;
debug_assert (erased == 1);
erase_inactive_votes_cache (hash);
// Notify observers about dropped elections & blocks lost confirmed elections
if (!info_a.confirmed || hash != info_a.winner)
{
node.observers.active_stopped.notify (hash);
}
}
if (!info_a.confirmed)
{
recently_dropped.add (info_a.root);
// Clear network filter in another thread
node.worker.push_task ([node_l = node.shared (), blocks_l = std::move (info_a.blocks)]() {
for (auto const & block : blocks_l)
{
node_l->network.publish_filter.clear (block.second);
}
});
}
}
void nano::active_transactions::add_expired_optimistic_election (nano::election const & election_a)
{
auto account = election_a.status.winner->account ();
@ -1013,8 +1044,8 @@ double nano::active_transactions::normalized_multiplier (nano::block const & blo
{
auto election (*root_it_a);
debug_assert (election != roots.end ());
auto find_block (election->election->blocks.find (block_a.hash ()));
if (find_block != election->election->blocks.end () && find_block->second->has_sideband ())
auto find_block (election->election->last_blocks.find (block_a.hash ()));
if (find_block != election->election->last_blocks.end () && find_block->second->has_sideband ())
{
threshold = nano::work_threshold (block_a.work_version (), find_block->second->sideband ().details);
}
@ -1113,18 +1144,6 @@ double nano::active_transactions::active_multiplier ()
return trended_active_multiplier.load ();
}
// List of active blocks in elections
std::deque<std::shared_ptr<nano::block>> nano::active_transactions::list_blocks ()
{
std::deque<std::shared_ptr<nano::block>> result;
nano::lock_guard<std::mutex> lock (mutex);
for (auto & root : roots)
{
result.push_back (root.election->status.winner);
}
return result;
}
std::deque<nano::election_status> nano::active_transactions::list_recently_cemented ()
{
nano::lock_guard<std::mutex> lock (mutex);
@ -1161,7 +1180,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->cleanup ();
cleanup_election (root_it->election->cleanup_info ());
roots.get<tag_root> ().erase (root_it);
lock.unlock ();
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 ()));

View file

@ -195,7 +195,6 @@ public:
uint64_t limited_active_difficulty (nano::block const &);
uint64_t limited_active_difficulty (nano::work_version const, uint64_t const);
double active_multiplier ();
std::deque<std::shared_ptr<nano::block>> list_blocks ();
void erase (nano::block const &);
bool empty ();
size_t size ();
@ -245,6 +244,8 @@ private:
bool update_difficulty_impl (roots_iterator const &, nano::block const &);
void request_loop ();
void request_confirm (nano::unique_lock<std::mutex> &);
// Erase all blocks from active and, if not confirmed, clear digests from network filters
void cleanup_election (nano::election_cleanup_info const &);
nano::condition_variable condition;
bool started{ false };
std::atomic<bool> stopped{ false };

View file

@ -7,10 +7,6 @@
using namespace std::chrono;
int constexpr nano::election::passive_duration_factor;
int constexpr nano::election::active_request_count_min;
int constexpr nano::election::confirmed_duration_factor;
std::chrono::milliseconds nano::election::base_latency () const
{
return node.network_params.network.is_dev_network () ? 25ms : 1000ms;
@ -25,14 +21,14 @@ nano::election_vote_result::election_vote_result (bool replay_a, bool processed_
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, bool prioritized_a, nano::election_behavior election_behavior_a) :
confirmation_action (confirmation_action_a),
prioritized_m (prioritized_a),
election_behavior (election_behavior_a),
behavior (election_behavior_a),
node (node_a),
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 }),
height (block_a->sideband ().height),
root (block_a->root ())
{
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);
last_blocks.emplace (block_a->hash (), block_a);
}
void nano::election::confirm_once (nano::election_status_type type_a)
@ -45,7 +41,7 @@ void nano::election::confirm_once (nano::election_status_type type_a)
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.block_count = nano::narrow_cast<decltype (status.block_count)> (last_blocks.size ());
status.voter_count = nano::narrow_cast<decltype (status.voter_count)> (last_votes.size ());
status.type = type_a;
auto status_l (status);
@ -251,16 +247,16 @@ bool nano::election::have_quorum (nano::tally_t const & tally_a, nano::uint128_t
nano::tally_t nano::election::tally ()
{
std::unordered_map<nano::block_hash, nano::uint128_t> block_weights;
for (auto vote_info : last_votes)
for (auto const & [account, info] : last_votes)
{
block_weights[vote_info.second.hash] += node.ledger.weight (vote_info.first);
block_weights[info.hash] += node.ledger.weight (account);
}
last_tally = block_weights;
nano::tally_t result;
for (auto item : block_weights)
{
auto block (blocks.find (item.first));
if (block != blocks.end ())
auto block (last_blocks.find (item.first));
if (block != last_blocks.end ())
{
result.emplace (item.second, block->second);
}
@ -274,9 +270,9 @@ void nano::election::confirm_if_quorum ()
debug_assert (!tally_l.empty ());
auto winner (tally_l.begin ());
auto block_l (winner->second);
auto winner_hash_l (block_l->hash ());
auto const & winner_hash_l (block_l->hash ());
status.tally = winner->first;
auto status_winner_hash_l (status.winner->hash ());
auto const & status_winner_hash_l (status.winner->hash ());
nano::uint128_t sum (0);
for (auto & i : tally_l)
{
@ -290,7 +286,7 @@ void nano::election::confirm_if_quorum ()
}
if (have_quorum (tally_l, sum))
{
if (node.config.logging.vote_logging () || (node.config.logging.election_fork_tally_logging () && blocks.size () > 1))
if (node.config.logging.vote_logging () || (node.config.logging.election_fork_tally_logging () && last_blocks.size () > 1))
{
log_votes (tally_l);
}
@ -376,7 +372,7 @@ bool nano::election::publish (std::shared_ptr<nano::block> block_a)
{
// Do not insert new blocks if already confirmed
auto result (confirmed ());
if (!result && blocks.size () >= 10)
if (!result && last_blocks.size () >= 10)
{
if (last_tally[block_a->hash ()] < node.online_reps.online_stake () / 10)
{
@ -385,10 +381,10 @@ bool nano::election::publish (std::shared_ptr<nano::block> block_a)
}
if (!result)
{
auto existing = blocks.find (block_a->hash ());
if (existing == blocks.end ())
auto existing = last_blocks.find (block_a->hash ());
if (existing == last_blocks.end ())
{
blocks.emplace (std::make_pair (block_a->hash (), block_a));
last_blocks.emplace (std::make_pair (block_a->hash (), block_a));
if (!insert_inactive_votes_cache (block_a->hash ()))
{
// Even if no votes were in cache, they could be in the election
@ -409,42 +405,15 @@ bool nano::election::publish (std::shared_ptr<nano::block> block_a)
return result;
}
size_t nano::election::last_votes_size ()
nano::election_cleanup_info nano::election::cleanup_info () const
{
nano::lock_guard<std::mutex> lock (node.active.mutex);
return last_votes.size ();
}
void nano::election::cleanup ()
{
bool unconfirmed (!confirmed ());
auto winner_root (status.winner->qualified_root ());
auto winner_hash (status.winner->hash ());
for (auto const & block : blocks)
{
auto & hash (block.first);
auto erased (node.active.blocks.erase (hash));
(void)erased;
debug_assert (erased == 1);
node.active.erase_inactive_votes_cache (hash);
// Notify observers about dropped elections & blocks lost confirmed elections
if (unconfirmed || hash != winner_hash)
{
node.observers.active_stopped.notify (hash);
}
}
if (unconfirmed)
{
node.active.recently_dropped.add (winner_root);
// Clear network filter in another thread
node.worker.push_task ([node_l = node.shared (), blocks_l = std::move (blocks)]() {
for (auto const & block : blocks_l)
{
node_l->network.publish_filter.clear (block.second);
}
});
}
debug_assert (!node.active.mutex.try_lock ());
return nano::election_cleanup_info{
confirmed (),
status.winner->qualified_root (),
status.winner->hash (),
last_blocks
};
}
size_t nano::election::insert_inactive_votes_cache (nano::block_hash const & hash_a)
@ -478,7 +447,7 @@ bool nano::election::prioritized () const
bool nano::election::optimistic () const
{
return election_behavior == nano::election_behavior::optimistic;
return behavior == nano::election_behavior::optimistic;
}
void nano::election::prioritize_election (nano::vote_generator_session & generator_session_a)
@ -489,6 +458,12 @@ void nano::election::prioritize_election (nano::vote_generator_session & generat
generator_session_a.add (root, status.winner->hash ());
}
std::shared_ptr<nano::block> nano::election::winner ()
{
nano::lock_guard<std::mutex> guard (node.active.mutex);
return status.winner;
}
void nano::election::generate_votes ()
{
debug_assert (!node.active.mutex.try_lock ());
@ -512,3 +487,24 @@ void nano::election::remove_votes (nano::block_hash const & hash_a)
node.history.erase (root);
}
}
void nano::election::force_confirm (nano::election_status_type type_a)
{
release_assert (node.network_params.network.is_dev_network ());
nano::lock_guard<std::mutex> guard (node.active.mutex);
confirm_once (type_a);
}
std::unordered_map<nano::block_hash, std::shared_ptr<nano::block>> nano::election::blocks ()
{
debug_assert (node.network_params.network.is_dev_network ());
nano::lock_guard<std::mutex> guard (node.active.mutex);
return last_blocks;
}
std::unordered_map<nano::account, nano::vote_info> nano::election::votes ()
{
debug_assert (node.network_params.network.is_dev_network ());
nano::lock_guard<std::mutex> guard (node.active.mutex);
return last_votes;
}

View file

@ -13,6 +13,7 @@ namespace nano
{
class channel;
class confirmation_solicitor;
class json_handler;
class node;
class vote_generator_session;
class vote_info final
@ -35,6 +36,13 @@ enum class election_behavior
normal,
optimistic
};
struct election_cleanup_info final
{
bool confirmed;
nano::qualified_root root;
nano::block_hash winner;
std::unordered_map<nano::block_hash, std::shared_ptr<nano::block>> blocks;
};
class election final : public std::enable_shared_from_this<nano::election>
{
@ -52,9 +60,9 @@ private: // State management
expired_confirmed,
expired_unconfirmed
};
static int constexpr passive_duration_factor = 5;
static int constexpr active_request_count_min = 2;
static int constexpr confirmed_duration_factor = 5;
static unsigned constexpr passive_duration_factor = 5;
static unsigned constexpr active_request_count_min = 2;
static unsigned constexpr confirmed_duration_factor = 5;
std::atomic<nano::election::state_t> state_m = { state_t::passive };
// These time points must be protected by this mutex
@ -65,54 +73,73 @@ private: // State management
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 (nano::confirmation_solicitor &);
void send_confirm_req (nano::confirmation_solicitor &);
// Calculate votes for local representatives
void generate_votes ();
void remove_votes (nano::block_hash const &);
std::atomic<bool> prioritized_m = { false };
public:
election (nano::node &, std::shared_ptr<nano::block>, std::function<void(std::shared_ptr<nano::block>)> const &, bool, nano::election_behavior);
nano::election_vote_result vote (nano::account, uint64_t, nano::block_hash);
nano::tally_t tally ();
// Check if we have vote quorum
bool have_quorum (nano::tally_t const &, nano::uint128_t) const;
void confirm_once (nano::election_status_type = nano::election_status_type::active_confirmed_quorum);
// Confirm this block if quorum is met
void confirm_if_quorum ();
void log_votes (nano::tally_t const &, std::string const & = "") const;
bool publish (std::shared_ptr<nano::block> block_a);
size_t last_votes_size ();
size_t insert_inactive_votes_cache (nano::block_hash const &);
bool prioritized () const;
bool optimistic () const;
void prioritize_election (nano::vote_generator_session &);
// Erase all blocks from active and, if not confirmed, clear digests from network filters
void cleanup ();
public: // State transitions
bool transition_time (nano::confirmation_solicitor &);
void transition_active ();
private:
void transition_active_impl ();
public:
public: // Status
bool confirmed () const;
bool failed () const;
nano::election_behavior election_behavior{ nano::election_behavior::normal };
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::now () };
bool prioritized () const;
bool optimistic () const;
std::shared_ptr<nano::block> winner ();
void log_votes (nano::tally_t const &, std::string const & = "") const;
nano::tally_t tally ();
bool have_quorum (nano::tally_t const &, nano::uint128_t) const;
nano::election_status status;
unsigned confirmation_request_count{ 0 };
std::unordered_map<nano::block_hash, nano::uint128_t> last_tally;
std::chrono::seconds late_blocks_delay{ 5 };
public: // Interface
election (nano::node &, std::shared_ptr<nano::block>, std::function<void(std::shared_ptr<nano::block>)> const &, bool, nano::election_behavior);
nano::election_vote_result vote (nano::account, uint64_t, nano::block_hash);
bool publish (std::shared_ptr<nano::block> block_a);
size_t insert_inactive_votes_cache (nano::block_hash const &);
// Confirm this block if quorum is met
void confirm_if_quorum ();
void prioritize_election (nano::vote_generator_session &);
nano::election_cleanup_info cleanup_info () const;
public: // Information
uint64_t const height;
nano::root const root;
private:
void transition_active_impl ();
void confirm_once (nano::election_status_type = nano::election_status_type::active_confirmed_quorum);
void broadcast_block (nano::confirmation_solicitor &);
void send_confirm_req (nano::confirmation_solicitor &);
// Calculate votes for local representatives
void generate_votes ();
void remove_votes (nano::block_hash const &);
private:
std::unordered_map<nano::block_hash, std::shared_ptr<nano::block>> last_blocks;
std::unordered_map<nano::account, nano::vote_info> last_votes;
std::unordered_map<nano::block_hash, nano::uint128_t> last_tally;
nano::election_behavior const behavior{ nano::election_behavior::normal };
std::chrono::steady_clock::time_point const election_start = { std::chrono::steady_clock::now () };
nano::node & node;
static std::chrono::seconds constexpr late_blocks_delay{ 5 };
friend class active_transactions;
friend class confirmation_solicitor;
friend class json_handler;
public: // Only used in tests
void force_confirm (nano::election_status_type = nano::election_status_type::active_confirmed_quorum);
std::unordered_map<nano::account, nano::vote_info> votes ();
std::unordered_map<nano::block_hash, std::shared_ptr<nano::block>> blocks ();
friend class confirmation_solicitor_different_hash_Test;
friend class confirmation_solicitor_bypass_max_requests_cap_Test;
friend class votes_add_existing_Test;
friend class votes_add_old_Test;
};
}

View file

@ -6726,12 +6726,9 @@ TEST (rpc, block_confirmed)
node->process_active (send);
node->block_processor.flush ();
node->block_confirm (send);
{
auto election = node->active.election (send->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node->active.mutex);
election->confirm_once ();
}
auto election = node->active.election (send->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
// Wait until the confirmation height has been set
ASSERT_TIMELY (10s, node->ledger.block_confirmed (node->store.tx_begin_read (), send->hash ()) && !node->confirmation_height_processor.is_processing_block (send->hash ()));
@ -7722,12 +7719,9 @@ TEST (rpc, confirmation_active)
node1.process_active (send2);
nano::blocks_confirm (node1, { send1, send2 });
ASSERT_EQ (2, node1.active.size ());
{
nano::lock_guard<std::mutex> guard (node1.active.mutex);
auto info (node1.active.roots.find (send1->qualified_root ()));
ASSERT_NE (node1.active.roots.end (), info);
info->election->confirm_once ();
}
auto election (node1.active.election (send1->qualified_root ()));
ASSERT_NE (nullptr, election);
election->force_confirm ();
boost::property_tree::ptree request;
request.put ("action", "confirmation_active");

View file

@ -224,8 +224,10 @@ TEST (node, fork_storm)
}
else
{
nano::lock_guard<std::mutex> lock (node_a->active.mutex);
if (node_a->active.roots.begin ()->election->last_votes_size () == 1)
nano::unique_lock<std::mutex> lock (node_a->active.mutex);
auto election = node_a->active.roots.begin ()->election;
lock.unlock ();
if (election->votes ().size () == 1)
{
++single;
}
@ -490,8 +492,7 @@ TEST (confirmation_height, many_accounts_single_confirmation)
auto election_insertion_result (node->active.insert (block));
ASSERT_TRUE (election_insertion_result.inserted);
ASSERT_NE (nullptr, election_insertion_result.election);
nano::lock_guard<std::mutex> guard (node->active.mutex);
election_insertion_result.election->confirm_once ();
election_insertion_result.election->force_confirm ();
}
ASSERT_TIMELY (120s, node->ledger.block_confirmed (node->store.tx_begin_read (), last_open_hash));
@ -559,8 +560,7 @@ TEST (confirmation_height, many_accounts_many_confirmations)
auto election_insertion_result (node->active.insert (open_block));
ASSERT_TRUE (election_insertion_result.inserted);
ASSERT_NE (nullptr, election_insertion_result.election);
nano::lock_guard<std::mutex> guard (node->active.mutex);
election_insertion_result.election->confirm_once ();
election_insertion_result.election->force_confirm ();
}
auto const num_blocks_to_confirm = (num_accounts - 1) * 2;
@ -647,8 +647,7 @@ TEST (confirmation_height, long_chains)
auto election_insertion_result (node->active.insert (receive1));
ASSERT_TRUE (election_insertion_result.inserted);
ASSERT_NE (nullptr, election_insertion_result.election);
nano::lock_guard<std::mutex> guard (node->active.mutex);
election_insertion_result.election->confirm_once ();
election_insertion_result.election->force_confirm ();
}
ASSERT_TIMELY (30s, node->ledger.block_confirmed (node->store.tx_begin_read (), receive1->hash ()));
@ -845,8 +844,7 @@ TEST (confirmation_height, many_accounts_send_receive_self)
node->block_confirm (open_block);
auto election = node->active.election (open_block->qualified_root ());
ASSERT_NE (nullptr, election);
nano::lock_guard<std::mutex> guard (node->active.mutex);
election->confirm_once ();
election->force_confirm ();
}
system.deadline_set (100s);