Remove dead code from active_transactions (#3868)

This commit is contained in:
Piotr Wójcik 2022-07-21 11:48:47 +02:00 committed by GitHub
commit f9d9b75ca7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 11 additions and 1213 deletions

View file

@ -1200,150 +1200,6 @@ TEST (active_transactions, activate_inactive)
ASSERT_FALSE (node.active.active (open->qualified_root ()) || node.block_confirmed_or_being_confirmed (node.store.tx_begin_read (), open->hash ())); ASSERT_FALSE (node.active.active (open->qualified_root ()) || node.block_confirmed_or_being_confirmed (node.store.tx_begin_read (), open->hash ()));
} }
namespace nano
{
TEST (active_transactions, pessimistic_elections)
{
nano::system system;
nano::node_flags flags;
nano::node_config config (nano::get_available_port (), system.logging);
config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
auto & node = *system.add_node (config, flags);
nano::keypair key;
nano::state_block_builder builder;
auto send = builder.make_block ()
.account (nano::dev::genesis_key.pub)
.previous (nano::dev::genesis->hash ())
.representative (nano::dev::genesis_key.pub)
.link (nano::dev::genesis_key.pub)
.balance (nano::dev::constants.genesis_amount - 1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*system.work.generate (nano::dev::genesis->hash ()))
.build_shared ();
ASSERT_EQ (nano::process_result::progress, node.process (*send).code);
auto send2 = builder.make_block ()
.account (nano::dev::genesis_key.pub)
.previous (send->hash ())
.representative (nano::dev::genesis_key.pub)
.link (key.pub)
.balance (nano::dev::constants.genesis_amount - 2)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*system.work.generate (send->hash ()))
.build ();
ASSERT_EQ (nano::process_result::progress, node.process (*send2).code);
auto open = builder.make_block ()
.account (key.pub)
.previous (0)
.representative (key.pub)
.link (send2->hash ())
.balance (1)
.sign (key.prv, key.pub)
.work (*system.work.generate (key.pub))
.build_shared ();
ASSERT_EQ (nano::process_result::progress, node.process (*open).code);
// This should only cement the first block in genesis account
uint64_t election_count = 0;
// Make dummy election with winner.
{
nano::election election1 (
node, send, [] (auto const &) {}, [] (auto const &) {}, nano::election_behavior::normal);
nano::election election2 (
node, open, [] (auto const &) {}, [] (auto const &) {}, nano::election_behavior::normal);
node.active.add_expired_optimistic_election (election1);
node.active.add_expired_optimistic_election (election2);
}
node.active.confirm_expired_frontiers_pessimistically (node.store.tx_begin_read (), 100, election_count);
ASSERT_EQ (1, election_count);
ASSERT_EQ (2, node.active.expired_optimistic_election_infos.size ());
ASSERT_EQ (2, node.active.expired_optimistic_election_infos.size ());
auto election_started_it = node.active.expired_optimistic_election_infos.get<nano::active_transactions::tag_election_started> ().begin ();
ASSERT_EQ (election_started_it->account, nano::dev::genesis->account ());
ASSERT_EQ (election_started_it->election_started, true);
ASSERT_EQ ((++election_started_it)->election_started, false);
// No new elections should get started yet
node.active.confirm_expired_frontiers_pessimistically (node.store.tx_begin_read (), 100, election_count);
ASSERT_EQ (1, election_count);
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);
election->force_confirm ();
ASSERT_TIMELY (3s, node.block_confirmed (send->hash ()) && !node.confirmation_height_processor.is_processing_added_block (send->hash ()));
nano::confirmation_height_info genesis_confirmation_height_info;
nano::confirmation_height_info key1_confirmation_height_info;
{
auto transaction = node.store.tx_begin_read ();
node.store.confirmation_height.get (transaction, nano::dev::genesis->account (), genesis_confirmation_height_info);
ASSERT_EQ (2, genesis_confirmation_height_info.height);
node.store.confirmation_height.get (transaction, key.pub, key1_confirmation_height_info);
ASSERT_EQ (0, key1_confirmation_height_info.height);
}
// Activation of cemented frontier successor should get started after the first pessimistic block is confirmed
ASSERT_TIMELY (10s, node.active.active (send2->qualified_root ()));
node.active.confirm_expired_frontiers_pessimistically (node.store.tx_begin_read (), 100, election_count);
ASSERT_EQ (1, election_count);
ASSERT_EQ (2, node.active.expired_optimistic_election_infos.size ());
// Confirm it
election = node.active.election (send2->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (3s, node.block_confirmed (send2->hash ()));
{
auto transaction = node.store.tx_begin_read ();
node.store.confirmation_height.get (transaction, nano::dev::genesis->account (), genesis_confirmation_height_info);
ASSERT_EQ (3, genesis_confirmation_height_info.height);
node.store.confirmation_height.get (transaction, key.pub, key1_confirmation_height_info);
ASSERT_EQ (0, key1_confirmation_height_info.height);
}
// Wait until activation of destination account is done.
ASSERT_TIMELY (10s, node.active.active (open->qualified_root ()));
// Election count should not increase, but the elections should be marked as started for that account afterwards
ASSERT_EQ (election_started_it->election_started, false);
node.active.confirm_expired_frontiers_pessimistically (node.store.tx_begin_read (), 100, election_count);
ASSERT_EQ (1, election_count);
ASSERT_EQ (2, node.active.expired_optimistic_election_infos.size ());
node.active.confirm_expired_frontiers_pessimistically (node.store.tx_begin_read (), 100, election_count);
election = node.active.election (open->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
ASSERT_TIMELY (3s, node.block_confirmed (open->hash ()));
{
auto transaction = node.store.tx_begin_read ();
node.store.confirmation_height.get (transaction, nano::dev::genesis->account (), genesis_confirmation_height_info);
ASSERT_EQ (3, genesis_confirmation_height_info.height);
node.store.confirmation_height.get (transaction, key.pub, key1_confirmation_height_info);
ASSERT_EQ (1, key1_confirmation_height_info.height);
}
// Sanity check that calling it again on a fully cemented chain has no adverse effects.
node.active.confirm_expired_frontiers_pessimistically (node.store.tx_begin_read (), 100, election_count);
ASSERT_EQ (1, election_count);
ASSERT_EQ (2, node.active.expired_optimistic_election_infos.size ());
}
}
TEST (active_transactions, list_active) TEST (active_transactions, list_active)
{ {
nano::system system (1); nano::system system (1);

View file

@ -6,371 +6,6 @@
using namespace std::chrono_literals; using namespace std::chrono_literals;
namespace nano
{
TEST (frontiers_confirmation, prioritize_frontiers)
{
nano::system system;
// Prevent frontiers being confirmed as it will affect the priorization checking
nano::node_config node_config (nano::get_available_port (), system.logging);
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
auto node = system.add_node (node_config);
nano::keypair key1;
nano::keypair key2;
nano::keypair key3;
nano::keypair key4;
nano::block_builder builder;
nano::block_hash latest1 (node->latest (nano::dev::genesis_key.pub));
// Send different numbers of blocks all accounts
auto send1 = builder
.send ()
.previous (latest1)
.destination (key1.pub)
.balance (node->config.online_weight_minimum.number () + 10000)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*system.work.generate (latest1))
.build ();
auto send2 = builder
.send ()
.previous (send1->hash ())
.destination (key1.pub)
.balance (node->config.online_weight_minimum.number () + 8500)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*system.work.generate (send1->hash ()))
.build ();
auto send3 = builder
.send ()
.previous (send2->hash ())
.destination (key1.pub)
.balance (node->config.online_weight_minimum.number () + 8000)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*system.work.generate (send2->hash ()))
.build ();
auto send4 = builder
.send ()
.previous (send3->hash ())
.destination (key2.pub)
.balance (node->config.online_weight_minimum.number () + 7500)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*system.work.generate (send3->hash ()))
.build ();
auto send5 = builder
.send ()
.previous (send4->hash ())
.destination (key3.pub)
.balance (node->config.online_weight_minimum.number () + 6500)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*system.work.generate (send4->hash ()))
.build ();
auto send6 = builder
.send ()
.previous (send5->hash ())
.destination (key4.pub)
.balance (node->config.online_weight_minimum.number () + 6000)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*system.work.generate (send5->hash ()))
.build ();
// Open all accounts and add other sends to get different uncemented counts (as well as some which are the same)
auto open1 = builder
.open ()
.source (send1->hash ())
.representative (nano::dev::genesis->account ())
.account (key1.pub)
.sign (key1.prv, key1.pub)
.work (*system.work.generate (key1.pub))
.build ();
auto send7 = builder
.send ()
.previous (open1->hash ())
.destination (nano::dev::genesis_key.pub)
.balance (500)
.sign (key1.prv, key1.pub)
.work (*system.work.generate (open1->hash ()))
.build ();
auto open2 = builder
.open ()
.source (send4->hash ())
.representative (nano::dev::genesis->account ())
.account (key2.pub)
.sign (key2.prv, key2.pub)
.work (*system.work.generate (key2.pub))
.build ();
auto open3 = builder
.open ()
.source (send5->hash ())
.representative (nano::dev::genesis->account ())
.account (key3.pub)
.sign (key3.prv, key3.pub)
.work (*system.work.generate (key3.pub))
.build ();
auto send8 = builder
.send ()
.previous (open3->hash ())
.destination (nano::dev::genesis_key.pub)
.balance (500)
.sign (key3.prv, key3.pub)
.work (*system.work.generate (open3->hash ()))
.build ();
auto send9 = builder
.send ()
.previous (send8->hash ())
.destination (nano::dev::genesis_key.pub)
.balance (200)
.sign (key3.prv, key3.pub)
.work (*system.work.generate (send8->hash ()))
.build ();
auto open4 = builder
.open ()
.source (send6->hash ())
.representative (nano::dev::genesis->account ())
.account (key4.pub)
.sign (key4.prv, key4.pub)
.work (*system.work.generate (key4.pub))
.build ();
auto send10 = builder
.send ()
.previous (open4->hash ())
.destination (nano::dev::genesis_key.pub)
.balance (500)
.sign (key4.prv, key4.pub)
.work (*system.work.generate (open4->hash ()))
.build ();
auto send11 = builder
.send ()
.previous (send10->hash ())
.destination (nano::dev::genesis_key.pub)
.balance (200)
.sign (key4.prv, key4.pub)
.work (*system.work.generate (send10->hash ()))
.build ();
{
auto transaction = node->store.tx_begin_write ();
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send1).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send2).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send3).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send4).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send5).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send6).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *open1).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send7).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *open2).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *open3).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send8).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send9).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *open4).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send10).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send11).code);
}
auto transaction = node->store.tx_begin_read ();
constexpr auto num_accounts = 5;
auto priority_orders_match = [] (auto const & cementable_frontiers, auto const & desired_order) {
return std::equal (desired_order.begin (), desired_order.end (), cementable_frontiers.template get<1> ().begin (), cementable_frontiers.template get<1> ().end (), [] (nano::account const & account, nano::cementable_account const & cementable_account) {
return (account == cementable_account.account);
});
};
{
node->active.prioritize_frontiers_for_confirmation (transaction, std::chrono::seconds (1), std::chrono::seconds (1));
ASSERT_EQ (node->active.priority_cementable_frontiers_size (), num_accounts);
// Check the order of accounts is as expected (greatest number of uncemented blocks at the front). key3 and key4 have the same value, the order is unspecified so check both
std::array<nano::account, num_accounts> desired_order_1{ nano::dev::genesis->account (), key3.pub, key4.pub, key1.pub, key2.pub };
std::array<nano::account, num_accounts> desired_order_2{ nano::dev::genesis->account (), key4.pub, key3.pub, key1.pub, key2.pub };
ASSERT_TRUE (priority_orders_match (node->active.priority_cementable_frontiers, desired_order_1) || priority_orders_match (node->active.priority_cementable_frontiers, desired_order_2));
}
{
// Add some to the local node wallets and check ordering of both containers
system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv);
system.wallet (0)->insert_adhoc (key1.prv);
system.wallet (0)->insert_adhoc (key2.prv);
node->active.prioritize_frontiers_for_confirmation (transaction, std::chrono::seconds (1), std::chrono::seconds (1));
ASSERT_EQ (node->active.priority_cementable_frontiers_size (), num_accounts - 3);
ASSERT_EQ (node->active.priority_wallet_cementable_frontiers_size (), num_accounts - 2);
std::array<nano::account, 3> local_desired_order{ nano::dev::genesis->account (), key1.pub, key2.pub };
ASSERT_TRUE (priority_orders_match (node->active.priority_wallet_cementable_frontiers, local_desired_order));
std::array<nano::account, 2> desired_order_1{ key3.pub, key4.pub };
std::array<nano::account, 2> desired_order_2{ key4.pub, key3.pub };
ASSERT_TRUE (priority_orders_match (node->active.priority_cementable_frontiers, desired_order_1) || priority_orders_match (node->active.priority_cementable_frontiers, desired_order_2));
}
{
// Add the remainder of accounts to node wallets and check size/ordering is correct
system.wallet (0)->insert_adhoc (key3.prv);
system.wallet (0)->insert_adhoc (key4.prv);
node->active.prioritize_frontiers_for_confirmation (transaction, std::chrono::seconds (1), std::chrono::seconds (1));
ASSERT_EQ (node->active.priority_cementable_frontiers_size (), 0);
ASSERT_EQ (node->active.priority_wallet_cementable_frontiers_size (), num_accounts);
std::array<nano::account, num_accounts> desired_order_1{ nano::dev::genesis->account (), key3.pub, key4.pub, key1.pub, key2.pub };
std::array<nano::account, num_accounts> desired_order_2{ nano::dev::genesis->account (), key4.pub, key3.pub, key1.pub, key2.pub };
ASSERT_TRUE (priority_orders_match (node->active.priority_wallet_cementable_frontiers, desired_order_1) || priority_orders_match (node->active.priority_wallet_cementable_frontiers, desired_order_2));
}
// Check that accounts which already exist have their order modified when the uncemented count changes.
auto send12 = builder
.send ()
.previous (send9->hash ())
.destination (nano::dev::genesis_key.pub)
.balance (100)
.sign (key3.prv, key3.pub)
.work (*system.work.generate (send9->hash ()))
.build ();
auto send13 = builder
.send ()
.previous (send12->hash ())
.destination (nano::dev::genesis_key.pub)
.balance (90)
.sign (key3.prv, key3.pub)
.work (*system.work.generate (send12->hash ()))
.build ();
auto send14 = builder
.send ()
.previous (send13->hash ())
.destination (nano::dev::genesis_key.pub)
.balance (80)
.sign (key3.prv, key3.pub)
.work (*system.work.generate (send13->hash ()))
.build ();
auto send15 = builder
.send ()
.previous (send14->hash ())
.destination (nano::dev::genesis_key.pub)
.balance (70)
.sign (key3.prv, key3.pub)
.work (*system.work.generate (send14->hash ()))
.build ();
auto send16 = builder
.send ()
.previous (send15->hash ())
.destination (nano::dev::genesis_key.pub)
.balance (60)
.sign (key3.prv, key3.pub)
.work (*system.work.generate (send15->hash ()))
.build ();
auto send17 = builder
.send ()
.previous (send16->hash ())
.destination (nano::dev::genesis_key.pub)
.balance (50)
.sign (key3.prv, key3.pub)
.work (*system.work.generate (send16->hash ()))
.build ();
{
auto transaction = node->store.tx_begin_write ();
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send12).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send13).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send14).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send15).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send16).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send17).code);
}
transaction.refresh ();
node->active.prioritize_frontiers_for_confirmation (transaction, std::chrono::seconds (1), std::chrono::seconds (1));
ASSERT_TRUE (priority_orders_match (node->active.priority_wallet_cementable_frontiers, std::array<nano::account, num_accounts>{ key3.pub, nano::dev::genesis->account (), key4.pub, key1.pub, key2.pub }));
uint64_t election_count = 0;
node->active.confirm_prioritized_frontiers (transaction, 100, election_count);
// Check that the active transactions roots contains the frontiers
ASSERT_TIMELY (10s, node->active.size () == num_accounts);
std::array<nano::qualified_root, num_accounts> frontiers{ send17->qualified_root (), send6->qualified_root (), send7->qualified_root (), open2->qualified_root (), send11->qualified_root () };
for (auto & frontier : frontiers)
{
ASSERT_TRUE (node->active.active (frontier));
}
}
TEST (frontiers_confirmation, prioritize_frontiers_max_optimistic_elections)
{
nano::system system;
// Prevent frontiers being confirmed as it will affect the priorization checking
nano::node_config node_config (nano::get_available_port (), system.logging);
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
auto node = system.add_node (node_config);
node->ledger.cache.cemented_count = node->ledger.bootstrap_weight_max_blocks - 1;
auto max_optimistic_election_count_under_hardcoded_weight = node->active.max_optimistic ();
node->ledger.cache.cemented_count = node->ledger.bootstrap_weight_max_blocks;
auto max_optimistic_election_count = node->active.max_optimistic ();
ASSERT_GT (max_optimistic_election_count_under_hardcoded_weight, max_optimistic_election_count);
for (auto i = 0; i < max_optimistic_election_count * 2; ++i)
{
auto transaction = node->store.tx_begin_write ();
auto latest = node->latest (nano::dev::genesis->account ());
nano::keypair key;
nano::block_builder builder;
auto send = builder
.send ()
.previous (latest)
.destination (key.pub)
.balance (node->config.online_weight_minimum.number () + 10000)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*system.work.generate (latest))
.build ();
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send).code);
auto open = builder
.open ()
.source (send->hash ())
.representative (nano::dev::genesis->account ())
.account (key.pub)
.sign (key.prv, key.pub)
.work (*system.work.generate (key.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *open).code);
}
{
nano::unique_lock<nano::mutex> lk (node->active.mutex);
node->active.frontiers_confirmation (lk);
}
ASSERT_EQ (max_optimistic_election_count, node->active.roots.size ());
nano::account next_frontier_account{ 2 };
node->active.next_frontier_account = next_frontier_account;
// Call frontiers confirmation again and confirm that next_frontier_account hasn't changed
{
nano::unique_lock<nano::mutex> lk (node->active.mutex);
node->active.frontiers_confirmation (lk);
}
ASSERT_EQ (max_optimistic_election_count, node->active.roots.size ());
ASSERT_EQ (next_frontier_account, node->active.next_frontier_account);
}
TEST (frontiers_confirmation, expired_optimistic_elections_removal)
{
nano::system system;
nano::node_config node_config (nano::get_available_port (), system.logging);
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
auto node = system.add_node (node_config);
// This should be removed on the next prioritization call
node->active.expired_optimistic_election_infos.emplace (std::chrono::steady_clock::now () - (node->active.expired_optimistic_election_info_cutoff + 1min), nano::account (1));
ASSERT_EQ (1, node->active.expired_optimistic_election_infos.size ());
node->active.prioritize_frontiers_for_confirmation (node->store.tx_begin_read (), 0s, 0s);
ASSERT_EQ (0, node->active.expired_optimistic_election_infos.size ());
// This should not be removed on the next prioritization call
node->active.expired_optimistic_election_infos.emplace (std::chrono::steady_clock::now () - (node->active.expired_optimistic_election_info_cutoff - 1min), nano::account (1));
ASSERT_EQ (1, node->active.expired_optimistic_election_infos.size ());
node->active.prioritize_frontiers_for_confirmation (node->store.tx_begin_read (), 0s, 0s);
ASSERT_EQ (1, node->active.expired_optimistic_election_infos.size ());
}
}
TEST (frontiers_confirmation, mode) TEST (frontiers_confirmation, mode)
{ {
nano::keypair key; nano::keypair key;

View file

@ -4139,10 +4139,14 @@ namespace nano
TEST (node, deferred_dependent_elections) TEST (node, deferred_dependent_elections)
{ {
nano::system system; nano::system system;
nano::node_config node_config_1{ nano::get_available_port (), system.logging };
node_config_1.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
nano::node_config node_config_2{ nano::get_available_port (), system.logging };
node_config_2.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
nano::node_flags flags; nano::node_flags flags;
flags.disable_request_loop = true; flags.disable_request_loop = true;
auto & node = *system.add_node (flags); auto & node = *system.add_node (node_config_1, flags);
auto & node2 = *system.add_node (flags); // node2 will be used to ensure all blocks are being propagated auto & node2 = *system.add_node (node_config_2, flags); // node2 will be used to ensure all blocks are being propagated
nano::state_block_builder builder; nano::state_block_builder builder;
nano::keypair key; nano::keypair key;
@ -4185,6 +4189,7 @@ TEST (node, deferred_dependent_elections)
.representative (nano::dev::genesis_key.pub) // was key.pub .representative (nano::dev::genesis_key.pub) // was key.pub
.sign (key.prv, key.pub) .sign (key.prv, key.pub)
.build_shared (); .build_shared ();
node.process_local (send1); node.process_local (send1);
node.block_processor.flush (); node.block_processor.flush ();
node.scheduler.flush (); node.scheduler.flush ();
@ -4224,14 +4229,6 @@ TEST (node, deferred_dependent_elections)
node.scheduler.flush (); node.scheduler.flush ();
ASSERT_FALSE (node.active.active (open->qualified_root ())); ASSERT_FALSE (node.active.active (open->qualified_root ()));
// Frontier confirmation also starts elections
ASSERT_NO_ERROR (system.poll_until_true (5s, [&node, &send2] {
nano::unique_lock<nano::mutex> lock{ node.active.mutex };
node.active.frontiers_confirmation (lock);
lock.unlock ();
return node.active.election (send2->qualified_root ()) != nullptr;
}));
// Drop both elections // Drop both elections
node.active.erase (*open); node.active.erase (*open);
ASSERT_FALSE (node.active.active (open->qualified_root ())); ASSERT_FALSE (node.active.active (open->qualified_root ()));

View file

@ -14,10 +14,6 @@
using namespace std::chrono; using namespace std::chrono;
std::size_t constexpr nano::active_transactions::max_active_elections_frontier_insertion;
constexpr std::chrono::minutes nano::active_transactions::expired_optimistic_election_info_cutoff;
nano::active_transactions::active_transactions (nano::node & node_a, nano::confirmation_height_processor & confirmation_height_processor_a) : nano::active_transactions::active_transactions (nano::node & node_a, nano::confirmation_height_processor & confirmation_height_processor_a) :
scheduler{ node_a.scheduler }, // Move dependencies requiring this circular reference scheduler{ node_a.scheduler }, // Move dependencies requiring this circular reference
confirmation_height_processor{ confirmation_height_processor_a }, confirmation_height_processor{ confirmation_height_processor_a },
@ -49,118 +45,6 @@ nano::active_transactions::~active_transactions ()
stop (); stop ();
} }
bool nano::active_transactions::insert_election_from_frontiers_confirmation (std::shared_ptr<nano::block> const & block_a, nano::account const & account_a, nano::uint128_t previous_balance_a, nano::election_behavior election_behavior_a)
{
bool inserted{ false };
nano::unique_lock<nano::mutex> lock (mutex);
if (roots.get<tag_root> ().find (block_a->qualified_root ()) == roots.get<tag_root> ().end ())
{
std::function<void (std::shared_ptr<nano::block> const &)> election_confirmation_cb;
if (election_behavior_a == nano::election_behavior::optimistic)
{
election_confirmation_cb = [this] (std::shared_ptr<nano::block> const & block_a) {
--optimistic_elections_count;
};
}
auto insert_result = insert_impl (lock, block_a, election_behavior_a, election_confirmation_cb);
inserted = insert_result.inserted;
if (inserted)
{
insert_result.election->transition_active ();
if (insert_result.election->optimistic ())
{
++optimistic_elections_count;
}
}
}
return inserted;
}
nano::frontiers_confirmation_info nano::active_transactions::get_frontiers_confirmation_info ()
{
// Limit maximum count of elections to start
auto rep_counts (node.wallets.reps ());
bool representative (node.config.enable_voting && rep_counts.voting > 0);
bool half_princpal_representative (representative && rep_counts.have_half_rep ());
/* Check less frequently for regular nodes in auto mode */
bool agressive_mode (half_princpal_representative || node.config.frontiers_confirmation == nano::frontiers_confirmation_mode::always);
auto is_dev_network = node.network_params.network.is_dev_network ();
auto roots_size = size ();
auto check_time_exceeded = std::chrono::steady_clock::now () >= next_frontier_check;
auto max_elections = max_active_elections_frontier_insertion;
auto low_active_elections = roots_size < max_elections;
bool wallets_check_required = (!skip_wallets || !priority_wallet_cementable_frontiers.empty ()) && !agressive_mode;
// Minimise dropping real-time transactions, set the number of frontiers added to a factor of the maximum number of possible active elections
auto max_active = node.config.active_elections_size / 20;
if (roots_size <= max_active && (check_time_exceeded || wallets_check_required || (!is_dev_network && low_active_elections && agressive_mode)))
{
// When the number of active elections is low increase max number of elections for setting confirmation height.
if (max_active > roots_size + max_elections)
{
max_elections = max_active - roots_size;
}
}
else
{
max_elections = 0;
}
return nano::frontiers_confirmation_info{ max_elections, agressive_mode };
}
void nano::active_transactions::set_next_frontier_check (bool agressive_mode_a)
{
auto request_interval (std::chrono::milliseconds (node.network_params.network.request_interval_ms));
auto rel_time_next_frontier_check = request_interval * (agressive_mode_a ? 20 : 60);
// Decrease check time for dev network
int dev_network_factor = node.network_params.network.is_dev_network () ? 1000 : 1;
next_frontier_check = steady_clock::now () + (rel_time_next_frontier_check / dev_network_factor);
}
void nano::active_transactions::confirm_prioritized_frontiers (nano::transaction const & transaction_a, uint64_t max_elections_a, uint64_t & elections_count_a)
{
nano::unique_lock<nano::mutex> lk (mutex);
auto start_elections_for_prioritized_frontiers = [&transaction_a, &elections_count_a, max_elections_a, &lk, this] (prioritize_num_uncemented & cementable_frontiers) {
while (!cementable_frontiers.empty () && !this->stopped && elections_count_a < max_elections_a && optimistic_elections_count < max_optimistic ())
{
auto cementable_account_front_it = cementable_frontiers.get<tag_uncemented> ().begin ();
auto cementable_account = *cementable_account_front_it;
cementable_frontiers.get<tag_uncemented> ().erase (cementable_account_front_it);
if (expired_optimistic_election_infos.get<tag_account> ().count (cementable_account.account) == 0)
{
lk.unlock ();
nano::account_info info;
auto error = this->node.store.account.get (transaction_a, cementable_account.account, info);
if (!error)
{
if (!this->confirmation_height_processor.is_processing_block (info.head))
{
nano::confirmation_height_info confirmation_height_info;
this->node.store.confirmation_height.get (transaction_a, cementable_account.account, confirmation_height_info);
if (info.block_count > confirmation_height_info.height)
{
auto block (this->node.store.block.get (transaction_a, info.head));
auto previous_balance (this->node.ledger.balance (transaction_a, block->previous ()));
auto inserted_election = this->insert_election_from_frontiers_confirmation (block, cementable_account.account, previous_balance, nano::election_behavior::optimistic);
if (inserted_election)
{
++elections_count_a;
}
}
}
}
lk.lock ();
}
}
};
start_elections_for_prioritized_frontiers (priority_wallet_cementable_frontiers);
start_elections_for_prioritized_frontiers (priority_cementable_frontiers);
}
void nano::active_transactions::block_cemented_callback (std::shared_ptr<nano::block> const & block_a) void nano::active_transactions::block_cemented_callback (std::shared_ptr<nano::block> const & block_a)
{ {
auto transaction = node.store.tx_begin_read (); auto transaction = node.store.tx_begin_read ();
@ -318,16 +202,6 @@ void nano::active_transactions::request_confirm (nano::unique_lock<nano::mutex>
if (election_l->transition_time (solicitor)) if (election_l->transition_time (solicitor))
{ {
if (election_l->optimistic () && election_l->failed ())
{
if (election_l->confirmation_request_count != 0)
{
// Locks active mutex
add_expired_optimistic_election (*election_l);
}
--optimistic_elections_count;
}
// Locks active mutex, cleans up the election and erases it from the main container // Locks active mutex, cleans up the election and erases it from the main container
if (!confirmed_l) if (!confirmed_l)
{ {
@ -429,156 +303,6 @@ std::vector<std::shared_ptr<nano::election>> nano::active_transactions::list_act
return result_l; return result_l;
} }
void nano::active_transactions::add_expired_optimistic_election (nano::election const & election_a)
{
nano::lock_guard<nano::mutex> guard (mutex);
auto account = election_a.status.winner->account ();
if (account.is_zero ())
{
account = election_a.status.winner->sideband ().account;
}
auto it = expired_optimistic_election_infos.get<tag_account> ().find (account);
if (it != expired_optimistic_election_infos.get<tag_account> ().end ())
{
expired_optimistic_election_infos.get<tag_account> ().modify (it, [] (auto & expired_optimistic_election) {
expired_optimistic_election.expired_time = std::chrono::steady_clock::now ();
expired_optimistic_election.election_started = false;
});
}
else
{
expired_optimistic_election_infos.emplace (std::chrono::steady_clock::now (), account);
}
// Expire the oldest one if a maximum is reached
auto const max_expired_optimistic_election_infos = 10000;
if (expired_optimistic_election_infos.size () > max_expired_optimistic_election_infos)
{
expired_optimistic_election_infos.get<tag_expired_time> ().erase (expired_optimistic_election_infos.get<tag_expired_time> ().begin ());
}
expired_optimistic_election_infos_size = expired_optimistic_election_infos.size ();
}
unsigned nano::active_transactions::max_optimistic ()
{
return node.ledger.cache.cemented_count < node.ledger.bootstrap_weight_max_blocks ? std::numeric_limits<unsigned>::max () : 50u;
}
void nano::active_transactions::frontiers_confirmation (nano::unique_lock<nano::mutex> & lock_a)
{
// Spend some time prioritizing accounts with the most uncemented blocks to reduce voting traffic
auto request_interval = std::chrono::milliseconds (node.network_params.network.request_interval_ms);
// Spend longer searching ledger accounts when there is a low amount of elections going on
auto low_active = roots.size () < 1000;
auto time_to_spend_prioritizing_ledger_accounts = request_interval / (low_active ? 20 : 100);
auto time_to_spend_prioritizing_wallet_accounts = request_interval / 250;
auto time_to_spend_confirming_pessimistic_accounts = time_to_spend_prioritizing_ledger_accounts;
lock_a.unlock ();
auto transaction = node.store.tx_begin_read ();
prioritize_frontiers_for_confirmation (transaction, node.network_params.network.is_dev_network () ? std::chrono::milliseconds (50) : time_to_spend_prioritizing_ledger_accounts, time_to_spend_prioritizing_wallet_accounts);
auto frontiers_confirmation_info = get_frontiers_confirmation_info ();
if (frontiers_confirmation_info.can_start_elections ())
{
uint64_t elections_count (0);
confirm_prioritized_frontiers (transaction, frontiers_confirmation_info.max_elections, elections_count);
confirm_expired_frontiers_pessimistically (transaction, frontiers_confirmation_info.max_elections, elections_count);
set_next_frontier_check (frontiers_confirmation_info.aggressive_mode);
}
lock_a.lock ();
}
/*
* This function takes the expired_optimistic_election_infos generated from failed elections from frontiers confirmations and starts
* confirming blocks at cemented height + 1 (cemented frontier successor) for an account only if all dependent blocks already
* confirmed.
*/
void nano::active_transactions::confirm_expired_frontiers_pessimistically (nano::transaction const & transaction_a, uint64_t max_elections_a, uint64_t & elections_count_a)
{
auto i{ node.store.account.begin (transaction_a, next_frontier_account) };
auto n{ node.store.account.end () };
nano::timer<std::chrono::milliseconds> timer (nano::timer_state::started);
nano::confirmation_height_info confirmation_height_info;
// Loop through any expired optimistic elections which have not been started yet. This tag stores already started ones first
std::vector<nano::account> elections_started_for_account;
for (auto i = expired_optimistic_election_infos.get<tag_election_started> ().lower_bound (false); i != expired_optimistic_election_infos.get<tag_election_started> ().end ();)
{
if (stopped || elections_count_a >= max_elections_a)
{
break;
}
auto const & account{ i->account };
nano::account_info account_info;
bool should_delete{ true };
if (!node.store.account.get (transaction_a, account, account_info))
{
node.store.confirmation_height.get (transaction_a, account, confirmation_height_info);
if (account_info.block_count > confirmation_height_info.height)
{
should_delete = false;
std::shared_ptr<nano::block> previous_block;
std::shared_ptr<nano::block> block;
if (confirmation_height_info.height == 0)
{
block = node.store.block.get (transaction_a, account_info.open_block);
}
else
{
previous_block = node.store.block.get (transaction_a, confirmation_height_info.frontier);
block = node.store.block.get (transaction_a, previous_block->sideband ().successor);
}
if (block && !node.confirmation_height_processor.is_processing_block (block->hash ()) && node.ledger.dependents_confirmed (transaction_a, *block))
{
nano::uint128_t previous_balance{ 0 };
if (previous_block && previous_block->balance ().is_zero ())
{
previous_balance = previous_block->sideband ().balance.number ();
}
auto inserted_election = insert_election_from_frontiers_confirmation (block, account, previous_balance, nano::election_behavior::normal);
if (inserted_election)
{
++elections_count_a;
}
elections_started_for_account.push_back (i->account);
}
}
}
if (should_delete)
{
// This account is confirmed already or doesn't exist.
i = expired_optimistic_election_infos.get<tag_election_started> ().erase (i);
expired_optimistic_election_infos_size = expired_optimistic_election_infos.size ();
}
else
{
++i;
}
}
for (auto const & account : elections_started_for_account)
{
auto it = expired_optimistic_election_infos.get<tag_account> ().find (account);
debug_assert (it != expired_optimistic_election_infos.get<tag_account> ().end ());
expired_optimistic_election_infos.get<tag_account> ().modify (it, [] (auto & expired_optimistic_election_info_a) {
expired_optimistic_election_info_a.election_started = true;
});
}
}
bool nano::active_transactions::should_do_frontiers_confirmation () const
{
auto pending_confirmation_height_size (confirmation_height_processor.awaiting_processing_size ());
auto disabled_confirmation_mode = (node.config.frontiers_confirmation == nano::frontiers_confirmation_mode::disabled);
auto conf_height_capacity_reached = pending_confirmation_height_size > confirmed_frontiers_max_pending_size;
auto all_cemented = node.ledger.cache.block_count == node.ledger.cache.cemented_count;
return (!disabled_confirmation_mode && !conf_height_capacity_reached && !all_cemented);
}
void nano::active_transactions::request_loop () void nano::active_transactions::request_loop ()
{ {
nano::unique_lock<nano::mutex> lock (mutex); nano::unique_lock<nano::mutex> lock (mutex);
@ -617,184 +341,6 @@ void nano::active_transactions::request_loop ()
} }
} }
bool nano::active_transactions::prioritize_account_for_confirmation (nano::active_transactions::prioritize_num_uncemented & cementable_frontiers_a, std::size_t & cementable_frontiers_size_a, nano::account const & account_a, nano::account_info const & info_a, uint64_t confirmation_height_a)
{
auto inserted_new{ false };
if (info_a.block_count > confirmation_height_a && !confirmation_height_processor.is_processing_block (info_a.head))
{
auto num_uncemented = info_a.block_count - confirmation_height_a;
nano::lock_guard<nano::mutex> guard (mutex);
auto it = cementable_frontiers_a.get<tag_account> ().find (account_a);
if (it != cementable_frontiers_a.get<tag_account> ().end ())
{
if (it->blocks_uncemented != num_uncemented)
{
// Account already exists and there is now a different uncemented block count so update it in the container
cementable_frontiers_a.get<tag_account> ().modify (it, [num_uncemented] (nano::cementable_account & info) {
info.blocks_uncemented = num_uncemented;
});
}
}
else
{
debug_assert (cementable_frontiers_size_a <= max_priority_cementable_frontiers);
if (cementable_frontiers_size_a == max_priority_cementable_frontiers)
{
// The maximum amount of frontiers stored has been reached. Check if the current frontier
// has more uncemented blocks than the lowest uncemented frontier in the collection if so replace it.
auto least_uncemented_frontier_it = cementable_frontiers_a.get<tag_uncemented> ().end ();
--least_uncemented_frontier_it;
if (num_uncemented > least_uncemented_frontier_it->blocks_uncemented)
{
cementable_frontiers_a.get<tag_uncemented> ().erase (least_uncemented_frontier_it);
cementable_frontiers_a.get<tag_account> ().emplace (account_a, num_uncemented);
}
}
else
{
inserted_new = true;
cementable_frontiers_a.get<tag_account> ().emplace (account_a, num_uncemented);
}
}
cementable_frontiers_size_a = cementable_frontiers_a.size ();
}
return inserted_new;
}
void nano::active_transactions::prioritize_frontiers_for_confirmation (nano::transaction const & transaction_a, std::chrono::milliseconds ledger_account_traversal_max_time_a, std::chrono::milliseconds wallet_account_traversal_max_time_a)
{
// Don't try to prioritize when there are a large number of pending confirmation heights as blocks can be cemented in the meantime, making the prioritization less reliable
if (confirmation_height_processor.awaiting_processing_size () < confirmed_frontiers_max_pending_size)
{
std::size_t priority_cementable_frontiers_size;
std::size_t priority_wallet_cementable_frontiers_size;
{
nano::lock_guard<nano::mutex> guard (mutex);
priority_cementable_frontiers_size = priority_cementable_frontiers.size ();
priority_wallet_cementable_frontiers_size = priority_wallet_cementable_frontiers.size ();
}
nano::timer<std::chrono::milliseconds> wallet_account_timer (nano::timer_state::started);
// Remove any old expired optimistic elections so they are no longer excluded in subsequent checks
auto expired_cutoff_it (expired_optimistic_election_infos.get<tag_expired_time> ().lower_bound (std::chrono::steady_clock::now () - expired_optimistic_election_info_cutoff));
expired_optimistic_election_infos.get<tag_expired_time> ().erase (expired_optimistic_election_infos.get<tag_expired_time> ().begin (), expired_cutoff_it);
expired_optimistic_election_infos_size = expired_optimistic_election_infos.size ();
auto num_new_inserted{ 0u };
auto should_iterate = [this, &num_new_inserted] () {
auto max_optimistic_l = max_optimistic ();
return !stopped && (max_optimistic_l > optimistic_elections_count && max_optimistic_l - optimistic_elections_count > num_new_inserted);
};
if (!skip_wallets)
{
// Prioritize wallet accounts first
{
nano::lock_guard<nano::mutex> lock (node.wallets.mutex);
auto wallet_transaction (node.wallets.tx_begin_read ());
auto const & items = node.wallets.items;
if (items.empty ())
{
skip_wallets = true;
}
for (auto item_it = items.cbegin (); item_it != items.cend () && should_iterate (); ++item_it)
{
// Skip this wallet if it has been traversed already while there are others still awaiting
if (wallet_ids_already_iterated.find (item_it->first) != wallet_ids_already_iterated.end ())
{
continue;
}
nano::account_info info;
auto & wallet (item_it->second);
nano::lock_guard<std::recursive_mutex> wallet_lock (wallet->store.mutex);
auto & next_wallet_frontier_account = next_wallet_id_accounts.emplace (item_it->first, wallet_store::special_count).first->second;
auto i (wallet->store.begin (wallet_transaction, next_wallet_frontier_account));
auto n (wallet->store.end ());
for (; i != n && should_iterate (); ++i)
{
auto const & account (i->first);
if (expired_optimistic_election_infos.get<tag_account> ().count (account) == 0 && !node.store.account.get (transaction_a, account, info))
{
nano::confirmation_height_info confirmation_height_info;
node.store.confirmation_height.get (transaction_a, account, confirmation_height_info);
// If it exists in normal priority collection delete from there.
auto it = priority_cementable_frontiers.find (account);
if (it != priority_cementable_frontiers.end ())
{
nano::lock_guard<nano::mutex> guard (mutex);
priority_cementable_frontiers.erase (it);
priority_cementable_frontiers_size = priority_cementable_frontiers.size ();
}
auto insert_newed = prioritize_account_for_confirmation (priority_wallet_cementable_frontiers, priority_wallet_cementable_frontiers_size, account, info, confirmation_height_info.height);
if (insert_newed)
{
++num_new_inserted;
}
if (wallet_account_timer.since_start () >= wallet_account_traversal_max_time_a)
{
break;
}
}
next_wallet_frontier_account = account.number () + 1;
}
// Go back to the beginning when we have reached the end of the wallet accounts for this wallet
if (i == n)
{
wallet_ids_already_iterated.emplace (item_it->first);
next_wallet_id_accounts.at (item_it->first) = wallet_store::special_count;
// Skip wallet accounts when they have all been traversed
if (std::next (item_it) == items.cend ())
{
wallet_ids_already_iterated.clear ();
skip_wallets = true;
}
}
}
}
}
nano::timer<std::chrono::milliseconds> timer (nano::timer_state::started);
auto i (node.store.account.begin (transaction_a, next_frontier_account));
auto n (node.store.account.end ());
for (; i != n && should_iterate (); ++i)
{
auto const & account (i->first);
auto const & info (i->second);
if (priority_wallet_cementable_frontiers.find (account) == priority_wallet_cementable_frontiers.end ())
{
if (expired_optimistic_election_infos.get<tag_account> ().count (account) == 0)
{
nano::confirmation_height_info confirmation_height_info;
node.store.confirmation_height.get (transaction_a, account, confirmation_height_info);
auto insert_newed = prioritize_account_for_confirmation (priority_cementable_frontiers, priority_cementable_frontiers_size, account, info, confirmation_height_info.height);
if (insert_newed)
{
++num_new_inserted;
}
}
}
next_frontier_account = account.number () + 1;
if (timer.since_start () >= ledger_account_traversal_max_time_a)
{
break;
}
}
// Go back to the beginning when we have reached the end of the accounts and start with wallet accounts next time
if (i == n)
{
next_frontier_account = 0;
skip_wallets = false;
}
}
}
void nano::active_transactions::stop () void nano::active_transactions::stop ()
{ {
nano::unique_lock<nano::mutex> lock (mutex); nano::unique_lock<nano::mutex> lock (mutex);
@ -830,14 +376,13 @@ nano::election_insertion_result nano::active_transactions::insert_impl (nano::un
{ {
result.inserted = true; result.inserted = true;
auto hash (block_a->hash ()); auto hash (block_a->hash ());
auto epoch (block_a->sideband ().details.epoch);
result.election = nano::make_shared<nano::election> ( result.election = nano::make_shared<nano::election> (
node, block_a, confirmation_action_a, [&node = node] (auto const & rep_a) { node, block_a, confirmation_action_a, [&node = node] (auto const & rep_a) {
// Representative is defined as online if replying to live votes or rep_crawler queries // Representative is defined as online if replying to live votes or rep_crawler queries
node.online_reps.observe (rep_a); node.online_reps.observe (rep_a);
}, },
election_behavior_a); election_behavior_a);
roots.get<tag_root> ().emplace (nano::active_transactions::conflict_info{ root, result.election, epoch, election_behavior_a }); roots.get<tag_root> ().emplace (nano::active_transactions::conflict_info{ root, result.election });
blocks.emplace (hash, result.election); blocks.emplace (hash, result.election);
// Increase hinted election counter while still holding lock // Increase hinted election counter while still holding lock
if (election_behavior_a == election_behavior::hinted) if (election_behavior_a == election_behavior::hinted)
@ -1126,18 +671,6 @@ boost::optional<nano::election_status_type> nano::active_transactions::confirm_b
return status_type; return status_type;
} }
std::size_t nano::active_transactions::priority_cementable_frontiers_size ()
{
nano::lock_guard<nano::mutex> guard (mutex);
return priority_cementable_frontiers.size ();
}
std::size_t nano::active_transactions::priority_wallet_cementable_frontiers_size ()
{
nano::lock_guard<nano::mutex> guard (mutex);
return priority_wallet_cementable_frontiers.size ();
}
std::size_t nano::active_transactions::inactive_votes_cache_size () std::size_t nano::active_transactions::inactive_votes_cache_size ()
{ {
nano::lock_guard<nano::mutex> guard (mutex); nano::lock_guard<nano::mutex> guard (mutex);
@ -1341,17 +874,6 @@ nano::cementable_account::cementable_account (nano::account const & account_a, s
{ {
} }
nano::expired_optimistic_election_info::expired_optimistic_election_info (std::chrono::steady_clock::time_point expired_time_a, nano::account account_a) :
expired_time (expired_time_a),
account (account_a)
{
}
bool nano::frontiers_confirmation_info::can_start_elections () const
{
return max_elections > 0;
}
std::unique_ptr<nano::container_info_component> nano::collect_container_info (active_transactions & active_transactions, std::string const & name) std::unique_ptr<nano::container_info_component> nano::collect_container_info (active_transactions & active_transactions, std::string const & name)
{ {
std::size_t roots_count; std::size_t roots_count;
@ -1373,11 +895,7 @@ std::unique_ptr<nano::container_info_component> nano::collect_container_info (ac
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "election_winner_details", active_transactions.election_winner_details_size (), sizeof (decltype (active_transactions.election_winner_details)::value_type) })); composite->add_component (std::make_unique<container_info_leaf> (container_info{ "election_winner_details", active_transactions.election_winner_details_size (), sizeof (decltype (active_transactions.election_winner_details)::value_type) }));
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "recently_confirmed", recently_confirmed_count, sizeof (decltype (active_transactions.recently_confirmed)::value_type) })); composite->add_component (std::make_unique<container_info_leaf> (container_info{ "recently_confirmed", recently_confirmed_count, sizeof (decltype (active_transactions.recently_confirmed)::value_type) }));
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "recently_cemented", recently_cemented_count, sizeof (decltype (active_transactions.recently_cemented)::value_type) })); composite->add_component (std::make_unique<container_info_leaf> (container_info{ "recently_cemented", recently_cemented_count, sizeof (decltype (active_transactions.recently_cemented)::value_type) }));
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "priority_wallet_cementable_frontiers", active_transactions.priority_wallet_cementable_frontiers_size (), sizeof (nano::cementable_account) }));
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "priority_cementable_frontiers", active_transactions.priority_cementable_frontiers_size (), sizeof (nano::cementable_account) }));
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "expired_optimistic_election_infos", active_transactions.expired_optimistic_election_infos_size, sizeof (decltype (active_transactions.expired_optimistic_election_infos)::value_type) }));
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "inactive_votes_cache", active_transactions.inactive_votes_cache_size (), sizeof (nano::gap_information) })); composite->add_component (std::make_unique<container_info_leaf> (container_info{ "inactive_votes_cache", active_transactions.inactive_votes_cache_size (), sizeof (nano::gap_information) }));
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "optimistic_elections_count", active_transactions.optimistic_elections_count, 0 })); // This isn't an extra container, is just to expose the count easily
composite->add_component (collect_container_info (active_transactions.generator, "generator")); composite->add_component (collect_container_info (active_transactions.generator, "generator"));
return composite; return composite;
} }

