Delay voting for non-priority elections under saturation (#2666)

* Delay voting for non-priority elections under saturation

This is part of https://github.com/nanocurrency/nano-node/pull/2440 . Using a similar strategy of saving the last prioritized difficulty during `update_active_difficulty()`, the result of inserting an election now includes another boolean, hinting that the election may not be a priority due to a large number of active elections and low difficulty of the inserted block.

Active difficulty is now calculated only from these prioritized elections. The cutoff is defined at 10% of thhe `active_elections_size` config which is now 50k. During `request_confirm`, the top elections are prioritized.

When an election is prioritized, a vote is generated for the current winner.

* Make sure to have at least one element when updating last_prioritized_difficulty
This commit is contained in:
Guilherme Lawless 2020-03-23 10:38:31 +00:00 committed by GitHub
commit 2b658acb4b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 182 additions and 103 deletions

View file

@ -797,7 +797,7 @@ TEST (active_transactions, dropped_cleanup)
ASSERT_FALSE (node.network.publish_filter.apply (block_bytes.data (), block_bytes.size ()));
ASSERT_TRUE (node.network.publish_filter.apply (block_bytes.data (), block_bytes.size ()));
auto election (node.active.insert (block).first);
auto election (node.active.insert (block).election);
ASSERT_NE (nullptr, election);
// Not yet removed
@ -843,7 +843,7 @@ TEST (active_transactions, confirmation_consistency)
system.deadline_set (5s);
while (!node.ledger.block_confirmed (node.store.tx_begin_read (), block->hash ()))
{
ASSERT_FALSE (node.active.insert (block).second);
ASSERT_FALSE (node.active.insert (block).inserted);
ASSERT_NO_ERROR (system.poll (5ms));
}
nano::lock_guard<std::mutex> guard (node.active.mutex);
@ -852,4 +852,45 @@ TEST (active_transactions, confirmation_consistency)
ASSERT_EQ (i + 1, node.active.recently_cemented.size ());
}
}
}
}
TEST (active_transactions, insertion_prioritization)
{
nano::system system;
nano::node_config node_config (nano::get_available_port (), system.logging);
// 10% of elections (1) are prioritized
node_config.active_elections_size = 10;
nano::node_flags node_flags;
node_flags.disable_request_loop = true;
auto & node = *system.add_node (node_config, node_flags);
auto send1 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, nano::genesis_hash, nano::test_genesis_key.pub, nano::genesis_amount - 10 * nano::xrb_ratio, nano::public_key (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (nano::genesis_hash)));
auto send2 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, send1->hash (), nano::test_genesis_key.pub, nano::genesis_amount - 20 * nano::xrb_ratio, nano::public_key (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send1->hash ())));
auto send3 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, send2->hash (), nano::test_genesis_key.pub, nano::genesis_amount - 30 * nano::xrb_ratio, nano::public_key (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send2->hash ())));
auto send4 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, send3->hash (), nano::test_genesis_key.pub, nano::genesis_amount - 40 * nano::xrb_ratio, nano::public_key (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send3->hash ())));
auto send5 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, send4->hash (), nano::test_genesis_key.pub, nano::genesis_amount - 50 * nano::xrb_ratio, nano::public_key (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send4->hash ())));
auto send6 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, send5->hash (), nano::test_genesis_key.pub, nano::genesis_amount - 60 * nano::xrb_ratio, nano::public_key (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send5->hash ())));
auto send7 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, send6->hash (), nano::test_genesis_key.pub, nano::genesis_amount - 70 * nano::xrb_ratio, nano::public_key (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send6->hash ())));
// Sort by difficulty, descending
std::vector<std::shared_ptr<nano::block>> blocks{ send1, send2, send3, send4, send5, send6, send7 };
std::sort (blocks.begin (), blocks.end (), [](auto const & blockl, auto const & blockr) { return blockl->difficulty () > blockr->difficulty (); });
auto update_active_difficulty = [&node] {
nano::unique_lock<std::mutex> lock (node.active.mutex);
node.active.update_active_difficulty (lock);
};
ASSERT_TRUE (node.active.insert (blocks[2]).prioritized);
update_active_difficulty ();
ASSERT_FALSE (node.active.insert (blocks[3]).prioritized);
update_active_difficulty ();
ASSERT_TRUE (node.active.insert (blocks[1]).prioritized);
update_active_difficulty ();
ASSERT_FALSE (node.active.insert (blocks[4]).prioritized);
update_active_difficulty ();
ASSERT_TRUE (node.active.insert (blocks[0]).prioritized);
update_active_difficulty ();
ASSERT_FALSE (node.active.insert (blocks[5]).prioritized);
update_active_difficulty ();
ASSERT_FALSE (node.active.insert (blocks[6]).prioritized);
}

View file

@ -22,8 +22,8 @@ TEST (conflicts, start_stop)
ASSERT_EQ (1, node1.active.size ());
{
nano::lock_guard<std::mutex> guard (node1.active.mutex);
ASSERT_NE (nullptr, election1.first);
ASSERT_EQ (1, election1.first->last_votes.size ());
ASSERT_NE (nullptr, election1.election);
ASSERT_EQ (1, election1.election->last_votes.size ());
}
}
@ -46,9 +46,9 @@ TEST (conflicts, add_existing)
ASSERT_EQ (1, node1.active.size ());
{
nano::lock_guard<std::mutex> guard (node1.active.mutex);
ASSERT_NE (nullptr, election1.first);
ASSERT_EQ (2, election1.first->last_votes.size ());
ASSERT_NE (election1.first->last_votes.end (), election1.first->last_votes.find (key2.pub));
ASSERT_NE (nullptr, election1.election);
ASSERT_EQ (2, election1.election->last_votes.size ());
ASSERT_NE (election1.election->last_votes.end (), election1.election->last_votes.find (key2.pub));
}
}
@ -208,9 +208,9 @@ TEST (conflicts, dependency)
// Check dependency for send block
{
nano::lock_guard<std::mutex> guard (node1->active.mutex);
ASSERT_NE (nullptr, election1.first);
ASSERT_EQ (1, election1.first->dependent_blocks.size ());
ASSERT_NE (election1.first->dependent_blocks.end (), election1.first->dependent_blocks.find (state_open1->hash ()));
ASSERT_NE (nullptr, election1.election);
ASSERT_EQ (1, election1.election->dependent_blocks.size ());
ASSERT_NE (election1.election->dependent_blocks.end (), election1.election->dependent_blocks.find (state_open1->hash ()));
}
}

View file

@ -9,7 +9,7 @@ TEST (election, construction)
nano::system system (1);
nano::genesis genesis;
auto & node = *system.nodes[0];
auto election = node.active.insert (genesis.open).first;
auto election = node.active.insert (genesis.open).election;
ASSERT_TRUE (election->idle ());
election->transition_active ();
ASSERT_FALSE (election->idle ());

View file

@ -768,7 +768,7 @@ TEST (votes, check_signature)
auto election1 = node1.active.insert (send1);
{
nano::lock_guard<std::mutex> lock (node1.active.mutex);
ASSERT_EQ (1, election1.first->last_votes.size ());
ASSERT_EQ (1, election1.election->last_votes.size ());
}
auto vote1 (std::make_shared<nano::vote> (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 1, send1));
vote1->signature.bytes[0] ^= 1;
@ -790,18 +790,18 @@ TEST (votes, add_one)
ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *send1).code);
auto election1 = node1.active.insert (send1);
nano::unique_lock<std::mutex> lock (node1.active.mutex);
ASSERT_EQ (1, election1.first->last_votes.size ());
ASSERT_EQ (1, election1.election->last_votes.size ());
lock.unlock ();
auto vote1 (std::make_shared<nano::vote> (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 1, send1));
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1));
auto vote2 (std::make_shared<nano::vote> (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 2, send1));
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2));
lock.lock ();
ASSERT_EQ (2, election1.first->last_votes.size ());
auto existing1 (election1.first->last_votes.find (nano::test_genesis_key.pub));
ASSERT_NE (election1.first->last_votes.end (), existing1);
ASSERT_EQ (2, election1.election->last_votes.size ());
auto existing1 (election1.election->last_votes.find (nano::test_genesis_key.pub));
ASSERT_NE (election1.election->last_votes.end (), existing1);
ASSERT_EQ (send1->hash (), existing1->second.hash);
auto winner (*election1.first->tally ().begin ());
auto winner (*election1.election->tally ().begin ());
ASSERT_EQ (*send1, *winner.second);
ASSERT_EQ (nano::genesis_amount - 100, winner.first);
}
@ -826,12 +826,12 @@ TEST (votes, add_two)
auto vote1 (std::make_shared<nano::vote> (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 1, send1));
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1));
lock.lock ();
ASSERT_EQ (3, election1.first->last_votes.size ());
ASSERT_NE (election1.first->last_votes.end (), election1.first->last_votes.find (nano::test_genesis_key.pub));
ASSERT_EQ (send1->hash (), election1.first->last_votes[nano::test_genesis_key.pub].hash);
ASSERT_NE (election1.first->last_votes.end (), election1.first->last_votes.find (key2.pub));
ASSERT_EQ (send2->hash (), election1.first->last_votes[key2.pub].hash);
auto winner (*election1.first->tally ().begin ());
ASSERT_EQ (3, election1.election->last_votes.size ());
ASSERT_NE (election1.election->last_votes.end (), election1.election->last_votes.find (nano::test_genesis_key.pub));
ASSERT_EQ (send1->hash (), election1.election->last_votes[nano::test_genesis_key.pub].hash);
ASSERT_NE (election1.election->last_votes.end (), election1.election->last_votes.find (key2.pub));
ASSERT_EQ (send2->hash (), election1.election->last_votes[key2.pub].hash);
auto winner (*election1.election->tally ().begin ());
ASSERT_EQ (*send1, *winner.second);
}
@ -857,30 +857,30 @@ TEST (votes, add_existing)
// Block is already processed from vote
ASSERT_TRUE (node1.active.publish (send1));
nano::unique_lock<std::mutex> lock (node1.active.mutex);
ASSERT_EQ (1, election1.first->last_votes[nano::test_genesis_key.pub].sequence);
ASSERT_EQ (1, election1.election->last_votes[nano::test_genesis_key.pub].sequence);
nano::keypair key2;
auto send2 (std::make_shared<nano::send_block> (genesis.hash (), key2.pub, nano::genesis_amount - nano::Gxrb_ratio, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0));
node1.work_generate_blocking (*send2);
auto vote2 (std::make_shared<nano::vote> (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 2, send2));
// Pretend we've waited the timeout
election1.first->last_votes[nano::test_genesis_key.pub].time = std::chrono::steady_clock::now () - std::chrono::seconds (20);
election1.election->last_votes[nano::test_genesis_key.pub].time = std::chrono::steady_clock::now () - std::chrono::seconds (20);
lock.unlock ();
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2));
ASSERT_FALSE (node1.active.publish (send2));
lock.lock ();
ASSERT_EQ (2, election1.first->last_votes[nano::test_genesis_key.pub].sequence);
ASSERT_EQ (2, election1.election->last_votes[nano::test_genesis_key.pub].sequence);
// Also resend the old vote, and see if we respect the sequence number
election1.first->last_votes[nano::test_genesis_key.pub].time = std::chrono::steady_clock::now () - std::chrono::seconds (20);
election1.election->last_votes[nano::test_genesis_key.pub].time = std::chrono::steady_clock::now () - std::chrono::seconds (20);
lock.unlock ();
ASSERT_EQ (nano::vote_code::replay, node1.active.vote (vote1));
lock.lock ();
ASSERT_EQ (2, election1.first->last_votes[nano::test_genesis_key.pub].sequence);
ASSERT_EQ (2, election1.first->last_votes.size ());
ASSERT_NE (election1.first->last_votes.end (), election1.first->last_votes.find (nano::test_genesis_key.pub));
ASSERT_EQ (send2->hash (), election1.first->last_votes[nano::test_genesis_key.pub].hash);
ASSERT_EQ (2, election1.election->last_votes[nano::test_genesis_key.pub].sequence);
ASSERT_EQ (2, election1.election->last_votes.size ());
ASSERT_NE (election1.election->last_votes.end (), election1.election->last_votes.find (nano::test_genesis_key.pub));
ASSERT_EQ (send2->hash (), election1.election->last_votes[nano::test_genesis_key.pub].hash);
{
auto transaction (node1.store.tx_begin_read ());
auto winner (*election1.first->tally ().begin ());
auto winner (*election1.election->tally ().begin ());
ASSERT_EQ (*send2, *winner.second);
}
}
@ -906,14 +906,14 @@ TEST (votes, add_old)
auto vote2 (std::make_shared<nano::vote> (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 1, send2));
{
nano::lock_guard<std::mutex> lock (node1.active.mutex);
election1.first->last_votes[nano::test_genesis_key.pub].time = std::chrono::steady_clock::now () - std::chrono::seconds (20);
election1.election->last_votes[nano::test_genesis_key.pub].time = std::chrono::steady_clock::now () - std::chrono::seconds (20);
}
node1.vote_processor.vote_blocking (vote2, channel);
ASSERT_EQ (2, election1.first->last_votes_size ());
ASSERT_EQ (2, election1.election->last_votes_size ());
nano::lock_guard<std::mutex> lock (node1.active.mutex);
ASSERT_NE (election1.first->last_votes.end (), election1.first->last_votes.find (nano::test_genesis_key.pub));
ASSERT_EQ (send1->hash (), election1.first->last_votes[nano::test_genesis_key.pub].hash);
auto winner (*election1.first->tally ().begin ());
ASSERT_NE (election1.election->last_votes.end (), election1.election->last_votes.find (nano::test_genesis_key.pub));
ASSERT_EQ (send1->hash (), election1.election->last_votes[nano::test_genesis_key.pub].hash);
auto winner (*election1.election->tally ().begin ());
ASSERT_EQ (*send1, *winner.second);
}
@ -933,27 +933,27 @@ TEST (votes, add_old_different_account)
ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *send2).code);
auto election1 = node1.active.insert (send1);
auto election2 = node1.active.insert (send2);
ASSERT_EQ (1, election1.first->last_votes_size ());
ASSERT_EQ (1, election2.first->last_votes_size ());
ASSERT_EQ (1, election1.election->last_votes_size ());
ASSERT_EQ (1, election2.election->last_votes_size ());
auto vote1 (std::make_shared<nano::vote> (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 2, send1));
auto channel (std::make_shared<nano::transport::channel_udp> (node1.network.udp_channels, node1.network.endpoint (), node1.network_params.protocol.protocol_version));
auto vote_result1 (node1.vote_processor.vote_blocking (vote1, channel));
ASSERT_EQ (nano::vote_code::vote, vote_result1);
ASSERT_EQ (2, election1.first->last_votes_size ());
ASSERT_EQ (1, election2.first->last_votes_size ());
ASSERT_EQ (2, election1.election->last_votes_size ());
ASSERT_EQ (1, election2.election->last_votes_size ());
auto vote2 (std::make_shared<nano::vote> (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 1, send2));
auto vote_result2 (node1.vote_processor.vote_blocking (vote2, channel));
ASSERT_EQ (nano::vote_code::vote, vote_result2);
ASSERT_EQ (2, election1.first->last_votes_size ());
ASSERT_EQ (2, election2.first->last_votes_size ());
ASSERT_EQ (2, election1.election->last_votes_size ());
ASSERT_EQ (2, election2.election->last_votes_size ());
nano::unique_lock<std::mutex> lock (node1.active.mutex);
ASSERT_NE (election1.first->last_votes.end (), election1.first->last_votes.find (nano::test_genesis_key.pub));
ASSERT_NE (election2.first->last_votes.end (), election2.first->last_votes.find (nano::test_genesis_key.pub));
ASSERT_EQ (send1->hash (), election1.first->last_votes[nano::test_genesis_key.pub].hash);
ASSERT_EQ (send2->hash (), election2.first->last_votes[nano::test_genesis_key.pub].hash);
auto winner1 (*election1.first->tally ().begin ());
ASSERT_NE (election1.election->last_votes.end (), election1.election->last_votes.find (nano::test_genesis_key.pub));
ASSERT_NE (election2.election->last_votes.end (), election2.election->last_votes.find (nano::test_genesis_key.pub));
ASSERT_EQ (send1->hash (), election1.election->last_votes[nano::test_genesis_key.pub].hash);
ASSERT_EQ (send2->hash (), election2.election->last_votes[nano::test_genesis_key.pub].hash);
auto winner1 (*election1.election->tally ().begin ());
ASSERT_EQ (*send1, *winner1.second);
auto winner2 (*election2.first->tally ().begin ());
auto winner2 (*election2.election->tally ().begin ());
ASSERT_EQ (*send2, *winner2.second);
}
@ -978,10 +978,10 @@ TEST (votes, add_cooldown)
auto vote2 (std::make_shared<nano::vote> (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 2, send2));
node1.vote_processor.vote_blocking (vote2, channel);
nano::unique_lock<std::mutex> lock (node1.active.mutex);
ASSERT_EQ (2, election1.first->last_votes.size ());
ASSERT_NE (election1.first->last_votes.end (), election1.first->last_votes.find (nano::test_genesis_key.pub));
ASSERT_EQ (send1->hash (), election1.first->last_votes[nano::test_genesis_key.pub].hash);
auto winner (*election1.first->tally ().begin ());
ASSERT_EQ (2, election1.election->last_votes.size ());
ASSERT_NE (election1.election->last_votes.end (), election1.election->last_votes.find (nano::test_genesis_key.pub));
ASSERT_EQ (send1->hash (), election1.election->last_votes[nano::test_genesis_key.pub].hash);
auto winner (*election1.election->tally ().begin ());
ASSERT_EQ (*send1, *winner.second);
}
@ -2656,10 +2656,10 @@ TEST (ledger, block_hash_account_conflict)
auto election3 = node1.active.insert (send2);
auto election4 = node1.active.insert (open_epoch1);
nano::lock_guard<std::mutex> lock (node1.active.mutex);
auto winner1 (*election1.first->tally ().begin ());
auto winner2 (*election2.first->tally ().begin ());
auto winner3 (*election3.first->tally ().begin ());
auto winner4 (*election4.first->tally ().begin ());
auto winner1 (*election1.election->tally ().begin ());
auto winner2 (*election2.election->tally ().begin ());
auto winner3 (*election3.election->tally ().begin ());
auto winner4 (*election4.election->tally ().begin ());
ASSERT_EQ (*send1, *winner1.second);
ASSERT_EQ (*receive1, *winner2.second);
ASSERT_EQ (*send2, *winner3.second);

