Fix epoch_conflict_confirm test (#3906)

* Add test utility functions

* Fix `epoch_conflict_confirm` test

* Renaming
This commit is contained in:
Piotr Wójcik 2022-08-30 19:36:24 +02:00 committed by GitHub
commit 55625db016
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 158 additions and 42 deletions

View file

@ -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<nano::mutex> 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.

View file

@ -511,6 +511,12 @@ bool nano::active_transactions::active (nano::block const & block_a)
return roots.get<tag_root> ().find (block_a.qualified_root ()) != roots.get<tag_root> ().end () && blocks.find (block_a.hash ()) != blocks.end ();
}
bool nano::active_transactions::active (const nano::block_hash & hash)
{
nano::lock_guard<nano::mutex> guard{ mutex };
return blocks.find (hash) != blocks.end ();
}
std::shared_ptr<nano::election> nano::active_transactions::election (nano::qualified_root const & root_a) const
{
std::shared_ptr<nano::election> result;

View file

@ -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<nano::election> election (nano::qualified_root const &) const;
std::shared_ptr<nano::block> winner (nano::block_hash const &) const;
// Returns a list of elections sorted by difficulty

View file

@ -50,6 +50,15 @@ bool nano::test::process (nano::node & node, std::vector<std::shared_ptr<nano::b
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
@ -79,9 +88,7 @@ bool nano::test::confirm (nano::node & node, std::vector<nano::block_hash> hashe
bool nano::test::confirm (nano::node & node, 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 confirm (node, hashes);
return confirm (node, blocks_to_hashes (blocks));
}
bool nano::test::confirmed (nano::node & node, std::vector<nano::block_hash> hashes)
@ -98,9 +105,61 @@ bool nano::test::confirmed (nano::node & node, std::vector<nano::block_hash> has
bool nano::test::confirmed (nano::node & node, 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 confirmed (node, hashes);
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.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)
@ -113,4 +172,11 @@ std::shared_ptr<nano::vote> nano::test::make_vote (nano::keypair key, std::vecto
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::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;
}

View file

@ -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<std::shared_ptr<nano::block>> 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<std::shared_ptr<nano::block>> 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<nano::block_hash> 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<nano::block_hash> 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<std::shared_ptr<nano::block>> 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<nano::block_hash> 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<std::shared_ptr<nano::block>> 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<nano::block_hash> 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<std::shared_ptr<nano::block>> 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<nano::vote> make_vote (nano::keypair key, std::vector<nano::block_hash> hashes, uint64_t timestamp = 0, uint8_t duration = 0);
/*
* Converts list of blocks to list of hashes
*/
std::vector<nano::block_hash> blocks_to_hashes (std::vector<std::shared_ptr<nano::block>> blocks);
}
}