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:
parent
89a8e6d153
commit
2b658acb4b
11 changed files with 182 additions and 103 deletions
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ());
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue