diff --git a/nano/core_test/active_elections.cpp b/nano/core_test/active_elections.cpp index 06f5a7ea32..fd94659a50 100644 --- a/nano/core_test/active_elections.cpp +++ b/nano/core_test/active_elections.cpp @@ -1294,35 +1294,33 @@ TEST (active_elections, list_active) TEST (active_elections, vacancy) { std::atomic updated = false; - { - nano::test::system system; - nano::node_config config = system.default_config (); - config.active_elections.size = 1; - auto & node = *system.add_node (config); - nano::state_block_builder builder; - auto send = builder.make_block () - .account (nano::dev::genesis_key.pub) - .previous (nano::dev::genesis->hash ()) - .representative (nano::dev::genesis_key.pub) - .link (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (nano::dev::genesis->hash ())) - .build (); - node.active.vacancy_update = [&updated] () { updated = true; }; - ASSERT_EQ (nano::block_status::progress, node.process (send)); - ASSERT_EQ (1, node.active.vacancy (nano::election_behavior::priority)); - ASSERT_EQ (0, node.active.size ()); - auto election1 = nano::test::start_election (system, node, send->hash ()); - ASSERT_TIMELY (1s, updated); - updated = false; - ASSERT_EQ (0, node.active.vacancy (nano::election_behavior::priority)); - ASSERT_EQ (1, node.active.size ()); - election1->force_confirm (); - ASSERT_TIMELY (1s, updated); - ASSERT_EQ (1, node.active.vacancy (nano::election_behavior::priority)); - ASSERT_EQ (0, node.active.size ()); - } + nano::test::system system; + nano::node_config config = system.default_config (); + config.active_elections.size = 1; + auto & node = *system.add_node (config); + nano::state_block_builder builder; + auto send = builder.make_block () + .account (nano::dev::genesis_key.pub) + .previous (nano::dev::genesis->hash ()) + .representative (nano::dev::genesis_key.pub) + .link (nano::dev::genesis_key.pub) + .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (nano::dev::genesis->hash ())) + .build (); + node.active.vacancy_update = [&updated] () { updated = true; }; + ASSERT_EQ (nano::block_status::progress, node.process (send)); + ASSERT_EQ (1, node.active.vacancy (nano::election_behavior::priority)); + ASSERT_EQ (0, node.active.size ()); + auto election1 = nano::test::start_election (system, node, send->hash ()); + ASSERT_TIMELY (1s, updated); + updated = false; + ASSERT_EQ (0, node.active.vacancy (nano::election_behavior::priority)); + ASSERT_EQ (1, node.active.size ()); + election1->force_confirm (); + ASSERT_TIMELY (1s, updated); + ASSERT_EQ (1, node.active.vacancy (nano::election_behavior::priority)); + ASSERT_EQ (0, node.active.size ()); } /* diff --git a/nano/core_test/election_scheduler.cpp b/nano/core_test/election_scheduler.cpp index 02a37bec89..13e5411b0b 100644 --- a/nano/core_test/election_scheduler.cpp +++ b/nano/core_test/election_scheduler.cpp @@ -13,14 +13,121 @@ using namespace std::chrono_literals; +namespace +{ +nano::keypair & keyzero () +{ + static nano::keypair result; + return result; +} +nano::keypair & key0 () +{ + static nano::keypair result; + return result; +} +nano::keypair & key1 () +{ + static nano::keypair result; + return result; +} +nano::keypair & key2 () +{ + static nano::keypair result; + return result; +} +nano::keypair & key3 () +{ + static nano::keypair result; + return result; +} +std::shared_ptr & blockzero () +{ + nano::block_builder builder; + static auto result = builder + .state () + .account (keyzero ().pub) + .previous (0) + .representative (keyzero ().pub) + .balance (0) + .link (0) + .sign (keyzero ().prv, keyzero ().pub) + .work (0) + .build (); + return result; +} +std::shared_ptr & block0 () +{ + nano::block_builder builder; + static auto result = builder + .state () + .account (key0 ().pub) + .previous (0) + .representative (key0 ().pub) + .balance (nano::Gxrb_ratio) + .link (0) + .sign (key0 ().prv, key0 ().pub) + .work (0) + .build (); + return result; +} +std::shared_ptr & block1 () +{ + nano::block_builder builder; + static auto result = builder + .state () + .account (key1 ().pub) + .previous (0) + .representative (key1 ().pub) + .balance (nano::Mxrb_ratio) + .link (0) + .sign (key1 ().prv, key1 ().pub) + .work (0) + .build (); + return result; +} +std::shared_ptr & block2 () +{ + nano::block_builder builder; + static auto result = builder + .state () + .account (key2 ().pub) + .previous (0) + .representative (key2 ().pub) + .balance (nano::Gxrb_ratio) + .link (0) + .sign (key2 ().prv, key2 ().pub) + .work (0) + .build (); + return result; +} +std::shared_ptr & block3 () +{ + nano::block_builder builder; + static auto result = builder + .state () + .account (key3 ().pub) + .previous (0) + .representative (key3 ().pub) + .balance (nano::Mxrb_ratio) + .link (0) + .sign (key3 ().prv, key3 ().pub) + .work (0) + .build (); + return result; +} +} + TEST (election_scheduler, construction) { - nano::test::system system{ 1 }; + nano::test::system system; + auto & node = *system.add_node (); } TEST (election_scheduler, activate_one_timely) { - nano::test::system system{ 1 }; + nano::test::system system; + auto & node = *system.add_node (); + nano::state_block_builder builder; auto send1 = builder.make_block () .account (nano::dev::genesis_key.pub) @@ -31,14 +138,16 @@ TEST (election_scheduler, activate_one_timely) .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) .work (*system.work.generate (nano::dev::genesis->hash ())) .build (); - system.nodes[0]->ledger.process (system.nodes[0]->ledger.tx_begin_write (), send1); - system.nodes[0]->scheduler.priority.activate (system.nodes[0]->ledger.tx_begin_read (), nano::dev::genesis_key.pub); - ASSERT_TIMELY (5s, system.nodes[0]->active.election (send1->qualified_root ())); + node.ledger.process (node.ledger.tx_begin_write (), send1); + node.scheduler.priority.activate (node.ledger.tx_begin_read (), nano::dev::genesis_key.pub); + ASSERT_TIMELY (5s, node.active.election (send1->qualified_root ())); } TEST (election_scheduler, activate_one_flush) { - nano::test::system system{ 1 }; + nano::test::system system; + auto & node = *system.add_node (); + nano::state_block_builder builder; auto send1 = builder.make_block () .account (nano::dev::genesis_key.pub) @@ -49,9 +158,9 @@ TEST (election_scheduler, activate_one_flush) .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) .work (*system.work.generate (nano::dev::genesis->hash ())) .build (); - system.nodes[0]->ledger.process (system.nodes[0]->ledger.tx_begin_write (), send1); - system.nodes[0]->scheduler.priority.activate (system.nodes[0]->ledger.tx_begin_read (), nano::dev::genesis_key.pub); - ASSERT_TIMELY (5s, system.nodes[0]->active.election (send1->qualified_root ())); + node.ledger.process (node.ledger.tx_begin_write (), send1); + node.scheduler.priority.activate (node.ledger.tx_begin_read (), nano::dev::genesis_key.pub); + ASSERT_TIMELY (5s, node.active.election (send1->qualified_root ())); } /** @@ -71,13 +180,13 @@ TEST (election_scheduler, activate_one_flush) */ TEST (election_scheduler, no_vacancy) { - nano::test::system system{}; + nano::test::system system; nano::node_config config = system.default_config (); config.active_elections.size = 1; config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - auto & node = *system.add_node (config); + nano::state_block_builder builder{}; nano::keypair key{}; @@ -146,3 +255,85 @@ TEST (election_scheduler, no_vacancy) ASSERT_TIMELY (5s, node.active.election (block2->qualified_root ()) != nullptr); ASSERT_TRUE (node.scheduler.priority.empty ()); } + +TEST (election_scheduler_bucket, construction) +{ + nano::test::system system; + auto & node = *system.add_node (); + + nano::scheduler::priority_bucket_config bucket_config; + nano::scheduler::bucket bucket{ nano::Gxrb_ratio, bucket_config, node.active, node.stats }; + ASSERT_EQ (nano::Gxrb_ratio, bucket.minimum_balance); + ASSERT_TRUE (bucket.empty ()); + ASSERT_EQ (0, bucket.size ()); +} + +TEST (election_scheduler_bucket, insert_one) +{ + nano::test::system system; + auto & node = *system.add_node (); + + nano::scheduler::priority_bucket_config bucket_config; + nano::scheduler::bucket bucket{ 0, bucket_config, node.active, node.stats }; + ASSERT_TRUE (bucket.push (1000, block0 ())); + ASSERT_FALSE (bucket.empty ()); + ASSERT_EQ (1, bucket.size ()); + auto blocks = bucket.blocks (); + ASSERT_EQ (1, blocks.size ()); + ASSERT_EQ (block0 (), blocks.front ()); +} + +TEST (election_scheduler_bucket, insert_duplicate) +{ + nano::test::system system; + auto & node = *system.add_node (); + + nano::scheduler::priority_bucket_config bucket_config; + nano::scheduler::bucket bucket{ 0, bucket_config, node.active, node.stats }; + ASSERT_TRUE (bucket.push (1000, block0 ())); + ASSERT_FALSE (bucket.push (1000, block0 ())); +} + +TEST (election_scheduler_bucket, insert_many) +{ + nano::test::system system; + auto & node = *system.add_node (); + + nano::scheduler::priority_bucket_config bucket_config; + nano::scheduler::bucket bucket{ 0, bucket_config, node.active, node.stats }; + ASSERT_TRUE (bucket.push (2000, block0 ())); + ASSERT_TRUE (bucket.push (1001, block1 ())); + ASSERT_TRUE (bucket.push (1000, block2 ())); + ASSERT_TRUE (bucket.push (900, block3 ())); + ASSERT_FALSE (bucket.empty ()); + ASSERT_EQ (4, bucket.size ()); + auto blocks = bucket.blocks (); + ASSERT_EQ (4, blocks.size ()); + // Ensure correct order + ASSERT_EQ (blocks[0], block3 ()); + ASSERT_EQ (blocks[1], block2 ()); + ASSERT_EQ (blocks[2], block1 ()); + ASSERT_EQ (blocks[3], block0 ()); +} + +TEST (election_scheduler_bucket, max_blocks) +{ + nano::test::system system; + auto & node = *system.add_node (); + + nano::scheduler::priority_bucket_config bucket_config{ + .max_blocks = 2 + }; + nano::scheduler::bucket bucket{ 0, bucket_config, node.active, node.stats }; + ASSERT_TRUE (bucket.push (2000, block0 ())); + ASSERT_TRUE (bucket.push (900, block1 ())); + ASSERT_FALSE (bucket.push (3000, block2 ())); + ASSERT_TRUE (bucket.push (1001, block3 ())); // Evicts 2000 + ASSERT_TRUE (bucket.push (1000, block0 ())); // Evicts 1001 + ASSERT_EQ (2, bucket.size ()); + auto blocks = bucket.blocks (); + ASSERT_EQ (2, blocks.size ()); + // Ensure correct order + ASSERT_EQ (blocks[0], block1 ()); + ASSERT_EQ (blocks[1], block0 ()); +} \ No newline at end of file diff --git a/nano/node/scheduler/bucket.cpp b/nano/node/scheduler/bucket.cpp index 21316785b8..f3e69383cd 100644 --- a/nano/node/scheduler/bucket.cpp +++ b/nano/node/scheduler/bucket.cpp @@ -167,6 +167,18 @@ void nano::scheduler::bucket::cancel_lowest_election () } } +std::deque> nano::scheduler::bucket::blocks () const +{ + nano::lock_guard lock{ mutex }; + + std::deque> result; + for (auto const & item : queue) + { + result.push_back (item.block); + } + return result; +} + void nano::scheduler::bucket::dump () const { for (auto const & item : queue) diff --git a/nano/node/scheduler/bucket.hpp b/nano/node/scheduler/bucket.hpp index 75b156fa1c..668bbec6d6 100644 --- a/nano/node/scheduler/bucket.hpp +++ b/nano/node/scheduler/bucket.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -10,6 +11,7 @@ #include #include +#include #include #include @@ -41,7 +43,9 @@ class priority_bucket_config final std::size_t max_elections{ 150 }; }; -/** A class which holds an ordered set of blocks to be scheduled, ordered by their block arrival time +/** + * A class which holds an ordered set of blocks to be scheduled, ordered by their block arrival time + * TODO: This combines both block ordering and election management, which makes the class harder to test. The functionality should be split. */ class bucket final { @@ -63,6 +67,8 @@ class bucket final size_t size () const; size_t election_count () const; bool empty () const; + std::deque> blocks () const; + void dump () const; private: