376 lines
11 KiB
C++
376 lines
11 KiB
C++
#include <nano/crypto_lib/random_pool.hpp>
|
|
#include <nano/lib/blocks.hpp>
|
|
#include <nano/node/active_elections.hpp>
|
|
#include <nano/node/election.hpp>
|
|
#include <nano/node/scheduler/component.hpp>
|
|
#include <nano/node/scheduler/manual.hpp>
|
|
#include <nano/node/scheduler/priority.hpp>
|
|
#include <nano/node/transport/fake.hpp>
|
|
#include <nano/node/vote_router.hpp>
|
|
#include <nano/secure/ledger.hpp>
|
|
#include <nano/secure/ledger_set_any.hpp>
|
|
#include <nano/secure/ledger_set_confirmed.hpp>
|
|
#include <nano/store/block.hpp>
|
|
#include <nano/test_common/system.hpp>
|
|
#include <nano/test_common/testutil.hpp>
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <cstdlib>
|
|
#include <numeric>
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
void nano::test::wait_peer_connections (nano::test::system & system_a)
|
|
{
|
|
auto wait_peer_count = [&system_a] (bool in_memory) {
|
|
auto num_nodes = system_a.nodes.size ();
|
|
system_a.deadline_set (20s);
|
|
size_t peer_count = 0;
|
|
while (peer_count != num_nodes * (num_nodes - 1))
|
|
{
|
|
ASSERT_NO_ERROR (system_a.poll ());
|
|
peer_count = std::accumulate (system_a.nodes.cbegin (), system_a.nodes.cend (), std::size_t{ 0 }, [in_memory] (auto total, auto const & node) {
|
|
if (in_memory)
|
|
{
|
|
return total += node->network.size ();
|
|
}
|
|
else
|
|
{
|
|
auto transaction = node->store.tx_begin_read ();
|
|
return total += node->store.peer.count (transaction);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
// Do a pre-pass with in-memory containers to reduce IO if still in the process of connecting to peers
|
|
wait_peer_count (true);
|
|
wait_peer_count (false);
|
|
}
|
|
|
|
nano::hash_or_account nano::test::random_hash_or_account ()
|
|
{
|
|
nano::hash_or_account random_hash;
|
|
nano::random_pool::generate_block (random_hash.bytes.data (), random_hash.bytes.size ());
|
|
return random_hash;
|
|
}
|
|
|
|
nano::block_hash nano::test::random_hash ()
|
|
{
|
|
return nano::test::random_hash_or_account ().as_block_hash ();
|
|
}
|
|
|
|
nano::account nano::test::random_account ()
|
|
{
|
|
return nano::test::random_hash_or_account ().as_account ();
|
|
}
|
|
|
|
bool nano::test::process (nano::node & node, std::vector<std::shared_ptr<nano::block>> blocks)
|
|
{
|
|
auto const transaction = node.ledger.tx_begin_write ();
|
|
for (auto & block : blocks)
|
|
{
|
|
auto result = node.process (transaction, block);
|
|
if (result != nano::block_status::progress && result != nano::block_status::old)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
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::confirmed (nano::node & node, std::vector<nano::block_hash> hashes)
|
|
{
|
|
for (auto & hash : hashes)
|
|
{
|
|
if (!node.block_confirmed (hash))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool nano::test::confirmed (nano::node & node, std::vector<std::shared_ptr<nano::block>> blocks)
|
|
{
|
|
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));
|
|
}
|
|
|
|
void nano::test::confirm (nano::ledger & ledger, std::vector<std::shared_ptr<nano::block>> const blocks)
|
|
{
|
|
for (auto const block : blocks)
|
|
{
|
|
confirm (ledger, block);
|
|
}
|
|
}
|
|
|
|
void nano::test::confirm (nano::ledger & ledger, std::shared_ptr<nano::block> const block)
|
|
{
|
|
confirm (ledger, block->hash ());
|
|
}
|
|
|
|
void nano::test::confirm (nano::ledger & ledger, nano::block_hash const & hash)
|
|
{
|
|
auto transaction = ledger.tx_begin_write ();
|
|
ledger.confirm (transaction, hash);
|
|
}
|
|
|
|
bool nano::test::block_or_pruned_all_exists (nano::node & node, std::vector<nano::block_hash> hashes)
|
|
{
|
|
auto transaction = node.ledger.tx_begin_read ();
|
|
return std::all_of (hashes.begin (), hashes.end (),
|
|
[&] (const auto & hash) {
|
|
return node.ledger.any.block_exists_or_pruned (transaction, hash);
|
|
});
|
|
}
|
|
|
|
bool nano::test::block_or_pruned_all_exists (nano::node & node, std::vector<std::shared_ptr<nano::block>> blocks)
|
|
{
|
|
return block_or_pruned_all_exists (node, blocks_to_hashes (blocks));
|
|
}
|
|
|
|
bool nano::test::block_or_pruned_none_exists (nano::node & node, std::vector<nano::block_hash> hashes)
|
|
{
|
|
auto transaction = node.ledger.tx_begin_read ();
|
|
return std::none_of (hashes.begin (), hashes.end (),
|
|
[&] (const auto & hash) {
|
|
return node.ledger.any.block_exists_or_pruned (transaction, hash);
|
|
});
|
|
}
|
|
|
|
bool nano::test::block_or_pruned_none_exists (nano::node & node, std::vector<std::shared_ptr<nano::block>> blocks)
|
|
{
|
|
return block_or_pruned_none_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.push (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.vote_router.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)
|
|
{
|
|
return std::make_shared<nano::vote> (key.pub, key.prv, timestamp, duration, hashes);
|
|
}
|
|
|
|
std::shared_ptr<nano::vote> nano::test::make_vote (nano::keypair key, std::vector<std::shared_ptr<nano::block>> blocks, uint64_t timestamp, uint8_t duration)
|
|
{
|
|
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::shared_ptr<nano::vote> nano::test::make_final_vote (nano::keypair key, std::vector<nano::block_hash> hashes)
|
|
{
|
|
return make_vote (key, hashes, nano::vote::timestamp_max, nano::vote::duration_max);
|
|
}
|
|
|
|
std::shared_ptr<nano::vote> nano::test::make_final_vote (nano::keypair key, std::vector<std::shared_ptr<nano::block>> blocks)
|
|
{
|
|
return make_vote (key, blocks, nano::vote::timestamp_max, nano::vote::duration_max);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
std::shared_ptr<nano::transport::fake::channel> nano::test::fake_channel (nano::node & node, nano::account node_id)
|
|
{
|
|
auto channel = std::make_shared<nano::transport::fake::channel> (node);
|
|
if (!node_id.is_zero ())
|
|
{
|
|
channel->set_node_id (node_id);
|
|
}
|
|
return channel;
|
|
}
|
|
|
|
std::shared_ptr<nano::election> nano::test::start_election (nano::test::system & system_a, nano::node & node_a, const nano::block_hash & hash_a)
|
|
{
|
|
system_a.deadline_set (5s);
|
|
|
|
// wait until and ensure that the block is in the ledger
|
|
auto block_l = node_a.block (hash_a);
|
|
while (!block_l)
|
|
{
|
|
if (system_a.poll ())
|
|
{
|
|
return nullptr;
|
|
}
|
|
block_l = node_a.block (hash_a);
|
|
}
|
|
|
|
node_a.scheduler.manual.push (block_l);
|
|
|
|
// wait for the election to appear
|
|
std::shared_ptr<nano::election> election = node_a.active.election (block_l->qualified_root ());
|
|
while (!election)
|
|
{
|
|
if (system_a.poll ())
|
|
{
|
|
return nullptr;
|
|
}
|
|
election = node_a.active.election (block_l->qualified_root ());
|
|
}
|
|
|
|
election->transition_active ();
|
|
return election;
|
|
}
|
|
|
|
bool nano::test::start_elections (nano::test::system & system_a, nano::node & node_a, std::vector<nano::block_hash> const & hashes_a, bool const forced_a)
|
|
{
|
|
for (auto const & hash_l : hashes_a)
|
|
{
|
|
auto election = nano::test::start_election (system_a, node_a, hash_l);
|
|
if (!election)
|
|
{
|
|
return false;
|
|
}
|
|
if (forced_a)
|
|
{
|
|
election->force_confirm ();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool nano::test::start_elections (nano::test::system & system_a, nano::node & node_a, std::vector<std::shared_ptr<nano::block>> const & blocks_a, bool const forced_a)
|
|
{
|
|
return nano::test::start_elections (system_a, node_a, blocks_to_hashes (blocks_a), forced_a);
|
|
}
|
|
|
|
nano::account_info nano::test::account_info (nano::node const & node, nano::account const & acc)
|
|
{
|
|
auto const tx = node.ledger.tx_begin_read ();
|
|
auto opt = node.ledger.any.account_get (tx, acc);
|
|
if (opt.has_value ())
|
|
{
|
|
return opt.value ();
|
|
}
|
|
return {};
|
|
}
|
|
|
|
void nano::test::print_all_receivable_entries (const nano::store::component & store)
|
|
{
|
|
std::cout << "Printing all receivable entries:\n";
|
|
auto const tx = store.tx_begin_read ();
|
|
auto const end = store.pending.end (tx);
|
|
for (auto i = store.pending.begin (tx); i != end; ++i)
|
|
{
|
|
std::cout << "Key: " << i->first << std::endl;
|
|
std::cout << "Info: " << i->second << std::endl;
|
|
}
|
|
}
|
|
|
|
void nano::test::print_all_account_info (const nano::ledger & ledger)
|
|
{
|
|
std::cout << "Printing all account info:\n";
|
|
auto const tx = ledger.tx_begin_read ();
|
|
auto const end = ledger.store.account.end (tx);
|
|
for (auto i = ledger.store.account.begin (tx); i != end; ++i)
|
|
{
|
|
nano::account acc = i->first;
|
|
nano::account_info acc_info = i->second;
|
|
nano::confirmation_height_info height_info;
|
|
std::cout << "Account: " << acc.to_account () << std::endl;
|
|
std::cout << " Unconfirmed Balance: " << acc_info.balance.to_string_dec () << std::endl;
|
|
std::cout << " Confirmed Balance: " << ledger.confirmed.account_balance (tx, acc).value_or (0) << std::endl;
|
|
std::cout << " Block Count: " << acc_info.block_count << std::endl;
|
|
if (!ledger.store.confirmation_height.get (tx, acc, height_info))
|
|
{
|
|
std::cout << " Conf. Height: " << height_info.height << std::endl;
|
|
std::cout << " Conf. Frontier: " << height_info.frontier.to_string () << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
void nano::test::print_all_blocks (const nano::store::component & store)
|
|
{
|
|
auto tx = store.tx_begin_read ();
|
|
auto i = store.block.begin (tx);
|
|
auto end = store.block.end (tx);
|
|
std::cout << "Listing all blocks" << std::endl;
|
|
for (; i != end; ++i)
|
|
{
|
|
nano::block_hash hash = i->first;
|
|
nano::store::block_w_sideband sideband = i->second;
|
|
std::shared_ptr<nano::block> b = sideband.block;
|
|
std::cout << "Hash: " << hash.to_string () << std::endl;
|
|
const auto acc = sideband.sideband.account;
|
|
std::cout << "Acc: " << acc.to_string () << "(" << acc.to_account () << ")" << std::endl;
|
|
std::cout << "Height: " << sideband.sideband.height << std::endl;
|
|
std::cout << b->to_json ();
|
|
}
|
|
}
|
|
|
|
std::vector<std::shared_ptr<nano::block>> nano::test::all_blocks (nano::node & node)
|
|
{
|
|
auto transaction = node.store.tx_begin_read ();
|
|
std::vector<std::shared_ptr<nano::block>> result;
|
|
for (auto it = node.store.block.begin (transaction), end = node.store.block.end (transaction); it != end; ++it)
|
|
{
|
|
result.push_back (it->second.block);
|
|
}
|
|
return result;
|
|
}
|