dncurrency/nano/core_test/conflicts.cpp
clemahieu 21b77a8829
Rename the class nano::scheduler::buckets to nano::scheduler::priority. (#4272)
Naming it "priority scheduler" matches the naming of other scheduler strategies like hinted scheduler and optimistic scheduler.
2023-09-01 13:25:42 +01:00

265 lines
9 KiB
C++

#include <nano/node/election.hpp>
#include <nano/node/scheduler/component.hpp>
#include <nano/node/scheduler/priority.hpp>
#include <nano/test_common/system.hpp>
#include <nano/test_common/testutil.hpp>
#include <gtest/gtest.h>
#include <boost/variant/get.hpp>
using namespace std::chrono_literals;
TEST (conflicts, start_stop)
{
nano::test::system system (1);
auto & node1 (*system.nodes[0]);
nano::keypair key1;
nano::block_builder builder;
auto send1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*send1);
ASSERT_EQ (nano::process_result::progress, node1.process (*send1).code);
ASSERT_EQ (0, node1.active.size ());
node1.scheduler.priority.activate (nano::dev::genesis_key.pub, node1.store.tx_begin_read ());
ASSERT_TIMELY (5s, node1.active.election (send1->qualified_root ()));
auto election1 = node1.active.election (send1->qualified_root ());
ASSERT_EQ (1, node1.active.size ());
ASSERT_NE (nullptr, election1);
ASSERT_EQ (1, election1->votes ().size ());
}
TEST (conflicts, add_existing)
{
nano::test::system system{ 1 };
auto & node1 = *system.nodes[0];
nano::keypair key1;
// create a send block to send all of the nano supply to key1
nano::block_builder builder;
auto send1 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key1.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*send1);
// add the block to ledger as an unconfirmed block
ASSERT_EQ (nano::process_result::progress, node1.process (*send1).code);
// wait for send1 to be inserted in the ledger
ASSERT_TIMELY (5s, node1.block (send1->hash ()));
// instruct the election scheduler to trigger an election for send1
node1.scheduler.priority.activate (nano::dev::genesis_key.pub, node1.store.tx_begin_read ());
// wait for election to be started before processing send2
ASSERT_TIMELY (5s, node1.active.active (*send1));
nano::keypair key2;
auto send2 = builder
.send ()
.previous (nano::dev::genesis->hash ())
.destination (key2.pub)
.balance (0)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (0)
.build_shared ();
node1.work_generate_blocking (*send2);
send2->sideband_set ({});
// the block processor will notice that the block is a fork and it will try to publish it
// which will update the election object
node1.block_processor.add (send2);
ASSERT_TRUE (node1.active.active (*send1));
ASSERT_TIMELY (5s, node1.active.active (*send2));
}
TEST (conflicts, add_two)
{
nano::test::system system{};
auto const & node = system.add_node ();
system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv);
// define a functor that sends from given account to given destination,
// optionally force-confirming the send blocks *and* receiving on the destination account;
// the functor returns a pair of the send and receive blocks created or nullptrs if something failed
//
auto const do_send = [&node, &system] (auto const & previous, auto const & from, auto const & to, bool forceConfirm = true)
-> std::pair<std::optional<std::shared_ptr<nano::block>>, std::optional<std::shared_ptr<nano::block>>> {
auto const send = nano::send_block_builder{}.make_block ().previous (previous).destination (to.pub).balance (0).sign (from.prv, from.pub).work (*system.work.generate (previous)).build_shared ();
if (nano::process_result::progress != node->process (*send).code)
{
return std::make_pair (std::nullopt, std::nullopt);
}
if (!forceConfirm)
{
return std::make_pair (std::move (send), std::nullopt);
}
auto const is_confirmed = [&node] (auto const & hash) {
return node->block_confirmed (hash);
};
node->process_confirmed (nano::election_status{ send });
auto const is_send_not_confirmed = system.poll_until_true (5s, std::bind (is_confirmed, send->hash ()));
if (is_send_not_confirmed)
{
return std::make_pair (std::nullopt, std::nullopt);
}
auto const receive = nano::open_block_builder{}.make_block ().account (to.pub).source (send->hash ()).representative (to.pub).sign (to.prv, to.pub).work (*system.work.generate (to.pub)).build_shared ();
if (nano::process_result::progress != node->process (*receive).code)
{
return std::make_pair (std::nullopt, std::nullopt);
}
node->process_confirmed (nano::election_status{ receive });
auto const is_receive_not_confirmed = system.poll_until_true (5s, std::bind (is_confirmed, receive->hash ()));
if (is_receive_not_confirmed)
{
return std::make_pair (std::move (send), std::nullopt);
}
return std::make_pair (std::move (send), std::move (receive));
};
// send from genesis to account1 and receive it on account1
//
nano::keypair account1{};
auto const [send1, receive1] = do_send (nano::dev::genesis->hash (), nano::dev::genesis_key, account1);
ASSERT_TRUE (send1.has_value () && receive1.has_value ());
// both blocks having been fully confirmed, we expect 1 (genesis) + 2 (send/receive) = 3 cemented blocks
//
ASSERT_EQ (3, node->ledger.cache.cemented_count);
nano::keypair account2{};
auto const [send2, receive2] = do_send ((*send1)->hash (), nano::dev::genesis_key, account2);
ASSERT_TRUE (send2.has_value () && receive2.has_value ());
ASSERT_EQ (5, node->ledger.cache.cemented_count);
// send from account1 to account3 but do not receive it on account3 and do not force-confirm the send block
//
nano::keypair account3{};
auto const [send3, dummy1] = do_send ((*receive1)->hash (), account1, account3, false);
ASSERT_TRUE (send3.has_value ());
// expect the number of cemented blocks not to have changed since the last operation
//
ASSERT_EQ (5, node->ledger.cache.cemented_count);
auto const [send4, dummy2] = do_send ((*receive2)->hash (), account2, account3, false);
ASSERT_TRUE (send4.has_value ());
ASSERT_EQ (5, node->ledger.cache.cemented_count);
// activate elections for the previous two send blocks (to account3) that we did not forcefully confirm
//
node->scheduler.priority.activate (account3.pub, node->store.tx_begin_read ());
ASSERT_TIMELY (5s, node->active.election ((*send3)->qualified_root ()) != nullptr);
ASSERT_TIMELY (5s, node->active.election ((*send4)->qualified_root ()) != nullptr);
// wait 3s before asserting just to make sure there would be enough time
// for the Active Elections Container to evict both elections in case they would wrongfully get confirmed
//
ASSERT_TIMELY (5s, node->active.size () == 2);
}
TEST (vote_uniquer, null)
{
nano::block_uniquer block_uniquer;
nano::vote_uniquer uniquer (block_uniquer);
ASSERT_EQ (nullptr, uniquer.unique (nullptr));
}
TEST (vote_uniquer, vbh_one)
{
nano::block_uniquer block_uniquer;
nano::vote_uniquer uniquer (block_uniquer);
nano::keypair key;
nano::block_builder builder;
auto block = builder
.state ()
.account (0)
.previous (0)
.representative (0)
.balance (0)
.link (0)
.sign (key.prv, key.pub)
.work (0)
.build_shared ();
std::vector<nano::block_hash> hashes;
hashes.push_back (block->hash ());
auto vote1 (std::make_shared<nano::vote> (key.pub, key.prv, 0, 0, hashes));
auto vote2 (std::make_shared<nano::vote> (*vote1));
ASSERT_EQ (vote1, uniquer.unique (vote1));
ASSERT_EQ (vote1, uniquer.unique (vote2));
}
TEST (vote_uniquer, vbh_two)
{
nano::block_uniquer block_uniquer;
nano::vote_uniquer uniquer (block_uniquer);
nano::keypair key;
nano::block_builder builder;
auto block1 = builder
.state ()
.account (0)
.previous (0)
.representative (0)
.balance (0)
.link (0)
.sign (key.prv, key.pub)
.work (0)
.build_shared ();
std::vector<nano::block_hash> hashes1;
hashes1.push_back (block1->hash ());
auto block2 = builder
.state ()
.account (1)
.previous (0)
.representative (0)
.balance (0)
.link (0)
.sign (key.prv, key.pub)
.work (0)
.build_shared ();
std::vector<nano::block_hash> hashes2;
hashes2.push_back (block2->hash ());
auto vote1 (std::make_shared<nano::vote> (key.pub, key.prv, 0, 0, hashes1));
auto vote2 (std::make_shared<nano::vote> (key.pub, key.prv, 0, 0, hashes2));
ASSERT_EQ (vote1, uniquer.unique (vote1));
ASSERT_EQ (vote2, uniquer.unique (vote2));
}
TEST (vote_uniquer, cleanup)
{
nano::block_uniquer block_uniquer;
nano::vote_uniquer uniquer (block_uniquer);
nano::keypair key;
auto vote1 = std::make_shared<nano::vote> (key.pub, key.prv, 0, 0, std::vector<nano::block_hash>{ nano::block_hash{ 0 } });
auto vote2 = std::make_shared<nano::vote> (key.pub, key.prv, nano::vote::timestamp_min * 1, 0, std::vector<nano::block_hash>{ nano::block_hash{ 0 } });
auto vote3 = uniquer.unique (vote1);
auto vote4 = uniquer.unique (vote2);
vote2.reset ();
vote4.reset ();
ASSERT_EQ (2, uniquer.size ());
auto iterations (0);
while (uniquer.size () == 2)
{
auto vote5 (uniquer.unique (vote1));
ASSERT_LT (iterations++, 200);
}
}