View file

@ -1840,12 +1840,12 @@ TEST (node, rep_self_vote)
node0->block_processor.generator.add (block0->hash ());
system.deadline_set (1s);
// Wait until representatives are activated & make vote
while (election1.first->last_votes_size () != 3)
while (election1.election->last_votes_size () != 3)
{
ASSERT_NO_ERROR (system.poll ());
}
nano::unique_lock<std::mutex> lock (active.mutex);
auto & rep_votes (election1.first->last_votes);
auto & rep_votes (election1.election->last_votes);
ASSERT_NE (rep_votes.end (), rep_votes.find (nano::test_genesis_key.pub));
ASSERT_NE (rep_votes.end (), rep_votes.find (rep_big.pub));
}

View file

@ -28,7 +28,7 @@ TEST (vote_processor, codes)
ASSERT_EQ (nano::vote_code::indeterminate, node.vote_processor.vote_blocking (vote, channel));
// First vote from an account for an ongoing election
ASSERT_TRUE (node.active.insert (genesis.open).second);
ASSERT_TRUE (node.active.insert (genesis.open).inserted);
ASSERT_EQ (nano::vote_code::vote, node.vote_processor.vote_blocking (vote, channel));
// Processing the same vote is a replay
@ -77,14 +77,14 @@ TEST (vote_processor, invalid_signature)
auto channel (std::make_shared<nano::transport::channel_udp> (node.network.udp_channels, node.network.endpoint (), node.network_params.protocol.protocol_version));
auto election (node.active.insert (genesis.open));
ASSERT_TRUE (election.first && election.second);
ASSERT_EQ (1, election.first->last_votes.size ());
ASSERT_TRUE (election.election && election.inserted);
ASSERT_EQ (1, election.election->last_votes.size ());
node.vote_processor.vote (vote_invalid, channel);
node.vote_processor.flush ();
ASSERT_EQ (1, election.first->last_votes.size ());
ASSERT_EQ (1, election.election->last_votes.size ());
node.vote_processor.vote (vote, channel);
node.vote_processor.flush ();
ASSERT_EQ (2, election.first->last_votes.size ());
ASSERT_EQ (2, election.election->last_votes.size ());
}
TEST (vote_processor, no_capacity)

