diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index cb7f355c6..9704b6035 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -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 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 election; - { - nano::lock_guard 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 (key.pub, key.prv, 1, std::vector (1, send->hash ()))); node.vote_processor.vote (vote1, std::make_shared (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 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 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 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 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 guard (node.active.mutex); - election->cleanup (); - } + node.active.erase (*block); // Push a worker task to ensure the cleanup is already performed std::atomic 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 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 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 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 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 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 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 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 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 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 ())); diff --git a/nano/core_test/bootstrap.cpp b/nano/core_test/bootstrap.cpp index 24b8be1d7..dc4ad5b2c 100644 --- a/nano/core_test/bootstrap.cpp +++ b/nano/core_test/bootstrap.cpp @@ -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 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)); diff --git a/nano/core_test/confirmation_height.cpp b/nano/core_test/confirmation_height.cpp index 9261c28ca..1d68bddbc 100644 --- a/nano/core_test/confirmation_height.cpp +++ b/nano/core_test/confirmation_height.cpp @@ -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 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 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 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 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 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 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 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 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 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 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 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 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 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); diff --git a/nano/core_test/confirmation_solicitor.cpp b/nano/core_test/confirmation_solicitor.cpp index ed0415300..3e99b227a 100644 --- a/nano/core_test/confirmation_solicitor.cpp +++ b/nano/core_test/confirmation_solicitor.cpp @@ -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::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 guard (node2.active.mutex); - auto election (std::make_shared (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 (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::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 (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 guard (node2.active.mutex); - auto election (std::make_shared (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 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 guard (node2.active.mutex); - auto election (std::make_shared (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 (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)); } +} diff --git a/nano/core_test/conflicts.cpp b/nano/core_test/conflicts.cpp index e3385791f..a7912336f 100644 --- a/nano/core_test/conflicts.cpp +++ b/nano/core_test/conflicts.cpp @@ -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 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 (key2.pub, key2.prv, 0, send2)); node1.active.vote (vote1); - ASSERT_EQ (1, node1.active.size ()); - { - nano::lock_guard 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) diff --git a/nano/core_test/gap_cache.cpp b/nano/core_test/gap_cache.cpp index 074c0e686..f22cd23cb 100644 --- a/nano/core_test/gap_cache.cpp +++ b/nano/core_test/gap_cache.cpp @@ -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 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); diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 90e63b4b8..351ca53bb 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -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 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::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 (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 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::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::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 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 lock (node1.active.mutex); - lock.unlock (); nano::keypair key2; auto send2 (std::make_shared (genesis.hash (), key2.pub, 0, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, 0)); auto vote2 (std::make_shared (key2.pub, key2.prv, 1, send2)); ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2)); auto vote1 (std::make_shared (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 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 (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::dev_genesis_key.pub, nano::dev_genesis_key.prv, 2, send2)); // Pretend we've waited the timeout + nano::unique_lock 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 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::dev_genesis_key.pub, nano::dev_genesis_key.prv, 2, send1)); auto channel (std::make_shared (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::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 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::dev_genesis_key.pub, nano::dev_genesis_key.prv, 2, send2)); node1.vote_processor.vote_blocking (vote2, channel); - nano::unique_lock 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 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) diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index da4d0adf3..2ef6b31fe 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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::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 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 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 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 election; - while (election == nullptr) - { - ASSERT_NO_ERROR (system.poll ()); - nano::lock_guard lock (node1.active.mutex); - auto existing = node1.active.blocks.find (send1->hash ()); - if (existing != node1.active.blocks.end ()) - { - election = existing->second; - } - } - nano::unique_lock 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 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 guard (node.active.mutex); - ASSERT_EQ (1, election->last_votes.size ()); + ASSERT_EQ (1, election->votes ().size ()); + nano::unique_lock 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 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 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 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); diff --git a/nano/core_test/request_aggregator.cpp b/nano/core_test/request_aggregator.cpp index 4025ea7ee..8ad34933e 100644 --- a/nano/core_test/request_aggregator.cpp +++ b/nano/core_test/request_aggregator.cpp @@ -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 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 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 ()); diff --git a/nano/core_test/vote_processor.cpp b/nano/core_test/vote_processor.cpp index bfd23cdbc..439a764b5 100644 --- a/nano/core_test/vote_processor.cpp +++ b/nano/core_test/vote_processor.cpp @@ -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)); diff --git a/nano/core_test/wallet.cpp b/nano/core_test/wallet.cpp index 20cf718f8..685fe8278 100644 --- a/nano/core_test/wallet.cpp +++ b/nano/core_test/wallet.cpp @@ -1251,11 +1251,10 @@ TEST (work_watcher, confirm_while_generating) notified = true; }); // Confirm the block - { - nano::lock_guard 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 guard (node.active.mutex); - election->confirm_once (); - } + election->force_confirm (); ASSERT_TIMELY (5s, node.block_confirmed (send->hash ()) && node.active.empty ()); diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index f9063ed99..83acfcd4d 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -324,7 +324,7 @@ void nano::active_transactions::request_confirm (nano::unique_lock & --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 & } } +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> nano::active_transactions::list_blocks () -{ - std::deque> result; - nano::lock_guard lock (mutex); - for (auto & root : roots) - { - result.push_back (root.election->status.winner); - } - return result; -} - std::deque nano::active_transactions::list_recently_cemented () { nano::lock_guard lock (mutex); @@ -1161,7 +1180,7 @@ void nano::active_transactions::erase (nano::block const & block_a) auto root_it (roots.get ().find (block_a.qualified_root ())); if (root_it != roots.get ().end ()) { - root_it->election->cleanup (); + cleanup_election (root_it->election->cleanup_info ()); roots.get ().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 ())); diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index 9331ac8e8..a84a0adbe 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -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> 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 &); + // 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 stopped{ false }; diff --git a/nano/node/election.cpp b/nano/node/election.cpp index caca8225b..a329816ad 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -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 block_a, std::function)> 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::system_clock::now ().time_since_epoch ()), std::chrono::duration_values::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::system_clock::now ().time_since_epoch ()); status.election_duration = std::chrono::duration_cast (std::chrono::steady_clock::now () - election_start); status.confirmation_request_count = confirmation_request_count; - status.block_count = nano::narrow_cast (blocks.size ()); + status.block_count = nano::narrow_cast (last_blocks.size ()); status.voter_count = nano::narrow_cast (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 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 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 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 block_a) return result; } -size_t nano::election::last_votes_size () +nano::election_cleanup_info nano::election::cleanup_info () const { - nano::lock_guard 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::election::winner () +{ + nano::lock_guard 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 guard (node.active.mutex); + confirm_once (type_a); +} + +std::unordered_map> nano::election::blocks () +{ + debug_assert (node.network_params.network.is_dev_network ()); + nano::lock_guard guard (node.active.mutex); + return last_blocks; +} + +std::unordered_map nano::election::votes () +{ + debug_assert (node.network_params.network.is_dev_network ()); + nano::lock_guard guard (node.active.mutex); + return last_votes; +} diff --git a/nano/node/election.hpp b/nano/node/election.hpp index 8bfeaa435..255cbcf9e 100644 --- a/nano/node/election.hpp +++ b/nano/node/election.hpp @@ -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> blocks; +}; class election final : public std::enable_shared_from_this { @@ -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 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 prioritized_m = { false }; -public: - election (nano::node &, std::shared_ptr, std::function)> 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 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 last_votes; - std::unordered_map> blocks; - std::chrono::steady_clock::time_point election_start = { std::chrono::steady_clock::now () }; + bool prioritized () const; + bool optimistic () const; + std::shared_ptr 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 last_tally; - std::chrono::seconds late_blocks_delay{ 5 }; + +public: // Interface + election (nano::node &, std::shared_ptr, std::function)> const &, bool, nano::election_behavior); + nano::election_vote_result vote (nano::account, uint64_t, nano::block_hash); + bool publish (std::shared_ptr 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> last_blocks; + std::unordered_map last_votes; + std::unordered_map 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 votes (); + std::unordered_map> 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; }; } diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 8e29d7ab2..6197aa307 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -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 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 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"); diff --git a/nano/slow_test/node.cpp b/nano/slow_test/node.cpp index 1d97f8acb..0be9c441d 100644 --- a/nano/slow_test/node.cpp +++ b/nano/slow_test/node.cpp @@ -224,8 +224,10 @@ TEST (node, fork_storm) } else { - nano::lock_guard lock (node_a->active.mutex); - if (node_a->active.roots.begin ()->election->last_votes_size () == 1) + nano::unique_lock 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 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 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 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 guard (node->active.mutex); - election->confirm_once (); + election->force_confirm (); } system.deadline_set (100s);