Naming it "priority scheduler" matches the naming of other scheduler strategies like hinted scheduler and optimistic scheduler.
273 lines
7.4 KiB
C++
273 lines
7.4 KiB
C++
#include <nano/crypto_lib/random_pool.hpp>
|
|
#include <nano/node/scheduler/component.hpp>
|
|
#include <nano/node/scheduler/priority.hpp>
|
|
#include <nano/node/transport/fake.hpp>
|
|
#include <nano/test_common/system.hpp>
|
|
#include <nano/test_common/testutil.hpp>
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <cstdlib>
|
|
#include <numeric>
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
void nano::test::wait_peer_connections (nano::test::system & system_a)
|
|
{
|
|
auto wait_peer_count = [&system_a] (bool in_memory) {
|
|
auto num_nodes = system_a.nodes.size ();
|
|
system_a.deadline_set (20s);
|
|
size_t peer_count = 0;
|
|
while (peer_count != num_nodes * (num_nodes - 1))
|
|
{
|
|
ASSERT_NO_ERROR (system_a.poll ());
|
|
peer_count = std::accumulate (system_a.nodes.cbegin (), system_a.nodes.cend (), std::size_t{ 0 }, [in_memory] (auto total, auto const & node) {
|
|
if (in_memory)
|
|
{
|
|
return total += node->network.size ();
|
|
}
|
|
else
|
|
{
|
|
auto transaction = node->store.tx_begin_read ();
|
|
return total += node->store.peer.count (transaction);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
// Do a pre-pass with in-memory containers to reduce IO if still in the process of connecting to peers
|
|
wait_peer_count (true);
|
|
wait_peer_count (false);
|
|
}
|
|
|
|
nano::hash_or_account nano::test::random_hash_or_account ()
|
|
{
|
|
nano::hash_or_account random_hash;
|
|
nano::random_pool::generate_block (random_hash.bytes.data (), random_hash.bytes.size ());
|
|
return random_hash;
|
|
}
|
|
|
|
nano::block_hash nano::test::random_hash ()
|
|
{
|
|
return nano::test::random_hash_or_account ().as_block_hash ();
|
|
}
|
|
|
|
nano::account nano::test::random_account ()
|
|
{
|
|
return nano::test::random_hash_or_account ().as_account ();
|
|
}
|
|
|
|
bool nano::test::process (nano::node & node, std::vector<std::shared_ptr<nano::block>> blocks)
|
|
{
|
|
auto const transaction = node.store.tx_begin_write ({ tables::accounts, tables::blocks, tables::frontiers, tables::pending });
|
|
for (auto & block : blocks)
|
|
{
|
|
auto result = node.process (transaction, *block);
|
|
if (result.code != nano::process_result::progress)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool nano::test::process_live (nano::node & node, std::vector<std::shared_ptr<nano::block>> blocks)
|
|
{
|
|
for (auto & block : blocks)
|
|
{
|
|
node.process_active (block);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool nano::test::confirm (nano::node & node, std::vector<nano::block_hash> hashes)
|
|
{
|
|
// Finish processing all blocks - FIXME: block processor flush is broken and should be removed
|
|
node.block_processor.flush ();
|
|
for (auto & hash : hashes)
|
|
{
|
|
if (node.block_confirmed (hash))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
auto disk_block = node.block (hash);
|
|
// A sideband is required to start an election
|
|
release_assert (disk_block != nullptr);
|
|
release_assert (disk_block->has_sideband ());
|
|
// This only starts election
|
|
auto election = node.block_confirm (disk_block);
|
|
if (election == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
// Here we actually confirm the block
|
|
election->force_confirm ();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool nano::test::confirm (nano::node & node, std::vector<std::shared_ptr<nano::block>> blocks)
|
|
{
|
|
return confirm (node, blocks_to_hashes (blocks));
|
|
}
|
|
|
|
bool nano::test::confirmed (nano::node & node, std::vector<nano::block_hash> hashes)
|
|
{
|
|
for (auto & hash : hashes)
|
|
{
|
|
if (!node.block_confirmed (hash))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool nano::test::confirmed (nano::node & node, std::vector<std::shared_ptr<nano::block>> blocks)
|
|
{
|
|
return confirmed (node, blocks_to_hashes (blocks));
|
|
}
|
|
|
|
bool nano::test::exists (nano::node & node, std::vector<nano::block_hash> hashes)
|
|
{
|
|
for (auto & hash : hashes)
|
|
{
|
|
if (!node.block (hash))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool nano::test::exists (nano::node & node, std::vector<std::shared_ptr<nano::block>> blocks)
|
|
{
|
|
return exists (node, blocks_to_hashes (blocks));
|
|
}
|
|
|
|
bool nano::test::activate (nano::node & node, std::vector<nano::block_hash> hashes)
|
|
{
|
|
for (auto & hash : hashes)
|
|
{
|
|
auto disk_block = node.block (hash);
|
|
if (disk_block == nullptr)
|
|
{
|
|
// Block does not exist in the ledger yet
|
|
return false;
|
|
}
|
|
node.scheduler.priority.manual (disk_block);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool nano::test::activate (nano::node & node, std::vector<std::shared_ptr<nano::block>> blocks)
|
|
{
|
|
return activate (node, blocks_to_hashes (blocks));
|
|
}
|
|
|
|
bool nano::test::active (nano::node & node, std::vector<nano::block_hash> hashes)
|
|
{
|
|
for (auto & hash : hashes)
|
|
{
|
|
if (!node.active.active (hash))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool nano::test::active (nano::node & node, std::vector<std::shared_ptr<nano::block>> blocks)
|
|
{
|
|
return active (node, blocks_to_hashes (blocks));
|
|
}
|
|
|
|
std::shared_ptr<nano::vote> nano::test::make_vote (nano::keypair key, std::vector<nano::block_hash> hashes, uint64_t timestamp, uint8_t duration)
|
|
{
|
|
return std::make_shared<nano::vote> (key.pub, key.prv, timestamp, duration, hashes);
|
|
}
|
|
|
|
std::shared_ptr<nano::vote> nano::test::make_vote (nano::keypair key, std::vector<std::shared_ptr<nano::block>> blocks, uint64_t timestamp, uint8_t duration)
|
|
{
|
|
std::vector<nano::block_hash> hashes;
|
|
std::transform (blocks.begin (), blocks.end (), std::back_inserter (hashes), [] (auto & block) { return block->hash (); });
|
|
return make_vote (key, hashes, timestamp, duration);
|
|
}
|
|
|
|
std::shared_ptr<nano::vote> nano::test::make_final_vote (nano::keypair key, std::vector<nano::block_hash> hashes)
|
|
{
|
|
return make_vote (key, hashes, nano::vote::timestamp_max, nano::vote::duration_max);
|
|
}
|
|
|
|
std::shared_ptr<nano::vote> nano::test::make_final_vote (nano::keypair key, std::vector<std::shared_ptr<nano::block>> blocks)
|
|
{
|
|
return make_vote (key, blocks, nano::vote::timestamp_max, nano::vote::duration_max);
|
|
}
|
|
|
|
std::vector<nano::block_hash> nano::test::blocks_to_hashes (std::vector<std::shared_ptr<nano::block>> blocks)
|
|
{
|
|
std::vector<nano::block_hash> hashes;
|
|
std::transform (blocks.begin (), blocks.end (), std::back_inserter (hashes), [] (auto & block) { return block->hash (); });
|
|
return hashes;
|
|
}
|
|
|
|
std::shared_ptr<nano::transport::channel> nano::test::fake_channel (nano::node & node, nano::account node_id)
|
|
{
|
|
auto channel = std::make_shared<nano::transport::fake::channel> (node);
|
|
if (!node_id.is_zero ())
|
|
{
|
|
channel->set_node_id (node_id);
|
|
}
|
|
return channel;
|
|
}
|
|
|
|
std::shared_ptr<nano::election> nano::test::start_election (nano::test::system & system_a, nano::node & node_a, const nano::block_hash & hash_a)
|
|
{
|
|
system_a.deadline_set (5s);
|
|
|
|
// wait until and ensure that the block is in the ledger
|
|
auto block_l = node_a.block (hash_a);
|
|
while (!block_l)
|
|
{
|
|
if (system_a.poll ())
|
|
{
|
|
return nullptr;
|
|
}
|
|
block_l = node_a.block (hash_a);
|
|
}
|
|
|
|
node_a.scheduler.priority.manual (block_l);
|
|
|
|
// wait for the election to appear
|
|
std::shared_ptr<nano::election> election = node_a.active.election (block_l->qualified_root ());
|
|
while (!election)
|
|
{
|
|
if (system_a.poll ())
|
|
{
|
|
return nullptr;
|
|
}
|
|
election = node_a.active.election (block_l->qualified_root ());
|
|
}
|
|
|
|
election->transition_active ();
|
|
return election;
|
|
}
|
|
|
|
void nano::test::start_elections (nano::test::system & system_a, nano::node & node_a, std::vector<nano::block_hash> const & hashes_a, bool const forced_a)
|
|
{
|
|
for (auto const & hash_l : hashes_a)
|
|
{
|
|
auto election = nano::test::start_election (system_a, node_a, hash_l);
|
|
release_assert (election);
|
|
if (forced_a)
|
|
{
|
|
election->force_confirm ();
|
|
}
|
|
}
|
|
}
|
|
|
|
void nano::test::start_elections (nano::test::system & system_a, nano::node & node_a, std::vector<std::shared_ptr<nano::block>> const & blocks_a, bool const forced_a)
|
|
{
|
|
nano::test::start_elections (system_a, node_a, blocks_to_hashes (blocks_a), forced_a);
|
|
}
|