View file

@ -21,6 +21,7 @@ multipliers_cb (20, 1.),
trended_active_difficulty (node_a.network_params.network.publish_threshold),
check_all_elections_period (node_a.network_params.network.is_test_network () ? 10ms : 5s),
election_time_to_live (node_a.network_params.network.is_test_network () ? 0s : 2s),
prioritized_cutoff (std::max<size_t> (1, node_a.config.active_elections_size / 10)),
thread ([this]() {
nano::thread_role::set (nano::thread_role::name::request_loop);
request_loop ();
@ -101,12 +102,12 @@ void nano::active_transactions::search_frontiers (nano::transaction const & tran
{
auto block (this->node.store.block_get (transaction_a, info.head));
auto election = this->insert (block);
if (election.second)
if (election.inserted)
{
election.first->transition_active ();
election.election->transition_active ();
++elections_count;
// Calculate votes for local representatives
if (representative)
if (election.prioritized && representative)
{
this->node.block_processor.generator.add (info.head);
}
@ -230,10 +231,12 @@ void nano::active_transactions::request_confirm (nano::unique_lock<std::mutex> &
nano::confirmation_solicitor solicitor (node.network, node.network_params.network);
solicitor.prepare (node.rep_crawler.principal_representatives (std::numeric_limits<size_t>::max (), node.network_params.protocol.tcp_realtime_protocol_version_min));
bool const representative_l (node.config.enable_voting && node.wallets.rep_counts ().voting > 0);
std::vector<nano::block_hash> hashes_generation_l;
auto & sorted_roots_l (roots.get<tag_difficulty> ());
auto const election_ttl_cutoff_l (std::chrono::steady_clock::now () - election_time_to_live);
bool const check_all_elections_l (std::chrono::steady_clock::now () - last_check_all_elections > check_all_elections_period);
size_t const this_loop_target_l (check_all_elections_l ? sorted_roots_l.size () : node.config.active_elections_size / 10);
size_t const this_loop_target_l (check_all_elections_l ? sorted_roots_l.size () : prioritized_cutoff);
size_t unconfirmed_count_l (0);
nano::timer<std::chrono::milliseconds> elapsed (nano::timer_state::started);
@ -247,7 +250,19 @@ void nano::active_transactions::request_confirm (nano::unique_lock<std::mutex> &
for (auto i = sorted_roots_l.begin (), n = sorted_roots_l.end (); i != n && unconfirmed_count_l < this_loop_target_l;)
{
auto & election_l (i->election);
unconfirmed_count_l += !election_l->confirmed ();
bool const confirmed_l (election_l->confirmed ());
// Queue vote generation for newly prioritized elections
if (!i->prioritized && unconfirmed_count_l < prioritized_cutoff)
{
sorted_roots_l.modify (i, [](nano::conflict_info & info_a) { info_a.prioritized = true; });
if (representative_l && !confirmed_l)
{
hashes_generation_l.push_back (election_l->status.winner->hash ());
}
}
unconfirmed_count_l += !confirmed_l;
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))
{
@ -261,13 +276,17 @@ void nano::active_transactions::request_confirm (nano::unique_lock<std::mutex> &
}
lock_a.unlock ();
solicitor.flush ();
for (auto const & hash_l : hashes_generation_l)
{
node.block_processor.generator.add (hash_l);
}
lock_a.lock ();
// This is updated after the loop to ensure slow machines don't do the full check often
if (check_all_elections_l)
{
last_check_all_elections = std::chrono::steady_clock::now ();
if (node.config.logging.timing_logging () && this_loop_target_l > node.config.active_elections_size / 10)
if (node.config.logging.timing_logging () && this_loop_target_l > prioritized_cutoff)
{
node.logger.try_log (boost::str (boost::format ("Processed %1% elections (%2% were already confirmed) in %3% %4%") % this_loop_target_l % (this_loop_target_l - unconfirmed_count_l) % elapsed.value ().count () % elapsed.unit ()));
}
@ -479,9 +498,9 @@ void nano::active_transactions::stop ()
roots.clear ();
}
std::pair<std::shared_ptr<nano::election>, bool> nano::active_transactions::insert_impl (std::shared_ptr<nano::block> block_a, 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> block_a, std::function<void(std::shared_ptr<nano::block>)> const & confirmation_action_a)
{
std::pair<std::shared_ptr<nano::election>, bool> result = { nullptr, false };
nano::election_insertion_result result;
if (!stopped)
{
auto root (block_a->qualified_root ());
@ -490,25 +509,26 @@ std::pair<std::shared_ptr<nano::election>, bool> nano::active_transactions::inse
{
if (recently_confirmed.get<tag_root> ().find (root) == recently_confirmed.get<tag_root> ().end ())
{
result.second = true;
result.inserted = true;
auto hash (block_a->hash ());
result.first = nano::make_shared<nano::election> (node, block_a, confirmation_action_a);
result.election = nano::make_shared<nano::election> (node, block_a, confirmation_action_a);
auto difficulty (block_a->difficulty ());
roots.get<tag_root> ().emplace (nano::conflict_info{ root, difficulty, difficulty, result.first });
blocks.emplace (hash, result.first);
result.prioritized = roots.size () < prioritized_cutoff || difficulty > last_prioritized_difficulty.value_or (0);
roots.get<tag_root> ().emplace (nano::conflict_info{ root, difficulty, difficulty, result.election, result.prioritized });
blocks.emplace (hash, result.election);
add_adjust_difficulty (hash);
result.first->insert_inactive_votes_cache (hash);
result.election->insert_inactive_votes_cache (hash);
}
}
else
{
result.first = existing->election;
result.election = existing->election;
}
}
return result;
}
std::pair<std::shared_ptr<nano::election>, bool> nano::active_transactions::insert (std::shared_ptr<nano::block> block_a, std::function<void(std::shared_ptr<nano::block>)> const & confirmation_action_a)
nano::election_insertion_result nano::active_transactions::insert (std::shared_ptr<nano::block> block_a, std::function<void(std::shared_ptr<nano::block>)> const & confirmation_action_a)
{
nano::lock_guard<std::mutex> lock (mutex);
return insert_impl (block_a, confirmation_action_a);
@ -741,23 +761,28 @@ void nano::active_transactions::update_adjusted_difficulty ()
void nano::active_transactions::update_active_difficulty (nano::unique_lock<std::mutex> & lock_a)
{
debug_assert (!mutex.try_lock ());
last_prioritized_difficulty.reset ();
double multiplier (1.);
if (!roots.empty ())
// Heurestic to filter out non-saturated network and frontier confirmation
if (roots.size () > prioritized_cutoff / 2 || (node.network_params.network.is_test_network () && !roots.empty ()))
{
auto & sorted_roots = roots.get<tag_difficulty> ();
std::vector<uint64_t> active_root_difficulties;
active_root_difficulties.reserve (std::min (sorted_roots.size (), node.config.active_elections_size));
size_t count (0);
for (auto it (sorted_roots.begin ()), end (sorted_roots.end ()); it != end && count++ < node.config.active_elections_size; ++it)
std::vector<uint64_t> prioritized;
prioritized.reserve (std::min (sorted_roots.size (), prioritized_cutoff));
for (auto it (sorted_roots.begin ()), end (sorted_roots.end ()); it != end && prioritized.size () < prioritized_cutoff; ++it)
{
if (!it->election->confirmed () && !it->election->idle ())
if (!it->election->confirmed ())
{
active_root_difficulties.push_back (it->adjusted_difficulty);
prioritized.push_back (it->adjusted_difficulty);
}
}
if (active_root_difficulties.size () > 10 || (!active_root_difficulties.empty () && node.network_params.network.is_test_network ()))
if (prioritized.size () > 10 || (node.network_params.network.is_test_network () && !prioritized.empty ()))
{
multiplier = nano::difficulty::to_multiplier (active_root_difficulties[active_root_difficulties.size () / 2], node.network_params.network.publish_threshold);
multiplier = nano::difficulty::to_multiplier (prioritized[prioritized.size () / 2], node.network_params.network.publish_threshold);
}
if (!prioritized.empty ())
{
last_prioritized_difficulty = prioritized.back ();
}
}
debug_assert (multiplier >= 1);

View file

@ -37,6 +37,7 @@ public:
uint64_t difficulty;
uint64_t adjusted_difficulty;
std::shared_ptr<nano::election> election;
bool prioritized;
};
class cementable_account final
@ -64,6 +65,14 @@ public:
bool confirmed{ false }; // Did item reach votes quorum? (minimum config value)
};
class election_insertion_result final
{
public:
std::shared_ptr<nano::election> election;
bool inserted{ false };
bool prioritized{ false };
};
// Core class for determining consensus
// Holds all active blocks i.e. recently added blocks that need confirmation
class active_transactions final
@ -86,7 +95,7 @@ public:
// Start an election for a block
// Call action with confirmed block, may be different than what we started with
// clang-format off
std::pair<std::shared_ptr<nano::election>, bool> insert (std::shared_ptr<nano::block>, std::function<void(std::shared_ptr<nano::block>)> const & = [](std::shared_ptr<nano::block>) {});
nano::election_insertion_result insert (std::shared_ptr<nano::block>, std::function<void(std::shared_ptr<nano::block>)> const & = [](std::shared_ptr<nano::block>) {});
// clang-format on
// Distinguishes replay votes, cannot be determined if the block is not in any election
nano::vote_code vote (std::shared_ptr<nano::vote>);
@ -119,6 +128,7 @@ public:
std::greater<uint64_t>>>>
roots;
// clang-format on
boost::optional<uint64_t> last_prioritized_difficulty{ boost::none };
std::unordered_map<nano::block_hash, std::shared_ptr<nano::election>> blocks;
std::deque<nano::election_status> list_recently_cemented ();
std::deque<nano::election_status> recently_cemented;
@ -146,7 +156,7 @@ private:
// Call action with confirmed block, may be different than what we started with
// clang-format off
std::pair<std::shared_ptr<nano::election>, bool> insert_impl (std::shared_ptr<nano::block>, 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>, std::function<void(std::shared_ptr<nano::block>)> const & = [](std::shared_ptr<nano::block>) {});
// clang-format on
void request_loop ();
void search_frontiers (nano::transaction const &);
@ -164,6 +174,9 @@ private:
// Maximum time an election can be kept active if it is extending the container
std::chrono::seconds const election_time_to_live;
// Elections above this position in the queue are prioritized
size_t const prioritized_cutoff;
static size_t constexpr recently_confirmed_size{ 65536 };
using recent_confirmation = std::pair<nano::qualified_root, nano::block_hash>;
// clang-format off

View file

@ -271,9 +271,9 @@ void nano::block_processor::process_live (nano::block_hash const & hash_a, std::
// Start collecting quorum on block
auto election = node.active.insert (block_a);
if (election.second)
if (election.inserted)
{
election.first->transition_passive ();
election.election->transition_passive ();
}
// Announce block contents to the network
@ -285,7 +285,7 @@ void nano::block_processor::process_live (nano::block_hash const & hash_a, std::
{
node.network.flood_block (block_a, nano::buffer_drop_policy::no_limiter_drop);
}
if (node.config.enable_voting && node.wallets.rep_counts ().voting > 0)
if (election.prioritized && node.config.enable_voting && node.wallets.rep_counts ().voting > 0)
{
// Announce our weighted vote to the network
generator.add (hash_a);

View file

@ -207,9 +207,9 @@ void nano::election::activate_dependencies ()
if (previous_l != nullptr && !node.block_confirmed_or_being_confirmed (transaction, previous_hash_l))
{
auto election = node.active.insert_impl (previous_l);
if (election.second)
if (election.inserted)
{
election.first->transition_active ();
election.election->transition_active ();
escalated_l = true;
}
}
@ -225,9 +225,9 @@ void nano::election::activate_dependencies ()
if (source_l != nullptr && !node.block_confirmed_or_being_confirmed (transaction, source_hash_l))
{
auto election = node.active.insert_impl (source_l);
if (election.second)
if (election.inserted)
{
election.first->transition_active ();
election.election->transition_active ();
escalated_l = true;
}
}

View file

@ -556,10 +556,10 @@ void nano::node::process_fork (nano::transaction const & transaction_a, std::sha
}
}
});
if (election.second)
if (election.inserted)
{
logger.always_log (boost::str (boost::format ("Resolving fork between our block: %1% and block %2% both with root %3%") % ledger_block->hash ().to_string () % block_a->hash ().to_string () % block_a->root ().to_string ()));
election.first->transition_active ();
election.election->transition_active ();
}
}
}
@ -1095,12 +1095,12 @@ void nano::node::add_initial_peers ()
void nano::node::block_confirm (std::shared_ptr<nano::block> block_a)
{
auto election = active.insert (block_a);
if (election.second)
if (election.inserted)
{
election.first->transition_active ();
election.election->transition_active ();
}
// Calculate votes for local representatives
if (config.enable_voting && wallets.rep_counts ().voting > 0 && active.active (*block_a))
if (election.prioritized && config.enable_voting && wallets.rep_counts ().voting > 0 && active.active (*block_a))
{
block_processor.generator.add (block_a->hash ());
}