View file

@ -46,32 +46,6 @@ public:
uint64_t blocks_uncemented{ 0 }; uint64_t blocks_uncemented{ 0 };
}; };
class election_timepoint final
{
public:
std::chrono::steady_clock::time_point time;
nano::qualified_root root;
};
class expired_optimistic_election_info final
{
public:
expired_optimistic_election_info (std::chrono::steady_clock::time_point, nano::account);
std::chrono::steady_clock::time_point expired_time;
nano::account account;
bool election_started{ false };
};
class frontiers_confirmation_info
{
public:
bool can_start_elections () const;
std::size_t max_elections{ 0 };
bool aggressive_mode{ false };
};
class election_insertion_result final class election_insertion_result final
{ {
public: public:
@ -88,8 +62,6 @@ class active_transactions final
public: public:
nano::qualified_root root; nano::qualified_root root;
std::shared_ptr<nano::election> election; std::shared_ptr<nano::election> election;
nano::epoch epoch;
nano::election_behavior election_behavior; // Used to prioritize portion of AEC for vote hinting
}; };
friend class nano::election; friend class nano::election;
@ -102,8 +74,6 @@ class active_transactions final
class tag_uncemented {}; class tag_uncemented {};
class tag_arrival {}; class tag_arrival {};
class tag_hash {}; class tag_hash {};
class tag_expired_time {};
class tag_election_started {};
// clang-format on // clang-format on
public: public:
@ -159,8 +129,6 @@ public:
nano::confirmation_height_processor & confirmation_height_processor; nano::confirmation_height_processor & confirmation_height_processor;
nano::node & node; nano::node & node;
mutable nano::mutex mutex{ mutex_identifier (mutexes::active) }; mutable nano::mutex mutex{ mutex_identifier (mutexes::active) };
std::size_t priority_cementable_frontiers_size ();
std::size_t priority_wallet_cementable_frontiers_size ();
std::size_t inactive_votes_cache_size (); std::size_t inactive_votes_cache_size ();
std::size_t election_winner_details_size (); std::size_t election_winner_details_size ();
void add_election_winner_details (nano::block_hash const &, std::shared_ptr<nano::election> const &); void add_election_winner_details (nano::block_hash const &, std::shared_ptr<nano::election> const &);
@ -220,51 +188,10 @@ private:
mi::hashed_unique<mi::tag<tag_hash>, mi::hashed_unique<mi::tag<tag_hash>,
mi::member<recent_confirmation, nano::block_hash, &recent_confirmation::second>>>> mi::member<recent_confirmation, nano::block_hash, &recent_confirmation::second>>>>
recently_confirmed; recently_confirmed;
using prioritize_num_uncemented = boost::multi_index_container<nano::cementable_account,
mi::indexed_by<
mi::hashed_unique<mi::tag<tag_account>,
mi::member<nano::cementable_account, nano::account, &nano::cementable_account::account>>,
mi::ordered_non_unique<mi::tag<tag_uncemented>,
mi::member<nano::cementable_account, uint64_t, &nano::cementable_account::blocks_uncemented>,
std::greater<uint64_t>>>>;
boost::multi_index_container<nano::expired_optimistic_election_info,
mi::indexed_by<
mi::ordered_non_unique<mi::tag<tag_expired_time>,
mi::member<expired_optimistic_election_info, std::chrono::steady_clock::time_point, &expired_optimistic_election_info::expired_time>>,
mi::hashed_unique<mi::tag<tag_account>,
mi::member<expired_optimistic_election_info, nano::account, &expired_optimistic_election_info::account>>,
mi::ordered_non_unique<mi::tag<tag_election_started>,
mi::member<expired_optimistic_election_info, bool, &expired_optimistic_election_info::election_started>, std::greater<bool>>>>
expired_optimistic_election_infos;
// clang-format on // clang-format on
std::atomic<uint64_t> expired_optimistic_election_infos_size{ 0 };
int active_hinted_elections_count{ 0 }; int active_hinted_elections_count{ 0 };
// Frontiers confirmation
nano::frontiers_confirmation_info get_frontiers_confirmation_info ();
void confirm_prioritized_frontiers (nano::transaction const &, uint64_t, uint64_t &);
void confirm_expired_frontiers_pessimistically (nano::transaction const &, uint64_t, uint64_t &);
void frontiers_confirmation (nano::unique_lock<nano::mutex> &);
bool insert_election_from_frontiers_confirmation (std::shared_ptr<nano::block> const &, nano::account const &, nano::uint128_t, nano::election_behavior);
nano::account next_frontier_account{};
std::chrono::steady_clock::time_point next_frontier_check{ std::chrono::steady_clock::now () };
constexpr static std::size_t max_active_elections_frontier_insertion{ 1000 };
prioritize_num_uncemented priority_wallet_cementable_frontiers;
prioritize_num_uncemented priority_cementable_frontiers;
std::unordered_set<nano::wallet_id> wallet_ids_already_iterated;
std::unordered_map<nano::wallet_id, nano::account> next_wallet_id_accounts;
bool skip_wallets{ false };
std::atomic<unsigned> optimistic_elections_count{ 0 };
void prioritize_frontiers_for_confirmation (nano::transaction const &, std::chrono::milliseconds, std::chrono::milliseconds);
bool prioritize_account_for_confirmation (prioritize_num_uncemented &, std::size_t &, nano::account const &, nano::account_info const &, uint64_t);
unsigned max_optimistic ();
void set_next_frontier_check (bool);
void add_expired_optimistic_election (nano::election const &);
bool should_do_frontiers_confirmation () const;
static std::size_t constexpr max_priority_cementable_frontiers{ 100000 };
static std::size_t constexpr confirmed_frontiers_max_pending_size{ 10000 };
static std::chrono::minutes constexpr expired_optimistic_election_info_cutoff{ 30 };
ordered_cache inactive_votes_cache; ordered_cache inactive_votes_cache;
nano::inactive_cache_status inactive_votes_bootstrap_check (nano::unique_lock<nano::mutex> &, std::vector<std::pair<nano::account, uint64_t>> const &, nano::block_hash const &, nano::inactive_cache_status const &); nano::inactive_cache_status inactive_votes_bootstrap_check (nano::unique_lock<nano::mutex> &, std::vector<std::pair<nano::account, uint64_t>> const &, nano::block_hash const &, nano::inactive_cache_status const &);
nano::inactive_cache_status inactive_votes_bootstrap_check (nano::unique_lock<nano::mutex> &, nano::account const &, nano::block_hash const &, nano::inactive_cache_status const &); nano::inactive_cache_status inactive_votes_bootstrap_check (nano::unique_lock<nano::mutex> &, nano::account const &, nano::block_hash const &, nano::inactive_cache_status const &);

View file

@ -128,7 +128,7 @@ bool nano::election::state_change (nano::election::state_t expected_a, nano::ele
void nano::election::send_confirm_req (nano::confirmation_solicitor & solicitor_a) void nano::election::send_confirm_req (nano::confirmation_solicitor & solicitor_a)
{ {
if ((base_latency () * (optimistic () ? 10 : 5)) < (std::chrono::steady_clock::now () - last_req)) if ((base_latency () * 5) < (std::chrono::steady_clock::now () - last_req))
{ {
nano::lock_guard<nano::mutex> guard (mutex); nano::lock_guard<nano::mutex> guard (mutex);
if (!solicitor_a.add (*this)) if (!solicitor_a.add (*this))
@ -218,11 +218,10 @@ std::chrono::milliseconds nano::election::time_to_live ()
{ {
case election_behavior::normal: case election_behavior::normal:
return std::chrono::milliseconds (5 * 60 * 1000); return std::chrono::milliseconds (5 * 60 * 1000);
case election_behavior::optimistic:
return std::chrono::milliseconds (node.network_params.network.is_dev_network () ? 500 : 60 * 1000);
case election_behavior::hinted: case election_behavior::hinted:
return std::chrono::milliseconds (30 * 1000); return std::chrono::milliseconds (30 * 1000);
} }
release_assert (false);
} }
bool nano::election::have_quorum (nano::tally_t const & tally_a) const bool nano::election::have_quorum (nano::tally_t const & tally_a) const
@ -480,11 +479,6 @@ std::size_t nano::election::insert_inactive_votes_cache (nano::inactive_cache_in
return cache_a.voters.size (); return cache_a.voters.size ();
} }
bool nano::election::optimistic () const
{
return behavior == nano::election_behavior::optimistic;
}
nano::election_extended_status nano::election::current_status () const nano::election_extended_status nano::election::current_status () const
{ {
nano::lock_guard<nano::mutex> guard (mutex); nano::lock_guard<nano::mutex> guard (mutex);

View file

@ -43,7 +43,6 @@ public:
enum class election_behavior enum class election_behavior
{ {
normal, normal,
optimistic,
hinted hinted
}; };
struct election_extended_status final struct election_extended_status final
@ -90,7 +89,6 @@ public: // State transitions
public: // Status public: // Status
bool confirmed () const; bool confirmed () const;
bool failed () const; bool failed () const;
bool optimistic () const;
nano::election_extended_status current_status () const; nano::election_extended_status current_status () const;
std::shared_ptr<nano::block> winner () const; std::shared_ptr<nano::block> winner () const;
std::atomic<unsigned> confirmation_request_count{ 0 }; std::atomic<unsigned> confirmation_request_count{ 0 };

View file

@ -1352,133 +1352,6 @@ TEST (confirmation_height, many_accounts_send_receive_self_no_elections)
ASSERT_EQ (cemented_count, ledger.cache.cemented_count); ASSERT_EQ (cemented_count, ledger.cache.cemented_count);
} }
// Can take up to 1 hour (recommend modifying test work difficulty base level to speed this up)
TEST (confirmation_height, prioritize_frontiers_overwrite)
{
nano::system system;
nano::node_config node_config (nano::get_available_port (), system.logging);
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
auto node = system.add_node (node_config);
system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv);
auto num_accounts = node->active.max_priority_cementable_frontiers * 2;
nano::keypair last_keypair = nano::dev::genesis_key;
auto last_open_hash = node->latest (nano::dev::genesis_key.pub);
// Clear confirmation height so that the genesis account has the same amount of uncemented blocks as the other frontiers
{
auto transaction = node->store.tx_begin_write ();
node->store.confirmation_height.clear (transaction);
}
nano::block_builder builder;
{
auto transaction = node->store.tx_begin_write ();
for (auto i = num_accounts - 1; i > 0; --i)
{
nano::keypair key;
system.wallet (0)->insert_adhoc (key.prv);
auto send = builder
.send ()
.previous (last_open_hash)
.destination (key.pub)
.balance (nano::Gxrb_ratio - 1)
.sign (last_keypair.prv, last_keypair.pub)
.work (*system.work.generate (last_open_hash))
.build ();
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send).code);
auto open = builder
.open ()
.source (send->hash ())
.representative (last_keypair.pub)
.account (key.pub)
.sign (key.prv, key.pub)
.work (*system.work.generate (key.pub))
.build ();
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *open).code);
last_open_hash = open->hash ();
last_keypair = key;
}
}
auto transaction = node->store.tx_begin_read ();
{
// Fill both priority frontier collections.
node->active.prioritize_frontiers_for_confirmation (transaction, std::chrono::seconds (60), std::chrono::seconds (60));
ASSERT_EQ (node->active.priority_cementable_frontiers_size () + node->active.priority_wallet_cementable_frontiers_size (), num_accounts);
// Confirm the last frontier has the least number of uncemented blocks
auto last_frontier_it = node->active.priority_cementable_frontiers.get<1> ().end ();
--last_frontier_it;
ASSERT_EQ (last_frontier_it->account, last_keypair.pub);
ASSERT_EQ (last_frontier_it->blocks_uncemented, 1);
}
// Add a new frontier with 1 block, it should not be added to the frontier container because it is not higher than any already in the maxed out container
nano::keypair key;
auto latest_genesis = node->latest (nano::dev::genesis_key.pub);
auto send = builder
.send ()
.previous (latest_genesis)
.destination (key.pub)
.balance (nano::Gxrb_ratio - 1)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*system.work.generate (latest_genesis))
.build ();
auto open = builder
.open ()
.source (send->hash ())
.representative (nano::dev::genesis_key.pub)
.account (key.pub)
.sign (key.prv, key.pub)
.work (*system.work.generate (key.pub))
.build ();
{
auto transaction = node->store.tx_begin_write ();
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *open).code);
}
transaction.refresh ();
node->active.prioritize_frontiers_for_confirmation (transaction, std::chrono::seconds (60), std::chrono::seconds (60));
ASSERT_EQ (node->active.priority_cementable_frontiers_size (), num_accounts / 2);
ASSERT_EQ (node->active.priority_wallet_cementable_frontiers_size (), num_accounts / 2);
// The account now has an extra block (2 in total) so has 1 more uncemented block than the next smallest frontier in the collection.
auto send1 = builder
.send ()
.previous (send->hash ())
.destination (key.pub)
.balance (nano::Gxrb_ratio - 2)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*system.work.generate (send->hash ()))
.build ();
auto receive = builder
.receive ()
.previous (open->hash ())
.source (send1->hash ())
.sign (key.prv, key.pub)
.work (*system.work.generate (open->hash ()))
.build ();
{
auto transaction = node->store.tx_begin_write ();
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *send1).code);
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *receive).code);
}
// Confirm that it gets replaced
transaction.refresh ();
node->active.prioritize_frontiers_for_confirmation (transaction, std::chrono::seconds (60), std::chrono::seconds (60));
ASSERT_EQ (node->active.priority_cementable_frontiers_size (), num_accounts / 2);
ASSERT_EQ (node->active.priority_wallet_cementable_frontiers_size (), num_accounts / 2);
ASSERT_EQ (node->active.priority_cementable_frontiers.find (last_keypair.pub), node->active.priority_cementable_frontiers.end ());
ASSERT_NE (node->active.priority_cementable_frontiers.find (key.pub), node->active.priority_cementable_frontiers.end ());
// Check there are no matching accounts found in both containers
for (auto it = node->active.priority_cementable_frontiers.begin (); it != node->active.priority_cementable_frontiers.end (); ++it)
{
ASSERT_EQ (node->active.priority_wallet_cementable_frontiers.find (it->account), node->active.priority_wallet_cementable_frontiers.end ());
}
}
} }
namespace namespace