Add cemented frontier successor confirmation in request loop (#2885)
* Add pessimistic frontiers confirmation * Only start pessimistic after an optimistic election has been marked expired_unconfirmed * Use can_vote * (Unrelated) Improve block_confirmed & remove unused method * Increase number of confirmation rounds and delay pessimistic elections (Srayman suggestions) * Add new container to stats->objects * Remove update to base_latency (spotted by Srayman) * Update container names * Update test compile error * Change if condition so elections_count gets updated correctly * Disable successor activation for test * Gui/Serg review comments * Change "default" enum value to "normal" * Change new test to rely on activation and also some Srayman/Gui suggestions * Fix build error and Gui comment * Add active mutex lock guard around election creation in test * Use active.active (Gui) * Enable dependency test and fix to work with pessimistic (thanks Gui) * Formatting * Limit optimistic frontiers after initial cementing * Fix logic when transitioning over cemented count threshold for hardcoded bootstrap weight * Add test to check maximum optimistic elections * Remove from optimistic count as soon as the election is confirmed (Gui comment) * Always set bootstrap_weight_max_blocks * Simplifiy election insert using null default arg confirmation action * Correctly update expired_optimistic_election_infos_size in all places * Move election count modification outside function (Gui comment) * Gui suggestions to improve active transactions during startup and initial cementing * Fix lower_bound checks (thanks Gui + for test) * Update changes needed from latest merge * C++14ify * Intermittent test failure Co-authored-by: Guilherme Lawless <guilherme@nano.org> Co-authored-by: clemahieu <clemahieu@gmail.com>
This commit is contained in:
parent
e091cd17a1
commit
758d6209c9
12 changed files with 595 additions and 112 deletions
|
@ -1447,3 +1447,157 @@ TEST (active_transactions, difficulty_update_observer)
|
|||
});
|
||||
ASSERT_TIMELY (3s, update_received);
|
||||
}
|
||||
|
||||
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::genesis_hash)
|
||||
.representative (nano::dev_genesis_key.pub)
|
||||
.link (nano::dev_genesis_key.pub)
|
||||
.balance (nano::genesis_amount - 1)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (*system.work.generate (nano::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::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::lock_guard<std::mutex> guard (node.active.mutex);
|
||||
nano::election election1 (
|
||||
node, send, [](auto const & block) {}, false, nano::election_behavior::normal);
|
||||
nano::election election2 (
|
||||
node, open, [](auto const & block) {}, false, 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::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);
|
||||
nano::lock_guard<std::mutex> guard (node.active.mutex);
|
||||
election->confirm_once ();
|
||||
}
|
||||
|
||||
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::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 (send->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
|
||||
{
|
||||
auto election = node.active.election (send2->qualified_root ());
|
||||
ASSERT_NE (nullptr, election);
|
||||
nano::lock_guard<std::mutex> guard (node.active.mutex);
|
||||
election->confirm_once ();
|
||||
}
|
||||
|
||||
ASSERT_TIMELY (3s, node.block_confirmed (send2->hash ()));
|
||||
|
||||
{
|
||||
auto transaction = node.store.tx_begin_read ();
|
||||
node.store.confirmation_height_get (transaction, nano::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 (send2->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);
|
||||
|
||||
{
|
||||
auto election = node.active.election (open->qualified_root ());
|
||||
ASSERT_NE (nullptr, election);
|
||||
nano::lock_guard<std::mutex> guard (node.active.mutex);
|
||||
election->confirm_once ();
|
||||
}
|
||||
|
||||
ASSERT_TIMELY (3s, node.block_confirmed (open->hash ()));
|
||||
|
||||
{
|
||||
auto transaction = node.store.tx_begin_read ();
|
||||
node.store.confirmation_height_get (transaction, nano::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 ());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,12 +34,12 @@ TEST (confirmation_solicitor, batches)
|
|||
nano::lock_guard<std::mutex> guard (node2.active.mutex);
|
||||
for (size_t i (0); i < nano::network::confirm_req_hashes_max; ++i)
|
||||
{
|
||||
auto election (std::make_shared<nano::election> (node2, send, nullptr, false));
|
||||
auto election (std::make_shared<nano::election> (node2, send, nullptr, false, nano::election_behavior::normal));
|
||||
ASSERT_FALSE (solicitor.add (*election));
|
||||
}
|
||||
ASSERT_EQ (1, solicitor.max_confirm_req_batches);
|
||||
// Reached the maximum amount of requests for the channel
|
||||
auto election (std::make_shared<nano::election> (node2, send, nullptr, false));
|
||||
auto election (std::make_shared<nano::election> (node2, send, nullptr, false, nano::election_behavior::normal));
|
||||
ASSERT_TRUE (solicitor.add (*election));
|
||||
// Broadcasting should be immediate
|
||||
ASSERT_EQ (0, node2.stats.count (nano::stat::type::message, nano::stat::detail::publish, nano::stat::dir::out));
|
||||
|
@ -75,7 +75,7 @@ TEST (confirmation_solicitor, different_hash)
|
|||
send->sideband_set ({});
|
||||
{
|
||||
nano::lock_guard<std::mutex> guard (node2.active.mutex);
|
||||
auto election (std::make_shared<nano::election> (node2, send, nullptr, false));
|
||||
auto election (std::make_shared<nano::election> (node2, send, nullptr, false, nano::election_behavior::normal));
|
||||
// Add a vote for something else, not the winner
|
||||
election->last_votes[representative.account] = { std::chrono::steady_clock::now (), 1, 1 };
|
||||
// Ensure the request and broadcast goes through
|
||||
|
@ -114,7 +114,7 @@ TEST (confirmation_solicitor, bypass_max_requests_cap)
|
|||
send->sideband_set ({});
|
||||
{
|
||||
nano::lock_guard<std::mutex> guard (node2.active.mutex);
|
||||
auto election (std::make_shared<nano::election> (node2, send, nullptr, false));
|
||||
auto election (std::make_shared<nano::election> (node2, send, nullptr, false, nano::election_behavior::normal));
|
||||
// Add a vote for something else, not the winner
|
||||
for (auto const & rep : representatives)
|
||||
{
|
||||
|
@ -130,7 +130,7 @@ TEST (confirmation_solicitor, bypass_max_requests_cap)
|
|||
solicitor.prepare (representatives);
|
||||
{
|
||||
nano::lock_guard<std::mutex> guard (node2.active.mutex);
|
||||
auto election (std::make_shared<nano::election> (node2, send, nullptr, false));
|
||||
auto election (std::make_shared<nano::election> (node2, send, nullptr, false, nano::election_behavior::normal));
|
||||
// Erase all votes
|
||||
election->last_votes.clear ();
|
||||
ASSERT_FALSE (solicitor.add (*election));
|
||||
|
|
|
@ -130,7 +130,7 @@ TEST (frontiers_confirmation, prioritize_frontiers)
|
|||
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::genesis_account, key4.pub, key1.pub, key2.pub }));
|
||||
uint64_t election_count = 0;
|
||||
node->active.confirm_prioritized_frontiers (transaction);
|
||||
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);
|
||||
|
@ -143,6 +143,72 @@ TEST (frontiers_confirmation, prioritize_frontiers)
|
|||
}
|
||||
}
|
||||
|
||||
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::genesis_account);
|
||||
nano::keypair key;
|
||||
nano::send_block send (latest, key.pub, node->config.online_weight_minimum.number () + 10000, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (latest));
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send).code);
|
||||
nano::open_block open (send.hash (), nano::genesis_account, key.pub, key.prv, key.pub, *system.work.generate (key.pub));
|
||||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, open).code);
|
||||
}
|
||||
|
||||
{
|
||||
nano::unique_lock<std::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<std::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)
|
||||
{
|
||||
nano::genesis genesis;
|
||||
|
@ -190,4 +256,3 @@ TEST (frontiers_confirmation, mode)
|
|||
ASSERT_EQ (0, node->active.size ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4205,14 +4205,16 @@ TEST (node, dependency_graph)
|
|||
ASSERT_TIMELY (5s, node.active.empty ());
|
||||
}
|
||||
|
||||
// Confirm a complex dependency graph starting from a frontier
|
||||
TEST (node, DISABLED_dependency_graph_frontier)
|
||||
// Confirm a complex dependency graph. Uses frontiers confirmation which will fail to
|
||||
// confirm a frontier optimistically then fallback to pessimistic confirmation.
|
||||
TEST (node, dependency_graph_frontier)
|
||||
{
|
||||
nano::system system;
|
||||
nano::node_config config (nano::get_available_port (), system.logging);
|
||||
config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto & node1 = *system.add_node (config);
|
||||
config.peering_port = nano::get_available_port ();
|
||||
config.frontiers_confirmation = nano::frontiers_confirmation_mode::always;
|
||||
auto & node2 = *system.add_node (config);
|
||||
|
||||
nano::state_block_builder builder;
|
||||
|
@ -4360,23 +4362,14 @@ TEST (node, DISABLED_dependency_graph_frontier)
|
|||
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *key3_epoch).code);
|
||||
}
|
||||
|
||||
ASSERT_TRUE (node1.active.empty () && node2.active.empty ());
|
||||
|
||||
// node1 can vote, but only on the first block
|
||||
system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv);
|
||||
|
||||
// activate the graph frontier
|
||||
// node2 activates dependencies in sequence until it reaches the first block
|
||||
node2.block_confirm (node2.block (key3_epoch->hash ()));
|
||||
|
||||
// Eventually the first block in the graph gets activated and confirmed via node1
|
||||
ASSERT_TIMELY (15s, node2.block_confirmed (gen_send1->hash ()));
|
||||
|
||||
// Activate the first block in node1, allowing it to confirm all blocks for both nodes
|
||||
ASSERT_TIMELY (10s, node2.active.active (gen_send1->qualified_root ()));
|
||||
node1.block_confirm (gen_send1);
|
||||
|
||||
ASSERT_TIMELY (15s, node1.ledger.cache.cemented_count == node1.ledger.cache.block_count);
|
||||
ASSERT_TIMELY (5s, node2.ledger.cache.cemented_count == node2.ledger.cache.block_count);
|
||||
ASSERT_TIMELY (5s, node1.active.empty () && node2.active.empty ());
|
||||
ASSERT_TIMELY (15s, node2.ledger.cache.cemented_count == node2.ledger.cache.block_count);
|
||||
}
|
||||
|
||||
namespace nano
|
||||
|
|
|
@ -16,6 +16,8 @@ using namespace std::chrono;
|
|||
|
||||
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) :
|
||||
recently_dropped (node_a.stats),
|
||||
confirmation_height_processor (confirmation_height_processor_a),
|
||||
|
@ -50,9 +52,38 @@ nano::active_transactions::~active_transactions ()
|
|||
stop ();
|
||||
}
|
||||
|
||||
void nano::active_transactions::confirm_prioritized_frontiers (nano::transaction const & transaction_a)
|
||||
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::lock_guard<std::mutex> guard (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 (block_a, previous_balance_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
|
||||
nano::frontiers_confirmation_info frontiers_confirmation_info;
|
||||
auto rep_counts (node.wallets.reps ());
|
||||
bool representative (node.config.enable_voting && rep_counts.voting > 0);
|
||||
bool half_princpal_representative (representative && rep_counts.half_principal > 0);
|
||||
|
@ -73,15 +104,36 @@ void nano::active_transactions::confirm_prioritized_frontiers (nano::transaction
|
|||
{
|
||||
max_elections = max_active - roots_size;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
max_elections = 0;
|
||||
}
|
||||
|
||||
size_t elections_count (0);
|
||||
nano::unique_lock<std::mutex> lk (mutex);
|
||||
auto start_elections_for_prioritized_frontiers = [&transaction_a, &elections_count, max_elections, &lk, this](prioritize_num_uncemented & cementable_frontiers) {
|
||||
while (!cementable_frontiers.empty () && !this->stopped && elections_count < max_elections)
|
||||
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<std::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)
|
||||
{
|
||||
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);
|
||||
lk.unlock ();
|
||||
nano::account_info info;
|
||||
auto error = this->node.store.account_get (transaction_a, cementable_account.account, info);
|
||||
|
@ -97,33 +149,21 @@ void nano::active_transactions::confirm_prioritized_frontiers (nano::transaction
|
|||
{
|
||||
auto block (this->node.store.block_get (transaction_a, info.head));
|
||||
auto previous_balance (this->node.ledger.balance (transaction_a, block->previous ()));
|
||||
lk.lock ();
|
||||
if (roots.get<tag_root> ().find (block->qualified_root ()) == roots.get<tag_root> ().end ())
|
||||
auto inserted_election = this->insert_election_from_frontiers_confirmation (block, cementable_account.account, previous_balance, nano::election_behavior::optimistic);
|
||||
if (inserted_election)
|
||||
{
|
||||
auto insert_result = this->insert_impl (block, previous_balance);
|
||||
if (insert_result.inserted)
|
||||
{
|
||||
insert_result.election->transition_active ();
|
||||
++elections_count;
|
||||
}
|
||||
++elections_count_a;
|
||||
}
|
||||
lk.unlock ();
|
||||
}
|
||||
}
|
||||
}
|
||||
lk.lock ();
|
||||
}
|
||||
};
|
||||
start_elections_for_prioritized_frontiers (priority_cementable_frontiers);
|
||||
start_elections_for_prioritized_frontiers (priority_wallet_cementable_frontiers);
|
||||
}
|
||||
};
|
||||
|
||||
auto request_interval (std::chrono::milliseconds (node.network_params.network.request_interval_ms));
|
||||
auto rel_time_next_frontier_check = request_interval * (agressive_mode ? 20 : 60);
|
||||
// Decrease check time for dev network
|
||||
int dev_network_factor = is_dev_network ? 1000 : 1;
|
||||
|
||||
next_frontier_check = steady_clock::now () + (rel_time_next_frontier_check / dev_network_factor);
|
||||
}
|
||||
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)
|
||||
|
@ -276,6 +316,15 @@ void nano::active_transactions::request_confirm (nano::unique_lock<std::mutex> &
|
|||
bool const overflow_l (unconfirmed_count_l > node.config.active_elections_size && election_l->election_start < election_ttl_cutoff_l && !node.wallets.watcher->is_watched (i->root));
|
||||
if (overflow_l || election_l->transition_time (solicitor))
|
||||
{
|
||||
if (election_l->optimistic () && election_l->failed ())
|
||||
{
|
||||
if (election_l->confirmation_request_count != 0)
|
||||
{
|
||||
add_expired_optimistic_election (*election_l);
|
||||
}
|
||||
--optimistic_elections_count;
|
||||
}
|
||||
|
||||
election_l->cleanup ();
|
||||
i = sorted_roots_l.erase (i);
|
||||
}
|
||||
|
@ -300,30 +349,156 @@ void nano::active_transactions::request_confirm (nano::unique_lock<std::mutex> &
|
|||
}
|
||||
}
|
||||
|
||||
void nano::active_transactions::add_expired_optimistic_election (nano::election const & election_a)
|
||||
{
|
||||
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<std::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.latest_begin (transaction_a, next_frontier_account) };
|
||||
auto n{ node.store.latest_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
|
||||
{
|
||||
/*
|
||||
* Confirm frontiers when there aren't many confirmations already pending and node finished initial bootstrap
|
||||
*/
|
||||
*/
|
||||
auto pending_confirmation_height_size (confirmation_height_processor.awaiting_processing_size ());
|
||||
auto bootstrap_weight_reached (node.ledger.cache.block_count >= node.ledger.bootstrap_weight_max_blocks);
|
||||
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;
|
||||
if (!disabled_confirmation_mode && bootstrap_weight_reached && !conf_height_capacity_reached && !all_cemented)
|
||||
{
|
||||
// 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_spent_prioritizing_ledger_accounts = request_interval / (low_active ? 20 : 100);
|
||||
auto time_spent_prioritizing_wallet_accounts = request_interval / 250;
|
||||
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_spent_prioritizing_ledger_accounts, time_spent_prioritizing_wallet_accounts);
|
||||
confirm_prioritized_frontiers (transaction);
|
||||
lock_a.lock ();
|
||||
}
|
||||
return (!disabled_confirmation_mode && bootstrap_weight_reached && !conf_height_capacity_reached && !all_cemented);
|
||||
}
|
||||
|
||||
void nano::active_transactions::request_loop ()
|
||||
|
@ -352,7 +527,10 @@ void nano::active_transactions::request_loop ()
|
|||
const auto stamp_l = std::chrono::steady_clock::now ();
|
||||
|
||||
// frontiers_confirmation should be above update_active_multiplier to ensure new sorted roots are updated
|
||||
frontiers_confirmation (lock);
|
||||
if (should_do_frontiers_confirmation ())
|
||||
{
|
||||
frontiers_confirmation (lock);
|
||||
}
|
||||
update_active_multiplier (lock);
|
||||
request_confirm (lock);
|
||||
|
||||
|
@ -365,11 +543,12 @@ void nano::active_transactions::request_loop ()
|
|||
}
|
||||
}
|
||||
|
||||
void nano::active_transactions::prioritize_account_for_confirmation (nano::active_transactions::prioritize_num_uncemented & cementable_frontiers_a, size_t & cementable_frontiers_size_a, nano::account const & account_a, nano::account_info const & info_a, uint64_t confirmation_height)
|
||||
bool nano::active_transactions::prioritize_account_for_confirmation (nano::active_transactions::prioritize_num_uncemented & cementable_frontiers_a, size_t & cementable_frontiers_size_a, nano::account const & account_a, nano::account_info const & info_a, uint64_t confirmation_height_a)
|
||||
{
|
||||
if (info_a.block_count > confirmation_height && !confirmation_height_processor.is_processing_block (info_a.head))
|
||||
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;
|
||||
auto num_uncemented = info_a.block_count - confirmation_height_a;
|
||||
nano::lock_guard<std::mutex> guard (mutex);
|
||||
auto it = cementable_frontiers_a.get<tag_account> ().find (account_a);
|
||||
if (it != cementable_frontiers_a.get<tag_account> ().end ())
|
||||
|
@ -399,11 +578,13 @@ void nano::active_transactions::prioritize_account_for_confirmation (nano::activ
|
|||
}
|
||||
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)
|
||||
|
@ -418,8 +599,18 @@ void nano::active_transactions::prioritize_frontiers_for_confirmation (nano::tra
|
|||
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;
|
||||
wallet_account_timer.start ();
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -432,7 +623,7 @@ void nano::active_transactions::prioritize_frontiers_for_confirmation (nano::tra
|
|||
{
|
||||
skip_wallets = true;
|
||||
}
|
||||
for (auto item_it = items.cbegin (); item_it != items.cend (); ++item_it)
|
||||
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 ())
|
||||
|
@ -449,10 +640,10 @@ void nano::active_transactions::prioritize_frontiers_for_confirmation (nano::tra
|
|||
auto i (wallet->store.begin (wallet_transaction, next_wallet_frontier_account));
|
||||
auto n (wallet->store.end ());
|
||||
nano::confirmation_height_info confirmation_height_info;
|
||||
for (; i != n; ++i)
|
||||
for (; i != n && should_iterate (); ++i)
|
||||
{
|
||||
auto const & account (i->first);
|
||||
if (!node.store.account_get (transaction_a, account, info) && !node.store.confirmation_height_get (transaction_a, account, confirmation_height_info))
|
||||
if (expired_optimistic_election_infos.get<tag_account> ().count (account) == 0 && !node.store.account_get (transaction_a, account, 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);
|
||||
|
@ -463,7 +654,11 @@ void nano::active_transactions::prioritize_frontiers_for_confirmation (nano::tra
|
|||
priority_cementable_frontiers_size = priority_cementable_frontiers.size ();
|
||||
}
|
||||
|
||||
prioritize_account_for_confirmation (priority_wallet_cementable_frontiers, priority_wallet_cementable_frontiers_size, account, info, confirmation_height_info.height);
|
||||
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)
|
||||
{
|
||||
|
@ -489,21 +684,23 @@ void nano::active_transactions::prioritize_frontiers_for_confirmation (nano::tra
|
|||
}
|
||||
}
|
||||
|
||||
nano::timer<std::chrono::milliseconds> timer;
|
||||
timer.start ();
|
||||
|
||||
nano::timer<std::chrono::milliseconds> timer (nano::timer_state::started);
|
||||
auto i (node.store.latest_begin (transaction_a, next_frontier_account));
|
||||
auto n (node.store.latest_end ());
|
||||
nano::confirmation_height_info confirmation_height_info;
|
||||
for (; i != n && !stopped; ++i)
|
||||
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 (!node.store.confirmation_height_get (transaction_a, account, confirmation_height_info))
|
||||
if (expired_optimistic_election_infos.get<tag_account> ().count (account) == 0 && !node.store.confirmation_height_get (transaction_a, account, confirmation_height_info))
|
||||
{
|
||||
prioritize_account_for_confirmation (priority_cementable_frontiers, priority_cementable_frontiers_size, account, info, confirmation_height_info.height);
|
||||
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;
|
||||
|
@ -541,7 +738,7 @@ void nano::active_transactions::stop ()
|
|||
roots.clear ();
|
||||
}
|
||||
|
||||
nano::election_insertion_result nano::active_transactions::insert_impl (std::shared_ptr<nano::block> const & block_a, boost::optional<nano::uint128_t> const & previous_balance_a, std::function<void(std::shared_ptr<nano::block>)> const & confirmation_action_a)
|
||||
nano::election_insertion_result nano::active_transactions::insert_impl (std::shared_ptr<nano::block> const & block_a, boost::optional<nano::uint128_t> const & previous_balance_a, nano::election_behavior election_behavior_a, std::function<void(std::shared_ptr<nano::block>)> const & confirmation_action_a)
|
||||
{
|
||||
debug_assert (block_a->has_sideband ());
|
||||
nano::election_insertion_result result;
|
||||
|
@ -568,7 +765,7 @@ nano::election_insertion_result nano::active_transactions::insert_impl (std::sha
|
|||
}
|
||||
double multiplier (normalized_multiplier (*block_a));
|
||||
bool prioritized = roots.size () < prioritized_cutoff || multiplier > last_prioritized_multiplier.value_or (0);
|
||||
result.election = nano::make_shared<nano::election> (node, block_a, confirmation_action_a, prioritized);
|
||||
result.election = nano::make_shared<nano::election> (node, block_a, confirmation_action_a, prioritized, election_behavior_a);
|
||||
roots.get<tag_root> ().emplace (nano::active_transactions::conflict_info{ root, multiplier, result.election, epoch, previous_balance });
|
||||
blocks.emplace (hash, result.election);
|
||||
result.election->insert_inactive_votes_cache (hash);
|
||||
|
@ -590,10 +787,10 @@ nano::election_insertion_result nano::active_transactions::insert_impl (std::sha
|
|||
return result;
|
||||
}
|
||||
|
||||
nano::election_insertion_result nano::active_transactions::insert (std::shared_ptr<nano::block> const & block_a, boost::optional<nano::uint128_t> const & previous_balance_a, std::function<void(std::shared_ptr<nano::block>)> const & confirmation_action_a)
|
||||
nano::election_insertion_result nano::active_transactions::insert (std::shared_ptr<nano::block> const & block_a, boost::optional<nano::uint128_t> const & previous_balance_a, nano::election_behavior election_behavior_a, std::function<void(std::shared_ptr<nano::block>)> const & confirmation_action_a)
|
||||
{
|
||||
nano::lock_guard<std::mutex> lock (mutex);
|
||||
return insert_impl (block_a, previous_balance_a, confirmation_action_a);
|
||||
return insert_impl (block_a, previous_balance_a, election_behavior_a, confirmation_action_a);
|
||||
}
|
||||
|
||||
// Validate a vote and apply it to the current election if one exists
|
||||
|
@ -1172,7 +1369,10 @@ nano::inactive_cache_status nano::active_transactions::inactive_votes_bootstrap_
|
|||
auto block = node.store.block_get (transaction, hash_a);
|
||||
if (block && status.election_started && !previously_a.election_started && !node.block_confirmed_or_being_confirmed (transaction, hash_a))
|
||||
{
|
||||
insert_impl (block);
|
||||
if (node.ledger.cache.cemented_count >= node.ledger.bootstrap_weight_max_blocks)
|
||||
{
|
||||
insert_impl (block);
|
||||
}
|
||||
}
|
||||
else if (!block && status.bootstrap_started && !previously_a.bootstrap_started)
|
||||
{
|
||||
|
@ -1193,6 +1393,17 @@ account (account_a), blocks_uncemented (blocks_uncemented_a)
|
|||
{
|
||||
}
|
||||
|
||||
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, const std::string & name)
|
||||
{
|
||||
size_t roots_count;
|
||||
|
@ -1214,9 +1425,11 @@ 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{ "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{ "priority_wallet_cementable_frontiers_count", 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_count", active_transactions.priority_cementable_frontiers_size (), sizeof (nano::cementable_account) }));
|
||||
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "inactive_votes_cache_count", active_transactions.inactive_votes_cache_size (), sizeof (nano::gap_information) }));
|
||||
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{ "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"));
|
||||
return composite;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/lib/numbers.hpp>
|
||||
#include <nano/node/election.hpp>
|
||||
#include <nano/node/voting.hpp>
|
||||
#include <nano/secure/common.hpp>
|
||||
|
||||
|
@ -74,6 +75,25 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
size_t max_elections{ 0 };
|
||||
bool aggressive_mode{ false };
|
||||
};
|
||||
|
||||
class dropped_elections final
|
||||
{
|
||||
public:
|
||||
|
@ -132,6 +152,8 @@ class active_transactions final
|
|||
class tag_uncemented {};
|
||||
class tag_arrival {};
|
||||
class tag_hash {};
|
||||
class tag_expired_time {};
|
||||
class tag_election_started {};
|
||||
// clang-format on
|
||||
|
||||
public:
|
||||
|
@ -152,7 +174,7 @@ public:
|
|||
// Start an election for a block
|
||||
// Call action with confirmed block, may be different than what we started with
|
||||
// clang-format off
|
||||
nano::election_insertion_result insert (std::shared_ptr<nano::block> const &, boost::optional<nano::uint128_t> const & = boost::none, std::function<void(std::shared_ptr<nano::block>)> const & = [](std::shared_ptr<nano::block>) {});
|
||||
nano::election_insertion_result insert (std::shared_ptr<nano::block> const &, boost::optional<nano::uint128_t> const & = boost::none, nano::election_behavior = nano::election_behavior::normal, std::function<void(std::shared_ptr<nano::block>)> const & = nullptr);
|
||||
// clang-format on
|
||||
// Distinguishes replay votes, cannot be determined if the block is not in any election
|
||||
nano::vote_code vote (std::shared_ptr<nano::vote>);
|
||||
|
@ -216,7 +238,7 @@ private:
|
|||
|
||||
// Call action with confirmed block, may be different than what we started with
|
||||
// clang-format off
|
||||
nano::election_insertion_result insert_impl (std::shared_ptr<nano::block> const &, boost::optional<nano::uint128_t> const & = boost::none, std::function<void(std::shared_ptr<nano::block>)> const & = [](std::shared_ptr<nano::block>) {});
|
||||
nano::election_insertion_result insert_impl (std::shared_ptr<nano::block> const &, boost::optional<nano::uint128_t> const & = boost::none, nano::election_behavior = nano::election_behavior::normal, std::function<void(std::shared_ptr<nano::block>)> const & = nullptr);
|
||||
// clang-format on
|
||||
// Returns false if the election difficulty was updated
|
||||
bool update_difficulty_impl (roots_iterator const &, nano::block const &);
|
||||
|
@ -254,23 +276,43 @@ private:
|
|||
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
|
||||
std::atomic<uint64_t> expired_optimistic_election_infos_size{ 0 };
|
||||
|
||||
// Frontiers confirmation
|
||||
void confirm_prioritized_frontiers (nano::transaction const &);
|
||||
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<std::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{ 0 };
|
||||
std::chrono::steady_clock::time_point next_frontier_check{ std::chrono::steady_clock::now () };
|
||||
constexpr static size_t max_active_elections_frontier_insertion{ 1000 };
|
||||
prioritize_num_uncemented priority_wallet_cementable_frontiers;
|
||||
prioritize_num_uncemented priority_cementable_frontiers;
|
||||
void prioritize_frontiers_for_confirmation (nano::transaction const &, std::chrono::milliseconds, std::chrono::milliseconds);
|
||||
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 };
|
||||
void prioritize_account_for_confirmation (prioritize_num_uncemented &, size_t &, nano::account const &, nano::account_info const &, uint64_t);
|
||||
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 &, 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 size_t constexpr max_priority_cementable_frontiers{ 100000 };
|
||||
static size_t constexpr confirmed_frontiers_max_pending_size{ 10000 };
|
||||
static std::chrono::minutes constexpr expired_optimistic_election_info_cutoff{ 30 };
|
||||
// clang-format off
|
||||
using ordered_cache = boost::multi_index_container<nano::inactive_cache_information,
|
||||
mi::indexed_by<
|
||||
|
@ -289,9 +331,12 @@ private:
|
|||
friend class active_transactions_dropped_cleanup_dev;
|
||||
friend class active_transactions_vote_replays_Test;
|
||||
friend class frontiers_confirmation_prioritize_frontiers_Test;
|
||||
friend class frontiers_confirmation_prioritize_frontiers_max_optimistic_elections_Test;
|
||||
friend class confirmation_height_prioritize_frontiers_overwrite_Test;
|
||||
friend class active_transactions_confirmation_consistency_Test;
|
||||
friend class node_deferred_dependent_elections_Test;
|
||||
friend class active_transactions_pessimistic_elections_Test;
|
||||
friend class frontiers_confirmation_expired_optimistic_elections_removal_Test;
|
||||
};
|
||||
|
||||
std::unique_ptr<container_info_component> collect_container_info (active_transactions & active_transactions, const std::string & name);
|
||||
|
|
|
@ -94,6 +94,7 @@ private:
|
|||
friend class confirmation_height_long_chains_Test;
|
||||
friend class confirmation_height_many_accounts_single_confirmation_Test;
|
||||
friend class request_aggregator_cannot_vote_Test;
|
||||
friend class active_transactions_pessimistic_elections_Test;
|
||||
};
|
||||
|
||||
std::unique_ptr<container_info_component> collect_container_info (confirmation_height_processor &, const std::string &);
|
||||
|
|
|
@ -22,9 +22,10 @@ nano::election_vote_result::election_vote_result (bool replay_a, bool processed_
|
|||
processed = processed_a;
|
||||
}
|
||||
|
||||
nano::election::election (nano::node & node_a, std::shared_ptr<nano::block> block_a, std::function<void(std::shared_ptr<nano::block>)> const & confirmation_action_a, bool prioritized_a) :
|
||||
nano::election::election (nano::node & node_a, std::shared_ptr<nano::block> block_a, std::function<void(std::shared_ptr<nano::block>)> const & confirmation_action_a, bool prioritized_a, nano::election_behavior election_behavior_a) :
|
||||
confirmation_action (confirmation_action_a),
|
||||
prioritized_m (prioritized_a),
|
||||
election_behavior (election_behavior_a),
|
||||
node (node_a),
|
||||
status ({ block_a, 0, std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::system_clock::now ().time_since_epoch ()), std::chrono::duration_values<std::chrono::milliseconds>::zero (), 0, 1, 0, nano::election_status_type::ongoing }),
|
||||
height (block_a->sideband ().height),
|
||||
|
@ -52,9 +53,12 @@ void nano::election::confirm_once (nano::election_status_type type_a)
|
|||
auto confirmation_action_l (confirmation_action);
|
||||
node.active.election_winner_details.emplace (status.winner->hash (), shared_from_this ());
|
||||
node.active.add_recently_confirmed (status_l.winner->qualified_root (), status_l.winner->hash ());
|
||||
node_l->process_confirmed (status_l);
|
||||
node.process_confirmed (status_l);
|
||||
node.background ([node_l, status_l, confirmation_action_l]() {
|
||||
confirmation_action_l (status_l.winner);
|
||||
if (confirmation_action_l)
|
||||
{
|
||||
confirmation_action_l (status_l.winner);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -137,7 +141,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)
|
||||
{
|
||||
if (base_latency () * 5 < std::chrono::steady_clock::now () - last_req)
|
||||
if ((base_latency () * (optimistic () ? 10 : 5)) < (std::chrono::steady_clock::now () - last_req))
|
||||
{
|
||||
if (!solicitor_a.add (*this))
|
||||
{
|
||||
|
@ -163,6 +167,11 @@ bool nano::election::confirmed () const
|
|||
return state_m == nano::election::state_t::confirmed || state_m == nano::election::state_t::expired_confirmed;
|
||||
}
|
||||
|
||||
bool nano::election::failed () const
|
||||
{
|
||||
return state_m == nano::election::state_t::expired_unconfirmed;
|
||||
}
|
||||
|
||||
void nano::election::broadcast_block (nano::confirmation_solicitor & solicitor_a)
|
||||
{
|
||||
if (base_latency () * 15 < std::chrono::steady_clock::now () - last_block)
|
||||
|
@ -210,7 +219,9 @@ bool nano::election::transition_time (nano::confirmation_solicitor & solicitor_a
|
|||
debug_assert (false);
|
||||
break;
|
||||
}
|
||||
if (!confirmed () && std::chrono::minutes (5) < std::chrono::steady_clock::now () - election_start)
|
||||
auto optimistic_expiration_time = node.network_params.network.is_dev_network () ? 500 : 60 * 1000;
|
||||
auto expire_time = std::chrono::milliseconds (optimistic () ? optimistic_expiration_time : 5 * 60 * 1000);
|
||||
if (!confirmed () && expire_time < std::chrono::steady_clock::now () - election_start)
|
||||
{
|
||||
result = true;
|
||||
state_change (state_m.load (), nano::election::state_t::expired_unconfirmed);
|
||||
|
@ -465,6 +476,11 @@ bool nano::election::prioritized () const
|
|||
return prioritized_m;
|
||||
}
|
||||
|
||||
bool nano::election::optimistic () const
|
||||
{
|
||||
return election_behavior == nano::election_behavior::optimistic;
|
||||
}
|
||||
|
||||
void nano::election::prioritize_election (nano::vote_generator_session & generator_session_a)
|
||||
{
|
||||
debug_assert (!node.active.mutex.try_lock ());
|
||||
|
|
|
@ -30,6 +30,12 @@ public:
|
|||
bool replay{ false };
|
||||
bool processed{ false };
|
||||
};
|
||||
enum class election_behavior
|
||||
{
|
||||
normal,
|
||||
optimistic
|
||||
};
|
||||
|
||||
class election final : public std::enable_shared_from_this<nano::election>
|
||||
{
|
||||
// Minimum time between broadcasts of the current winner of an election, as a backup to requesting confirmations
|
||||
|
@ -67,7 +73,7 @@ private: // State management
|
|||
std::atomic<bool> prioritized_m = { false };
|
||||
|
||||
public:
|
||||
election (nano::node &, std::shared_ptr<nano::block>, std::function<void(std::shared_ptr<nano::block>)> const &, bool);
|
||||
election (nano::node &, std::shared_ptr<nano::block>, std::function<void(std::shared_ptr<nano::block>)> const &, bool, nano::election_behavior);
|
||||
nano::election_vote_result vote (nano::account, uint64_t, nano::block_hash);
|
||||
nano::tally_t tally ();
|
||||
// Check if we have vote quorum
|
||||
|
@ -80,6 +86,7 @@ public:
|
|||
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 ();
|
||||
|
@ -93,6 +100,8 @@ private:
|
|||
|
||||
public:
|
||||
bool confirmed () const;
|
||||
bool failed () const;
|
||||
nano::election_behavior election_behavior{ nano::election_behavior::normal };
|
||||
nano::node & node;
|
||||
std::unordered_map<nano::account, nano::vote_info> last_votes;
|
||||
std::unordered_map<nano::block_hash, std::shared_ptr<nano::block>> blocks;
|
||||
|
|
|
@ -526,7 +526,7 @@ void nano::node::process_fork (nano::transaction const & transaction_a, std::sha
|
|||
if (ledger_block && !block_confirmed_or_being_confirmed (transaction_a, ledger_block->hash ()) && (ledger.dependents_confirmed (transaction_a, *ledger_block) || modified_a < nano::seconds_since_epoch () - 300 || !block_arrival.recent (block_a->hash ())))
|
||||
{
|
||||
std::weak_ptr<nano::node> this_w (shared_from_this ());
|
||||
auto election = active.insert (ledger_block, boost::none, [this_w, root, root_block_type = block_a->type ()](std::shared_ptr<nano::block>) {
|
||||
auto election = active.insert (ledger_block, boost::none, nano::election_behavior::normal, [this_w, root, root_block_type = block_a->type ()](std::shared_ptr<nano::block>) {
|
||||
if (auto this_l = this_w.lock ())
|
||||
{
|
||||
auto attempt (this_l->bootstrap_initiator.current_attempt ());
|
||||
|
|
|
@ -1202,18 +1202,6 @@ bool nano::ledger::block_confirmed (nano::transaction const & transaction_a, nan
|
|||
return confirmed;
|
||||
}
|
||||
|
||||
bool nano::ledger::block_not_confirmed_or_not_exists (nano::block const & block_a) const
|
||||
{
|
||||
bool result (true);
|
||||
auto hash (block_a.hash ());
|
||||
auto transaction (store.tx_begin_read ());
|
||||
if (store.block_exists (transaction, hash))
|
||||
{
|
||||
result = !block_confirmed (transaction, hash);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<nano::container_info_component> nano::collect_container_info (ledger & ledger, const std::string & name)
|
||||
{
|
||||
auto count = ledger.bootstrap_weights_size.load ();
|
||||
|
|
|
@ -26,7 +26,6 @@ public:
|
|||
std::shared_ptr<nano::block> successor (nano::transaction const &, nano::qualified_root const &);
|
||||
std::shared_ptr<nano::block> forked_block (nano::transaction const &, nano::block const &);
|
||||
bool block_confirmed (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const;
|
||||
bool block_not_confirmed_or_not_exists (nano::block const & block_a) const;
|
||||
nano::block_hash latest (nano::transaction const &, nano::account const &);
|
||||
nano::root latest_root (nano::transaction const &, nano::account const &);
|
||||
nano::block_hash representative (nano::transaction const &, nano::block_hash const &);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue