diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index 7deb41ee..9f38a208 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -2689,9 +2689,9 @@ TEST (node, epoch_conflict_confirm) nano::test::system system; nano::node_config node_config (nano::test::get_available_port (), system.logging); node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - auto node0 = system.add_node (node_config); + auto & node0 = *system.add_node (node_config); node_config.peering_port = nano::test::get_available_port (); - auto node1 = system.add_node (node_config); + auto & node1 = *system.add_node (node_config); nano::keypair key; nano::keypair epoch_signer (nano::dev::genesis_key); nano::state_block_builder builder; @@ -2736,44 +2736,44 @@ TEST (node, epoch_conflict_confirm) .previous (0) .representative (0) .balance (0) - .link (node0->ledger.epoch_link (nano::epoch::epoch_1)) + .link (node0.ledger.epoch_link (nano::epoch::epoch_1)) .sign (epoch_signer.prv, epoch_signer.pub) .work (*system.work.generate (open->hash ())) .build_shared (); - ASSERT_EQ (nano::process_result::progress, node1->process (*send).code); - ASSERT_EQ (nano::process_result::progress, node1->process (*send2).code); - ASSERT_EQ (nano::process_result::progress, node1->process (*open).code); - // Confirm block in node1 to allow generating votes - node1->block_confirm (open); - auto election (node1->active.election (open->qualified_root ())); - ASSERT_NE (nullptr, election); - election->force_confirm (); - ASSERT_TIMELY (3s, node1->block_confirmed (open->hash ())); - ASSERT_EQ (nano::process_result::progress, node0->process (*send).code); - ASSERT_EQ (nano::process_result::progress, node0->process (*send2).code); - ASSERT_EQ (nano::process_result::progress, node0->process (*open).code); - node0->process_active (change); - node0->process_active (epoch_open); - ASSERT_TIMELY (10s, node0->block (change->hash ()) && node0->block (epoch_open->hash ()) && node1->block (change->hash ()) && node1->block (epoch_open->hash ())); - // Confirm blocks in node1 to allow generating votes - nano::test::blocks_confirm (*node1, { change, epoch_open }, true /* forced */); - ASSERT_TIMELY (3s, node1->block_confirmed (change->hash ()) && node1->block_confirmed (epoch_open->hash ())); - // Start elections for node0 - nano::test::blocks_confirm (*node0, { change, epoch_open }); - ASSERT_EQ (2, node0->active.size ()); - { - nano::lock_guard lock (node0->active.mutex); - ASSERT_TRUE (node0->active.blocks.find (change->hash ()) != node0->active.blocks.end ()); - ASSERT_TRUE (node0->active.blocks.find (epoch_open->hash ()) != node0->active.blocks.end ()); - } + + // Process initial blocks on node1 + ASSERT_TRUE (nano::test::process (node1, { send, send2, open })); + + // Confirm open block in node1 to allow generating votes + ASSERT_TRUE (nano::test::confirm (node1, { open })); + ASSERT_TIMELY (5s, nano::test::confirmed (node1, { open })); + + // Process initial blocks on node0 + ASSERT_TRUE (nano::test::process (node0, { send, send2, open })); + + // Process conflicting blocks on node 0 as blocks coming from live network + ASSERT_TRUE (nano::test::process_live (node0, { change, epoch_open })); + + // Ensure blocks were propagated to both nodes + ASSERT_TIMELY (5s, nano::test::exists (node0, { change, epoch_open })); + ASSERT_TIMELY (5s, nano::test::exists (node1, { change, epoch_open })); + + // Confirm initial blocks in node1 to allow generating votes later + ASSERT_TRUE (nano::test::confirm (node1, { change, epoch_open, send2 })); + ASSERT_TIMELY (5s, nano::test::confirmed (node1, { change, epoch_open, send2 })); + + // Start elections for node0 for conflicting change and epoch_open blocks (those two blocks have the same root) + ASSERT_TRUE (nano::test::activate (node0, { change, epoch_open })); + ASSERT_TIMELY (5s, nano::test::active (node0, { change, epoch_open })); + + // Make node1 a representative system.wallet (1)->insert_adhoc (nano::dev::genesis_key.prv); - ASSERT_TIMELY (5s, node0->active.election (change->qualified_root ()) == nullptr); - ASSERT_TIMELY (5s, node0->active.empty ()); - { - auto transaction (node0->store.tx_begin_read ()); - ASSERT_TRUE (node0->ledger.store.block.exists (transaction, change->hash ())); - ASSERT_TRUE (node0->ledger.store.block.exists (transaction, epoch_open->hash ())); - } + + // Ensure the elections for conflicting blocks have completed + ASSERT_TIMELY (5s, nano::test::active (node0, { change, epoch_open })); + + // Ensure both conflicting blocks were successfully processed and confirmed + ASSERT_TIMELY (5s, nano::test::confirmed (node0, { change, epoch_open })); } // Test disabled because it's failing intermittently. diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 929e5430..80357ed8 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -511,6 +511,12 @@ bool nano::active_transactions::active (nano::block const & block_a) return roots.get ().find (block_a.qualified_root ()) != roots.get ().end () && blocks.find (block_a.hash ()) != blocks.end (); } +bool nano::active_transactions::active (const nano::block_hash & hash) +{ + nano::lock_guard guard{ mutex }; + return blocks.find (hash) != blocks.end (); +} + std::shared_ptr nano::active_transactions::election (nano::qualified_root const & root_a) const { std::shared_ptr result; diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index 2ea6495f..a8ae71ab 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -151,6 +151,10 @@ public: // Is the root of this block in the roots container bool active (nano::block const &); bool active (nano::qualified_root const &); + /* + * Is the block hash present in any active election + */ + bool active (nano::block_hash const &); std::shared_ptr election (nano::qualified_root const &) const; std::shared_ptr winner (nano::block_hash const &) const; // Returns a list of elections sorted by difficulty diff --git a/nano/test_common/testutil.cpp b/nano/test_common/testutil.cpp index 284a1b84..5718ee15 100644 --- a/nano/test_common/testutil.cpp +++ b/nano/test_common/testutil.cpp @@ -50,6 +50,15 @@ bool nano::test::process (nano::node & node, std::vector> blocks) +{ + for (auto & block : blocks) + { + node.process_active (block); + } + return true; +} + bool nano::test::confirm (nano::node & node, std::vector hashes) { // Finish processing all blocks @@ -79,9 +88,7 @@ bool nano::test::confirm (nano::node & node, std::vector hashe bool nano::test::confirm (nano::node & node, std::vector> blocks) { - std::vector hashes; - std::transform (blocks.begin (), blocks.end (), std::back_inserter (hashes), [] (auto & block) { return block->hash (); }); - return confirm (node, hashes); + return confirm (node, blocks_to_hashes (blocks)); } bool nano::test::confirmed (nano::node & node, std::vector hashes) @@ -98,9 +105,61 @@ bool nano::test::confirmed (nano::node & node, std::vector has bool nano::test::confirmed (nano::node & node, std::vector> blocks) { - std::vector hashes; - std::transform (blocks.begin (), blocks.end (), std::back_inserter (hashes), [] (auto & block) { return block->hash (); }); - return confirmed (node, hashes); + return confirmed (node, blocks_to_hashes (blocks)); +} + +bool nano::test::exists (nano::node & node, std::vector hashes) +{ + for (auto & hash : hashes) + { + if (!node.block (hash)) + { + return false; + } + } + return true; +} + +bool nano::test::exists (nano::node & node, std::vector> blocks) +{ + return exists (node, blocks_to_hashes (blocks)); +} + +bool nano::test::activate (nano::node & node, std::vector 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.manual (disk_block); + } + return true; +} + +bool nano::test::activate (nano::node & node, std::vector> blocks) +{ + return activate (node, blocks_to_hashes (blocks)); +} + +bool nano::test::active (nano::node & node, std::vector hashes) +{ + for (auto & hash : hashes) + { + if (!node.active.active (hash)) + { + return false; + } + } + return true; +} + +bool nano::test::active (nano::node & node, std::vector> blocks) +{ + return active (node, blocks_to_hashes (blocks)); } std::shared_ptr nano::test::make_vote (nano::keypair key, std::vector hashes, uint64_t timestamp, uint8_t duration) @@ -113,4 +172,11 @@ std::shared_ptr nano::test::make_vote (nano::keypair key, std::vecto std::vector hashes; std::transform (blocks.begin (), blocks.end (), std::back_inserter (hashes), [] (auto & block) { return block->hash (); }); return make_vote (key, hashes, timestamp, duration); +} + +std::vector nano::test::blocks_to_hashes (std::vector> blocks) +{ + std::vector hashes; + std::transform (blocks.begin (), blocks.end (), std::back_inserter (hashes), [] (auto & block) { return block->hash (); }); + return hashes; } \ No newline at end of file diff --git a/nano/test_common/testutil.hpp b/nano/test_common/testutil.hpp index 3e291031..b0a877fc 100644 --- a/nano/test_common/testutil.hpp +++ b/nano/test_common/testutil.hpp @@ -224,6 +224,12 @@ namespace test @return true if all blocks were successfully processed and inserted into ledger */ bool process (nano::node & node, std::vector> blocks); + /* + * Convenience function to process multiple blocks as if they were live blocks arriving from the network + * It is not guaranted that those blocks will be inserted into ledger (there might be forks, missing links etc) + * @return true if all blocks were successfully processed + */ + bool process_live (nano::node & node, std::vector> blocks); /* * Convenience function to confirm a list of blocks * The actual confirmation will happen asynchronously, check for that with `nano::test::confirmed (..)` function @@ -246,6 +252,36 @@ namespace test * @return true if all blocks are confirmed, false otherwise */ bool confirmed (nano::node & node, std::vector hashes); + /* + * Convenience function to check whether a list of hashes exists in node ledger. + * @return true if all blocks are fully processed and inserted in the ledger, false otherwise + */ + bool exists (nano::node & node, std::vector hashes); + /* + * Convenience function to check whether a list of blocks exists in node ledger. + * @return true if all blocks are fully processed and inserted in the ledger, false otherwise + */ + bool exists (nano::node & node, std::vector> blocks); + /* + * Convenience function to start elections for a list of hashes. Blocks are loaded from ledger. + * @return true if all blocks exist and were queued to election scheduler + */ + bool activate (nano::node & node, std::vector hashes); + /* + * Convenience function to start elections for a list of hashes. Blocks are loaded from ledger. + * @return true if all blocks exist and were queued to election scheduler + */ + bool activate (nano::node & node, std::vector> blocks); + /* + * Convenience function that checks whether all hashes from list have currently active elections + * @return true if all blocks have currently active elections, false othersie + */ + bool active (nano::node & node, std::vector hashes); + /* + * Convenience function that checks whether all hashes from list have currently active elections + * @return true if all blocks have currently active elections, false othersie + */ + bool active (nano::node & node, std::vector> blocks); /* * Convenience function to create a new vote from list of blocks */ @@ -254,5 +290,9 @@ namespace test * Convenience function to create a new vote from list of block hashes */ std::shared_ptr make_vote (nano::keypair key, std::vector hashes, uint64_t timestamp = 0, uint8_t duration = 0); + /* + * Converts list of blocks to list of hashes + */ + std::vector blocks_to_hashes (std::vector> blocks); } } \ No newline at end of file