dncurrency/nano/core_test/distributed_work.cpp
Dimitrios Siganos 78631a6b1c
Move test code into namespace nano::test (#3890)
* Move nano::establish_tcp into nano::test namespace
* Move system.hpp declarations into namespace nano::test
* Move telemetry test code to namespace nano::test
* Move testutil declarations into namespace nano::test
2022-08-10 20:48:31 +01:00

248 lines
10 KiB
C++

#include <nano/core_test/fakes/work_peer.hpp>
#include <nano/test_common/system.hpp>
#include <nano/test_common/testutil.hpp>
#include <gtest/gtest.h>
using namespace std::chrono_literals;
TEST (distributed_work, stopped)
{
nano::test::system system (1);
system.nodes[0]->distributed_work.stop ();
ASSERT_TRUE (system.nodes[0]->distributed_work.make (nano::work_version::work_1, nano::block_hash (), {}, nano::dev::network_params.work.base, {}));
}
TEST (distributed_work, no_peers)
{
nano::test::system system (1);
auto node (system.nodes[0]);
nano::block_hash hash{ 1 };
boost::optional<uint64_t> work;
std::atomic<bool> done{ false };
auto callback = [&work, &done] (boost::optional<uint64_t> work_a) {
ASSERT_TRUE (work_a.is_initialized ());
work = work_a;
done = true;
};
ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, node->config.work_peers, node->network_params.work.base, callback, nano::account ()));
ASSERT_TIMELY (5s, done);
ASSERT_GE (nano::dev::network_params.work.difficulty (nano::work_version::work_1, hash, *work), node->network_params.work.base);
// should only be removed after cleanup
ASSERT_EQ (1, node->distributed_work.size ());
while (node->distributed_work.size () > 0)
{
node->distributed_work.cleanup_finished ();
ASSERT_NO_ERROR (system.poll ());
}
}
TEST (distributed_work, no_peers_disabled)
{
nano::test::system system;
nano::node_config node_config (nano::test::get_available_port (), system.logging);
node_config.work_threads = 0;
auto & node = *system.add_node (node_config);
ASSERT_TRUE (node.distributed_work.make (nano::work_version::work_1, nano::block_hash (), node.config.work_peers, nano::dev::network_params.work.base, {}));
}
TEST (distributed_work, no_peers_cancel)
{
nano::test::system system;
nano::node_config node_config (nano::test::get_available_port (), system.logging);
node_config.max_work_generate_multiplier = 1e6;
auto & node = *system.add_node (node_config);
nano::block_hash hash{ 1 };
bool done{ false };
auto callback_to_cancel = [&done] (boost::optional<uint64_t> work_a) {
ASSERT_FALSE (work_a.is_initialized ());
done = true;
};
ASSERT_FALSE (node.distributed_work.make (nano::work_version::work_1, hash, node.config.work_peers, nano::difficulty::from_multiplier (1e6, node.network_params.work.base), callback_to_cancel));
ASSERT_EQ (1, node.distributed_work.size ());
// cleanup should not cancel or remove an ongoing work
node.distributed_work.cleanup_finished ();
ASSERT_EQ (1, node.distributed_work.size ());
// manually cancel
node.distributed_work.cancel (hash);
ASSERT_TIMELY (20s, done && node.distributed_work.size () == 0);
// now using observer
done = false;
ASSERT_FALSE (node.distributed_work.make (nano::work_version::work_1, hash, node.config.work_peers, nano::difficulty::from_multiplier (1e6, node.network_params.work.base), callback_to_cancel));
ASSERT_EQ (1, node.distributed_work.size ());
node.observers.work_cancel.notify (hash);
ASSERT_TIMELY (20s, done && node.distributed_work.size () == 0);
}
TEST (distributed_work, no_peers_multi)
{
nano::test::system system (1);
auto node (system.nodes[0]);
nano::block_hash hash{ 1 };
unsigned total{ 10 };
std::atomic<unsigned> count{ 0 };
auto callback = [&count] (boost::optional<uint64_t> work_a) {
ASSERT_TRUE (work_a.is_initialized ());
++count;
};
// Test many works for the same root
for (unsigned i{ 0 }; i < total; ++i)
{
ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, node->config.work_peers, nano::difficulty::from_multiplier (10, node->network_params.work.base), callback));
}
ASSERT_TIMELY (5s, count == total);
system.deadline_set (5s);
while (node->distributed_work.size () > 0)
{
node->distributed_work.cleanup_finished ();
ASSERT_NO_ERROR (system.poll ());
}
count = 0;
// Test many works for different roots
for (unsigned i{ 0 }; i < total; ++i)
{
nano::block_hash hash_i (i + 1);
ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash_i, node->config.work_peers, node->network_params.work.base, callback));
}
ASSERT_TIMELY (5s, count == total);
system.deadline_set (5s);
while (node->distributed_work.size () > 0)
{
node->distributed_work.cleanup_finished ();
ASSERT_NO_ERROR (system.poll ());
}
}
TEST (distributed_work, peer)
{
nano::test::system system;
nano::node_config node_config;
node_config.peering_port = nano::test::get_available_port ();
// Disable local work generation
node_config.work_threads = 0;
auto node (system.add_node (node_config));
ASSERT_FALSE (node->local_work_generation_enabled ());
nano::block_hash hash{ 1 };
boost::optional<uint64_t> work;
std::atomic<bool> done{ false };
auto callback = [&work, &done] (boost::optional<uint64_t> work_a) {
ASSERT_TRUE (work_a.is_initialized ());
work = work_a;
done = true;
};
auto work_peer (std::make_shared<fake_work_peer> (node->work, node->io_ctx, nano::test::get_available_port (), work_peer_type::good));
work_peer->start ();
decltype (node->config.work_peers) peers;
peers.emplace_back ("::ffff:127.0.0.1", work_peer->port ());
ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, peers, node->network_params.work.base, callback, nano::account ()));
ASSERT_TIMELY (5s, done);
ASSERT_GE (nano::dev::network_params.work.difficulty (nano::work_version::work_1, hash, *work), node->network_params.work.base);
ASSERT_EQ (1, work_peer->generations_good);
ASSERT_EQ (0, work_peer->generations_bad);
ASSERT_NO_ERROR (system.poll ());
ASSERT_EQ (0, work_peer->cancels);
}
TEST (distributed_work, peer_malicious)
{
nano::test::system system (1);
auto node (system.nodes[0]);
ASSERT_TRUE (node->local_work_generation_enabled ());
nano::block_hash hash{ 1 };
boost::optional<uint64_t> work;
std::atomic<bool> done{ false };
auto callback = [&work, &done] (boost::optional<uint64_t> work_a) {
ASSERT_TRUE (work_a.is_initialized ());
work = work_a;
done = true;
};
auto malicious_peer (std::make_shared<fake_work_peer> (node->work, node->io_ctx, nano::test::get_available_port (), work_peer_type::malicious));
malicious_peer->start ();
decltype (node->config.work_peers) peers;
peers.emplace_back ("::ffff:127.0.0.1", malicious_peer->port ());
ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, peers, node->network_params.work.base, callback, nano::account ()));
ASSERT_TIMELY (5s, done);
ASSERT_GE (nano::dev::network_params.work.difficulty (nano::work_version::work_1, hash, *work), node->network_params.work.base);
ASSERT_TIMELY (5s, malicious_peer->generations_bad >= 1);
// make sure it was *not* the malicious peer that replied
ASSERT_EQ (0, malicious_peer->generations_good);
// initial generation + the second time when it also starts doing local generation
// it is possible local work generation finishes before the second request is sent, only 1 failure can be required to pass
ASSERT_GE (malicious_peer->generations_bad, 1);
// this peer should not receive a cancel
ASSERT_EQ (0, malicious_peer->cancels);
// Test again with no local work generation enabled to make sure the malicious peer is sent more than one request
node->config.work_threads = 0;
ASSERT_FALSE (node->local_work_generation_enabled ());
auto malicious_peer2 (std::make_shared<fake_work_peer> (node->work, node->io_ctx, nano::test::get_available_port (), work_peer_type::malicious));
malicious_peer2->start ();
peers[0].second = malicious_peer2->port ();
ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, peers, node->network_params.work.base, {}, nano::account ()));
ASSERT_TIMELY (5s, malicious_peer2->generations_bad >= 2);
node->distributed_work.cancel (hash);
ASSERT_EQ (0, malicious_peer2->cancels);
}
// Test disabled because it's failing intermittently.
// PR in which it got disabled: https://github.com/nanocurrency/nano-node/pull/3629
// Issue for investigating it: https://github.com/nanocurrency/nano-node/issues/3630
TEST (distributed_work, DISABLED_peer_multi)
{
nano::test::system system (1);
auto node (system.nodes[0]);
ASSERT_TRUE (node->local_work_generation_enabled ());
nano::block_hash hash{ 1 };
boost::optional<uint64_t> work;
std::atomic<bool> done{ false };
auto callback = [&work, &done] (boost::optional<uint64_t> work_a) {
ASSERT_TRUE (work_a.is_initialized ());
work = work_a;
done = true;
};
auto good_peer (std::make_shared<fake_work_peer> (node->work, node->io_ctx, nano::test::get_available_port (), work_peer_type::good));
auto malicious_peer (std::make_shared<fake_work_peer> (node->work, node->io_ctx, nano::test::get_available_port (), work_peer_type::malicious));
auto slow_peer (std::make_shared<fake_work_peer> (node->work, node->io_ctx, nano::test::get_available_port (), work_peer_type::slow));
good_peer->start ();
malicious_peer->start ();
slow_peer->start ();
decltype (node->config.work_peers) peers;
peers.emplace_back ("localhost", malicious_peer->port ());
peers.emplace_back ("localhost", slow_peer->port ());
peers.emplace_back ("localhost", good_peer->port ());
ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, peers, node->network_params.work.base, callback, nano::account ()));
ASSERT_TIMELY (5s, done);
ASSERT_GE (nano::dev::network_params.work.difficulty (nano::work_version::work_1, hash, *work), node->network_params.work.base);
ASSERT_TIMELY (5s, slow_peer->cancels == 1);
ASSERT_EQ (0, malicious_peer->generations_good);
ASSERT_EQ (1, malicious_peer->generations_bad);
ASSERT_EQ (0, malicious_peer->cancels);
ASSERT_EQ (0, slow_peer->generations_good);
ASSERT_EQ (0, slow_peer->generations_bad);
ASSERT_EQ (1, slow_peer->cancels);
ASSERT_EQ (1, good_peer->generations_good);
ASSERT_EQ (0, good_peer->generations_bad);
ASSERT_EQ (0, good_peer->cancels);
}
TEST (distributed_work, fail_resolve)
{
nano::test::system system (1);
auto node (system.nodes[0]);
nano::block_hash hash{ 1 };
boost::optional<uint64_t> work;
std::atomic<bool> done{ false };
auto callback = [&work, &done] (boost::optional<uint64_t> work_a) {
ASSERT_TRUE (work_a.is_initialized ());
work = work_a;
done = true;
};
decltype (node->config.work_peers) peers;
peers.emplace_back ("beeb.boop.123z", 0);
ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, peers, node->network_params.work.base, callback, nano::account ()));
ASSERT_TIMELY (5s, done);
ASSERT_GE (nano::dev::network_params.work.difficulty (nano::work_version::work_1, hash, *work), node->network_params.work.base);
}