Add lazy bootstrap possible links and accounts (#2315)

* request bulk_pull for state blocks where previous block is legacy receive, open or change (doesn't contain balance in block)
* request bulk_pull for most used destinations accounts
* allow to drop lazy bulk_pulls after failed attempt for destiations or unknown state blocks
* split bootstrap core_test
* allow hash_or_account to be concrete
* improve requeue invalid signature function
* add bootstrap attempt duration
This commit is contained in:
Sergey Kroshnin 2019-10-11 21:31:15 +03:00 committed by GitHub
commit 65da97fec2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 1160 additions and 941 deletions

View file

@ -4,6 +4,7 @@ add_executable (core_test
active_transactions.cpp
block.cpp
block_store.cpp
bootstrap.cpp
confirmation_height.cpp
conflicts.cpp
difficulty.cpp

View file

@ -0,0 +1,819 @@
#include <nano/core_test/testutil.hpp>
#include <nano/node/testing.hpp>
#include <gtest/gtest.h>
using namespace std::chrono_literals;
// If the account doesn't exist, current == end so there's no iteration
TEST (bulk_pull, no_address)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::bulk_pull> req (new nano::bulk_pull);
req->start = 1;
req->end = 2;
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::bulk_pull_server> (connection, std::move (req)));
ASSERT_EQ (request->current, request->request->end);
ASSERT_TRUE (request->current.is_zero ());
}
TEST (bulk_pull, genesis_to_end)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::bulk_pull> req (new nano::bulk_pull{});
req->start = nano::test_genesis_key.pub;
req->end.clear ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::bulk_pull_server> (connection, std::move (req)));
ASSERT_EQ (system.nodes[0]->latest (nano::test_genesis_key.pub), request->current);
ASSERT_EQ (request->request->end, request->request->end);
}
// If we can't find the end block, send everything
TEST (bulk_pull, no_end)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::bulk_pull> req (new nano::bulk_pull{});
req->start = nano::test_genesis_key.pub;
req->end = 1;
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::bulk_pull_server> (connection, std::move (req)));
ASSERT_EQ (system.nodes[0]->latest (nano::test_genesis_key.pub), request->current);
ASSERT_TRUE (request->request->end.is_zero ());
}
TEST (bulk_pull, end_not_owned)
{
nano::system system (24000, 1);
nano::keypair key2;
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
ASSERT_NE (nullptr, system.wallet (0)->send_action (nano::test_genesis_key.pub, key2.pub, 100));
nano::block_hash latest (system.nodes[0]->latest (nano::test_genesis_key.pub));
nano::open_block open (0, 1, 2, nano::keypair ().prv, 4, 5);
open.hashables.account = key2.pub;
open.hashables.representative = key2.pub;
open.hashables.source = latest;
open.signature = nano::sign_message (key2.prv, key2.pub, open.hash ());
system.nodes[0]->work_generate_blocking (open);
ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (open).code);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
nano::genesis genesis;
std::unique_ptr<nano::bulk_pull> req (new nano::bulk_pull{});
req->start = key2.pub;
req->end = genesis.hash ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::bulk_pull_server> (connection, std::move (req)));
ASSERT_EQ (request->current, request->request->end);
}
TEST (bulk_pull, none)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
nano::genesis genesis;
std::unique_ptr<nano::bulk_pull> req (new nano::bulk_pull{});
req->start = nano::test_genesis_key.pub;
req->end = genesis.hash ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::bulk_pull_server> (connection, std::move (req)));
auto block (request->get_next ());
ASSERT_EQ (nullptr, block);
}
TEST (bulk_pull, get_next_on_open)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::bulk_pull> req (new nano::bulk_pull{});
req->start = nano::test_genesis_key.pub;
req->end.clear ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::bulk_pull_server> (connection, std::move (req)));
auto block (request->get_next ());
ASSERT_NE (nullptr, block);
ASSERT_TRUE (block->previous ().is_zero ());
ASSERT_FALSE (connection->requests.empty ());
ASSERT_EQ (request->current, request->request->end);
}
TEST (bulk_pull, by_block)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
nano::genesis genesis;
std::unique_ptr<nano::bulk_pull> req (new nano::bulk_pull{});
req->start = genesis.hash ();
req->end.clear ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::bulk_pull_server> (connection, std::move (req)));
auto block (request->get_next ());
ASSERT_NE (nullptr, block);
ASSERT_EQ (block->hash (), genesis.hash ());
block = request->get_next ();
ASSERT_EQ (nullptr, block);
}
TEST (bulk_pull, by_block_single)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
nano::genesis genesis;
std::unique_ptr<nano::bulk_pull> req (new nano::bulk_pull{});
req->start = genesis.hash ();
req->end = genesis.hash ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::bulk_pull_server> (connection, std::move (req)));
auto block (request->get_next ());
ASSERT_NE (nullptr, block);
ASSERT_EQ (block->hash (), genesis.hash ());
block = request->get_next ();
ASSERT_EQ (nullptr, block);
}
TEST (bulk_pull, count_limit)
{
nano::system system (24000, 1);
nano::genesis genesis;
auto send1 (std::make_shared<nano::send_block> (system.nodes[0]->latest (nano::test_genesis_key.pub), nano::test_genesis_key.pub, 1, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (system.nodes[0]->latest (nano::test_genesis_key.pub))));
ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (*send1).code);
auto receive1 (std::make_shared<nano::receive_block> (send1->hash (), send1->hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send1->hash ())));
ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (*receive1).code);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::bulk_pull> req (new nano::bulk_pull{});
req->start = receive1->hash ();
req->set_count_present (true);
req->count = 2;
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::bulk_pull_server> (connection, std::move (req)));
ASSERT_EQ (request->max_count, 2);
ASSERT_EQ (request->sent_count, 0);
auto block (request->get_next ());
ASSERT_EQ (receive1->hash (), block->hash ());
block = request->get_next ();
ASSERT_EQ (send1->hash (), block->hash ());
block = request->get_next ();
ASSERT_EQ (nullptr, block);
}
TEST (bootstrap_processor, DISABLED_process_none)
{
nano::system system (24000, 1);
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
ASSERT_FALSE (node1->init_error ());
auto done (false);
node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint ());
while (!done)
{
system.io_ctx.run_one ();
}
node1->stop ();
}
// Bootstrap can pull one basic block
TEST (bootstrap_processor, process_one)
{
nano::system system (24000, 1);
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
ASSERT_NE (nullptr, system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::test_genesis_key.pub, 100));
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
nano::block_hash hash1 (system.nodes[0]->latest (nano::test_genesis_key.pub));
nano::block_hash hash2 (node1->latest (nano::test_genesis_key.pub));
ASSERT_NE (hash1, hash2);
node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint ());
ASSERT_NE (node1->latest (nano::test_genesis_key.pub), system.nodes[0]->latest (nano::test_genesis_key.pub));
system.deadline_set (10s);
while (node1->latest (nano::test_genesis_key.pub) != system.nodes[0]->latest (nano::test_genesis_key.pub))
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (0, node1->active.size ());
node1->stop ();
}
TEST (bootstrap_processor, process_two)
{
nano::system system (24000, 1);
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
nano::block_hash hash1 (system.nodes[0]->latest (nano::test_genesis_key.pub));
ASSERT_NE (nullptr, system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::test_genesis_key.pub, 50));
nano::block_hash hash2 (system.nodes[0]->latest (nano::test_genesis_key.pub));
ASSERT_NE (nullptr, system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::test_genesis_key.pub, 50));
nano::block_hash hash3 (system.nodes[0]->latest (nano::test_genesis_key.pub));
ASSERT_NE (hash1, hash2);
ASSERT_NE (hash1, hash3);
ASSERT_NE (hash2, hash3);
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
ASSERT_FALSE (node1->init_error ());
node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint ());
ASSERT_NE (node1->latest (nano::test_genesis_key.pub), system.nodes[0]->latest (nano::test_genesis_key.pub));
system.deadline_set (10s);
while (node1->latest (nano::test_genesis_key.pub) != system.nodes[0]->latest (nano::test_genesis_key.pub))
{
ASSERT_NO_ERROR (system.poll ());
}
node1->stop ();
}
// Bootstrap can pull universal blocks
TEST (bootstrap_processor, process_state)
{
nano::system system (24000, 1);
nano::genesis genesis;
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
auto node0 (system.nodes[0]);
auto block1 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, node0->latest (nano::test_genesis_key.pub), nano::test_genesis_key.pub, nano::genesis_amount - 100, nano::test_genesis_key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0));
auto block2 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, block1->hash (), nano::test_genesis_key.pub, nano::genesis_amount, block1->hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0));
node0->work_generate_blocking (*block1);
node0->work_generate_blocking (*block2);
node0->process (*block1);
node0->process (*block2);
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
ASSERT_EQ (node0->latest (nano::test_genesis_key.pub), block2->hash ());
ASSERT_NE (node1->latest (nano::test_genesis_key.pub), block2->hash ());
node1->bootstrap_initiator.bootstrap (node0->network.endpoint ());
ASSERT_NE (node1->latest (nano::test_genesis_key.pub), node0->latest (nano::test_genesis_key.pub));
system.deadline_set (10s);
while (node1->latest (nano::test_genesis_key.pub) != node0->latest (nano::test_genesis_key.pub))
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (0, node1->active.size ());
node1->stop ();
}
TEST (bootstrap_processor, process_new)
{
nano::system system (24000, 2);
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
nano::keypair key2;
system.wallet (1)->insert_adhoc (key2.prv);
ASSERT_NE (nullptr, system.wallet (0)->send_action (nano::test_genesis_key.pub, key2.pub, system.nodes[0]->config.receive_minimum.number ()));
system.deadline_set (10s);
while (system.nodes[0]->balance (key2.pub).is_zero ())
{
ASSERT_NO_ERROR (system.poll ());
}
nano::uint128_t balance1 (system.nodes[0]->balance (nano::test_genesis_key.pub));
nano::uint128_t balance2 (system.nodes[0]->balance (key2.pub));
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24002, nano::unique_path (), system.alarm, system.logging, system.work));
ASSERT_FALSE (node1->init_error ());
node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint ());
system.deadline_set (10s);
while (node1->balance (key2.pub) != balance2)
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (balance1, node1->balance (nano::test_genesis_key.pub));
node1->stop ();
}
TEST (bootstrap_processor, pull_diamond)
{
nano::system system (24000, 1);
nano::keypair key;
auto send1 (std::make_shared<nano::send_block> (system.nodes[0]->latest (nano::test_genesis_key.pub), key.pub, 0, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (system.nodes[0]->latest (nano::test_genesis_key.pub))));
ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (*send1).code);
auto open (std::make_shared<nano::open_block> (send1->hash (), 1, key.pub, key.prv, key.pub, *system.work.generate (key.pub)));
ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (*open).code);
auto send2 (std::make_shared<nano::send_block> (open->hash (), nano::test_genesis_key.pub, std::numeric_limits<nano::uint128_t>::max () - 100, key.prv, key.pub, *system.work.generate (open->hash ())));
ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (*send2).code);
auto receive (std::make_shared<nano::receive_block> (send1->hash (), send2->hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send1->hash ())));
ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (*receive).code);
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24002, nano::unique_path (), system.alarm, system.logging, system.work));
ASSERT_FALSE (node1->init_error ());
node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint ());
system.deadline_set (10s);
while (node1->balance (nano::test_genesis_key.pub) != 100)
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (100, node1->balance (nano::test_genesis_key.pub));
node1->stop ();
}
TEST (bootstrap_processor, push_diamond)
{
nano::system system (24000, 1);
nano::keypair key;
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24002, nano::unique_path (), system.alarm, system.logging, system.work));
ASSERT_FALSE (node1->init_error ());
auto wallet1 (node1->wallets.create (100));
wallet1->insert_adhoc (nano::test_genesis_key.prv);
wallet1->insert_adhoc (key.prv);
auto send1 (std::make_shared<nano::send_block> (system.nodes[0]->latest (nano::test_genesis_key.pub), key.pub, 0, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (system.nodes[0]->latest (nano::test_genesis_key.pub))));
ASSERT_EQ (nano::process_result::progress, node1->process (*send1).code);
auto open (std::make_shared<nano::open_block> (send1->hash (), 1, key.pub, key.prv, key.pub, *system.work.generate (key.pub)));
ASSERT_EQ (nano::process_result::progress, node1->process (*open).code);
auto send2 (std::make_shared<nano::send_block> (open->hash (), nano::test_genesis_key.pub, std::numeric_limits<nano::uint128_t>::max () - 100, key.prv, key.pub, *system.work.generate (open->hash ())));
ASSERT_EQ (nano::process_result::progress, node1->process (*send2).code);
auto receive (std::make_shared<nano::receive_block> (send1->hash (), send2->hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send1->hash ())));
ASSERT_EQ (nano::process_result::progress, node1->process (*receive).code);
node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint ());
system.deadline_set (10s);
while (system.nodes[0]->balance (nano::test_genesis_key.pub) != 100)
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (100, system.nodes[0]->balance (nano::test_genesis_key.pub));
node1->stop ();
}
TEST (bootstrap_processor, push_one)
{
nano::system system (24000, 1);
nano::keypair key1;
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
auto wallet (node1->wallets.create (nano::random_wallet_id ()));
ASSERT_NE (nullptr, wallet);
wallet->insert_adhoc (nano::test_genesis_key.prv);
nano::uint128_t balance1 (node1->balance (nano::test_genesis_key.pub));
ASSERT_NE (nullptr, wallet->send_action (nano::test_genesis_key.pub, key1.pub, 100));
ASSERT_NE (balance1, node1->balance (nano::test_genesis_key.pub));
node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint ());
system.deadline_set (10s);
while (system.nodes[0]->balance (nano::test_genesis_key.pub) == balance1)
{
ASSERT_NO_ERROR (system.poll ());
}
node1->stop ();
}
TEST (bootstrap_processor, lazy_hash)
{
nano::system system (24000, 1);
nano::genesis genesis;
nano::keypair key1;
nano::keypair key2;
// Generating test chain
auto send1 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key1.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.nodes[0]->work_generate_blocking (genesis.hash ())));
auto receive1 (std::make_shared<nano::state_block> (key1.pub, 0, key1.pub, nano::Gxrb_ratio, send1->hash (), key1.prv, key1.pub, *system.nodes[0]->work_generate_blocking (key1.pub)));
auto send2 (std::make_shared<nano::state_block> (key1.pub, receive1->hash (), key1.pub, 0, key2.pub, key1.prv, key1.pub, *system.nodes[0]->work_generate_blocking (receive1->hash ())));
auto receive2 (std::make_shared<nano::state_block> (key2.pub, 0, key2.pub, nano::Gxrb_ratio, send2->hash (), key2.prv, key2.pub, *system.nodes[0]->work_generate_blocking (key2.pub)));
// Processing test chain
system.nodes[0]->block_processor.add (send1);
system.nodes[0]->block_processor.add (receive1);
system.nodes[0]->block_processor.add (send2);
system.nodes[0]->block_processor.add (receive2);
system.nodes[0]->block_processor.flush ();
// Start lazy bootstrap with last block in chain known
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
node1->network.udp_channels.insert (system.nodes[0]->network.endpoint (), node1->network_params.protocol.protocol_version);
node1->bootstrap_initiator.bootstrap_lazy (receive2->hash ());
// Check processed blocks
system.deadline_set (10s);
while (node1->balance (key2.pub) == 0)
{
ASSERT_NO_ERROR (system.poll ());
}
node1->stop ();
}
TEST (bootstrap_processor, lazy_max_pull_count)
{
nano::system system (24000, 1);
nano::genesis genesis;
nano::keypair key1;
nano::keypair key2;
// Generating test chain
auto send1 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key1.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.nodes[0]->work_generate_blocking (genesis.hash ())));
auto receive1 (std::make_shared<nano::state_block> (key1.pub, 0, key1.pub, nano::Gxrb_ratio, send1->hash (), key1.prv, key1.pub, *system.nodes[0]->work_generate_blocking (key1.pub)));
auto send2 (std::make_shared<nano::state_block> (key1.pub, receive1->hash (), key1.pub, 0, key2.pub, key1.prv, key1.pub, *system.nodes[0]->work_generate_blocking (receive1->hash ())));
auto receive2 (std::make_shared<nano::state_block> (key2.pub, 0, key2.pub, nano::Gxrb_ratio, send2->hash (), key2.prv, key2.pub, *system.nodes[0]->work_generate_blocking (key2.pub)));
auto change1 (std::make_shared<nano::state_block> (key2.pub, receive2->hash (), key1.pub, nano::Gxrb_ratio, 0, key2.prv, key2.pub, *system.nodes[0]->work_generate_blocking (receive2->hash ())));
auto change2 (std::make_shared<nano::state_block> (key2.pub, change1->hash (), nano::test_genesis_key.pub, nano::Gxrb_ratio, 0, key2.prv, key2.pub, *system.nodes[0]->work_generate_blocking (change1->hash ())));
auto change3 (std::make_shared<nano::state_block> (key2.pub, change2->hash (), key2.pub, nano::Gxrb_ratio, 0, key2.prv, key2.pub, *system.nodes[0]->work_generate_blocking (change2->hash ())));
// Processing test chain
system.nodes[0]->block_processor.add (send1);
system.nodes[0]->block_processor.add (receive1);
system.nodes[0]->block_processor.add (send2);
system.nodes[0]->block_processor.add (receive2);
system.nodes[0]->block_processor.add (change1);
system.nodes[0]->block_processor.add (change2);
system.nodes[0]->block_processor.add (change3);
system.nodes[0]->block_processor.flush ();
// Start lazy bootstrap with last block in chain known
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
node1->network.udp_channels.insert (system.nodes[0]->network.endpoint (), node1->network_params.protocol.protocol_version);
node1->bootstrap_initiator.bootstrap_lazy (change3->hash ());
// Check processed blocks
system.deadline_set (10s);
while (node1->block (change3->hash ()) == nullptr)
{
ASSERT_NO_ERROR (system.poll ());
}
node1->stop ();
}
TEST (bootstrap_processor, lazy_unclear_state_link)
{
nano::system system;
nano::node_flags node_flags;
node_flags.disable_legacy_bootstrap = true;
auto node1 = system.add_node (nano::node_config (24000, system.logging), node_flags);
nano::genesis genesis;
nano::keypair key;
// Generating test chain
auto send1 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (genesis.hash ())));
ASSERT_EQ (nano::process_result::progress, node1->process (*send1).code);
auto send2 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, send1->hash (), nano::test_genesis_key.pub, nano::genesis_amount - 2 * nano::Gxrb_ratio, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send1->hash ())));
ASSERT_EQ (nano::process_result::progress, node1->process (*send2).code);
auto open (std::make_shared<nano::open_block> (send1->hash (), key.pub, key.pub, key.prv, key.pub, *system.work.generate (key.pub)));
ASSERT_EQ (nano::process_result::progress, node1->process (*open).code);
auto receive (std::make_shared<nano::state_block> (key.pub, open->hash (), key.pub, 2 * nano::Gxrb_ratio, send2->hash (), key.prv, key.pub, *system.work.generate (open->hash ()))); // It is not possible to define this block send/receive status based on previous block (legacy open)
ASSERT_EQ (nano::process_result::progress, node1->process (*receive).code);
// Start lazy bootstrap with last block in chain known
auto node2 = system.add_node (nano::node_config (24001, system.logging), node_flags);
node2->network.udp_channels.insert (node1->network.endpoint (), node1->network_params.protocol.protocol_version);
node2->bootstrap_initiator.bootstrap_lazy (receive->hash ());
// Check processed blocks
system.deadline_set (10s);
while (node2->bootstrap_initiator.in_progress ())
{
ASSERT_NO_ERROR (system.poll ());
}
node2->block_processor.flush ();
ASSERT_TRUE (node2->ledger.block_exists (send1->hash ()));
ASSERT_TRUE (node2->ledger.block_exists (send2->hash ()));
ASSERT_TRUE (node2->ledger.block_exists (open->hash ()));
ASSERT_TRUE (node2->ledger.block_exists (receive->hash ()));
ASSERT_EQ (0, node2->stats.count (nano::stat::type::bootstrap, nano::stat::detail::bulk_pull_failed_account, nano::stat::dir::in));
}
TEST (bootstrap_processor, lazy_unclear_state_link_not_existing)
{
nano::system system;
nano::node_flags node_flags;
node_flags.disable_legacy_bootstrap = true;
auto node1 = system.add_node (nano::node_config (24000, system.logging), node_flags);
nano::genesis genesis;
nano::keypair key, key2;
// Generating test chain
auto send1 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (genesis.hash ())));
ASSERT_EQ (nano::process_result::progress, node1->process (*send1).code);
auto send2 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, send1->hash (), nano::test_genesis_key.pub, nano::genesis_amount - 2 * nano::Gxrb_ratio, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send1->hash ())));
ASSERT_EQ (nano::process_result::progress, node1->process (*send2).code);
auto open (std::make_shared<nano::open_block> (send1->hash (), key.pub, key.pub, key.prv, key.pub, *system.work.generate (key.pub)));
ASSERT_EQ (nano::process_result::progress, node1->process (*open).code);
auto send3 (std::make_shared<nano::state_block> (key.pub, open->hash (), key.pub, 0, key2.pub, key.prv, key.pub, *system.work.generate (open->hash ()))); // It is not possible to define this block send/receive status based on previous block (legacy open)
ASSERT_EQ (nano::process_result::progress, node1->process (*send3).code);
// Start lazy bootstrap with last block in chain known
auto node2 = system.add_node (nano::node_config (24001, system.logging), node_flags);
node2->network.udp_channels.insert (node1->network.endpoint (), node1->network_params.protocol.protocol_version);
node2->bootstrap_initiator.bootstrap_lazy (send3->hash ());
// Check processed blocks
system.deadline_set (15s);
while (node2->bootstrap_initiator.in_progress ())
{
ASSERT_NO_ERROR (system.poll ());
}
node2->block_processor.flush ();
ASSERT_TRUE (node2->ledger.block_exists (send1->hash ()));
ASSERT_TRUE (node2->ledger.block_exists (send2->hash ()));
ASSERT_TRUE (node2->ledger.block_exists (open->hash ()));
ASSERT_TRUE (node2->ledger.block_exists (send3->hash ()));
ASSERT_EQ (1, node2->stats.count (nano::stat::type::bootstrap, nano::stat::detail::bulk_pull_failed_account, nano::stat::dir::in));
}
TEST (bootstrap_processor, lazy_destinations)
{
nano::system system;
nano::node_flags node_flags;
node_flags.disable_legacy_bootstrap = true;
auto node1 = system.add_node (nano::node_config (24000, system.logging), node_flags);
nano::genesis genesis;
nano::keypair key1, key2;
// Generating test chain
auto send1 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key1.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (genesis.hash ())));
ASSERT_EQ (nano::process_result::progress, node1->process (*send1).code);
auto send2 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, send1->hash (), nano::test_genesis_key.pub, nano::genesis_amount - 2 * nano::Gxrb_ratio, key2.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send1->hash ())));
ASSERT_EQ (nano::process_result::progress, node1->process (*send2).code);
auto open (std::make_shared<nano::open_block> (send1->hash (), key1.pub, key1.pub, key1.prv, key1.pub, *system.work.generate (key1.pub)));
ASSERT_EQ (nano::process_result::progress, node1->process (*open).code);
auto state_open (std::make_shared<nano::state_block> (key2.pub, 0, key2.pub, nano::Gxrb_ratio, send2->hash (), key2.prv, key2.pub, *system.work.generate (key2.pub)));
ASSERT_EQ (nano::process_result::progress, node1->process (*state_open).code);
// Start lazy bootstrap with last block in sender chain
auto node2 = system.add_node (nano::node_config (24001, system.logging), node_flags);
node2->network.udp_channels.insert (node1->network.endpoint (), node1->network_params.protocol.protocol_version);
node2->bootstrap_initiator.bootstrap_lazy (send2->hash ());
// Check processed blocks
system.deadline_set (10s);
while (node2->bootstrap_initiator.in_progress ())
{
ASSERT_NO_ERROR (system.poll ());
}
node2->block_processor.flush ();
ASSERT_TRUE (node2->ledger.block_exists (send1->hash ()));
ASSERT_TRUE (node2->ledger.block_exists (send2->hash ()));
ASSERT_TRUE (node2->ledger.block_exists (open->hash ()));
ASSERT_TRUE (node2->ledger.block_exists (state_open->hash ()));
}
TEST (bootstrap_processor, wallet_lazy_frontier)
{
nano::system system (24000, 1);
nano::genesis genesis;
nano::keypair key1;
nano::keypair key2;
// Generating test chain
auto send1 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key1.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.nodes[0]->work_generate_blocking (genesis.hash ())));
auto receive1 (std::make_shared<nano::state_block> (key1.pub, 0, key1.pub, nano::Gxrb_ratio, send1->hash (), key1.prv, key1.pub, *system.nodes[0]->work_generate_blocking (key1.pub)));
auto send2 (std::make_shared<nano::state_block> (key1.pub, receive1->hash (), key1.pub, 0, key2.pub, key1.prv, key1.pub, *system.nodes[0]->work_generate_blocking (receive1->hash ())));
auto receive2 (std::make_shared<nano::state_block> (key2.pub, 0, key2.pub, nano::Gxrb_ratio, send2->hash (), key2.prv, key2.pub, *system.nodes[0]->work_generate_blocking (key2.pub)));
// Processing test chain
system.nodes[0]->block_processor.add (send1);
system.nodes[0]->block_processor.add (receive1);
system.nodes[0]->block_processor.add (send2);
system.nodes[0]->block_processor.add (receive2);
system.nodes[0]->block_processor.flush ();
// Start wallet lazy bootstrap
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
node1->network.udp_channels.insert (system.nodes[0]->network.endpoint (), node1->network_params.protocol.protocol_version);
auto wallet (node1->wallets.create (nano::random_wallet_id ()));
ASSERT_NE (nullptr, wallet);
wallet->insert_adhoc (key2.prv);
node1->bootstrap_wallet ();
// Check processed blocks
system.deadline_set (10s);
while (!node1->ledger.block_exists (receive2->hash ()))
{
ASSERT_NO_ERROR (system.poll ());
}
node1->stop ();
}
TEST (bootstrap_processor, wallet_lazy_pending)
{
nano::system system (24000, 1);
nano::genesis genesis;
nano::keypair key1;
nano::keypair key2;
// Generating test chain
auto send1 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key1.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.nodes[0]->work_generate_blocking (genesis.hash ())));
auto receive1 (std::make_shared<nano::state_block> (key1.pub, 0, key1.pub, nano::Gxrb_ratio, send1->hash (), key1.prv, key1.pub, *system.nodes[0]->work_generate_blocking (key1.pub)));
auto send2 (std::make_shared<nano::state_block> (key1.pub, receive1->hash (), key1.pub, 0, key2.pub, key1.prv, key1.pub, *system.nodes[0]->work_generate_blocking (receive1->hash ())));
// Processing test chain
system.nodes[0]->block_processor.add (send1);
system.nodes[0]->block_processor.add (receive1);
system.nodes[0]->block_processor.add (send2);
system.nodes[0]->block_processor.flush ();
// Start wallet lazy bootstrap
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
node1->network.udp_channels.insert (system.nodes[0]->network.endpoint (), node1->network_params.protocol.protocol_version);
auto wallet (node1->wallets.create (nano::random_wallet_id ()));
ASSERT_NE (nullptr, wallet);
wallet->insert_adhoc (key2.prv);
node1->bootstrap_wallet ();
// Check processed blocks
system.deadline_set (10s);
while (!node1->ledger.block_exists (send2->hash ()))
{
ASSERT_NO_ERROR (system.poll ());
}
node1->stop ();
}
TEST (frontier_req_response, DISABLED_destruction)
{
{
std::shared_ptr<nano::frontier_req_server> hold; // Destructing tcp acceptor on non-existent io_context
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::frontier_req> req (new nano::frontier_req);
req->start.clear ();
req->age = std::numeric_limits<decltype (req->age)>::max ();
req->count = std::numeric_limits<decltype (req->count)>::max ();
connection->requests.push (std::unique_ptr<nano::message>{});
hold = std::make_shared<nano::frontier_req_server> (connection, std::move (req));
}
}
ASSERT_TRUE (true);
}
TEST (frontier_req, begin)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::frontier_req> req (new nano::frontier_req);
req->start.clear ();
req->age = std::numeric_limits<decltype (req->age)>::max ();
req->count = std::numeric_limits<decltype (req->count)>::max ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::frontier_req_server> (connection, std::move (req)));
ASSERT_EQ (nano::test_genesis_key.pub, request->current);
nano::genesis genesis;
ASSERT_EQ (genesis.hash (), request->frontier);
}
TEST (frontier_req, end)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::frontier_req> req (new nano::frontier_req);
req->start = nano::test_genesis_key.pub.number () + 1;
req->age = std::numeric_limits<decltype (req->age)>::max ();
req->count = std::numeric_limits<decltype (req->count)>::max ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::frontier_req_server> (connection, std::move (req)));
ASSERT_TRUE (request->current.is_zero ());
}
TEST (frontier_req, count)
{
nano::system system (24000, 1);
auto & node1 (*system.nodes[0]);
nano::genesis genesis;
// Public key FB93... after genesis in accounts table
nano::keypair key1 ("ED5AE0A6505B14B67435C29FD9FEEBC26F597D147BC92F6D795FFAD7AFD3D967");
nano::state_block send1 (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key1.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0);
node1.work_generate_blocking (send1);
ASSERT_EQ (nano::process_result::progress, node1.process (send1).code);
nano::state_block receive1 (key1.pub, 0, nano::test_genesis_key.pub, nano::Gxrb_ratio, send1.hash (), key1.prv, key1.pub, 0);
node1.work_generate_blocking (receive1);
ASSERT_EQ (nano::process_result::progress, node1.process (receive1).code);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::frontier_req> req (new nano::frontier_req);
req->start.clear ();
req->age = std::numeric_limits<decltype (req->age)>::max ();
req->count = 1;
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::frontier_req_server> (connection, std::move (req)));
ASSERT_EQ (nano::test_genesis_key.pub, request->current);
ASSERT_EQ (send1.hash (), request->frontier);
}
TEST (frontier_req, time_bound)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::frontier_req> req (new nano::frontier_req);
req->start.clear ();
req->age = 1;
req->count = std::numeric_limits<decltype (req->count)>::max ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::frontier_req_server> (connection, std::move (req)));
ASSERT_EQ (nano::test_genesis_key.pub, request->current);
// Wait 2 seconds until age of account will be > 1 seconds
std::this_thread::sleep_for (std::chrono::milliseconds (2100));
std::unique_ptr<nano::frontier_req> req2 (new nano::frontier_req);
req2->start.clear ();
req2->age = 1;
req2->count = std::numeric_limits<decltype (req2->count)>::max ();
auto connection2 (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
connection2->requests.push (std::unique_ptr<nano::message>{});
auto request2 (std::make_shared<nano::frontier_req_server> (connection, std::move (req2)));
ASSERT_TRUE (request2->current.is_zero ());
}
TEST (frontier_req, time_cutoff)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::frontier_req> req (new nano::frontier_req);
req->start.clear ();
req->age = 3;
req->count = std::numeric_limits<decltype (req->count)>::max ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::frontier_req_server> (connection, std::move (req)));
ASSERT_EQ (nano::test_genesis_key.pub, request->current);
nano::genesis genesis;
ASSERT_EQ (genesis.hash (), request->frontier);
// Wait 4 seconds until age of account will be > 3 seconds
std::this_thread::sleep_for (std::chrono::milliseconds (4100));
std::unique_ptr<nano::frontier_req> req2 (new nano::frontier_req);
req2->start.clear ();
req2->age = 3;
req2->count = std::numeric_limits<decltype (req2->count)>::max ();
auto connection2 (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
connection2->requests.push (std::unique_ptr<nano::message>{});
auto request2 (std::make_shared<nano::frontier_req_server> (connection, std::move (req2)));
ASSERT_TRUE (request2->frontier.is_zero ());
}
TEST (bulk, genesis)
{
nano::system system (24000, 1);
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
ASSERT_FALSE (node1->init_error ());
nano::block_hash latest1 (system.nodes[0]->latest (nano::test_genesis_key.pub));
nano::block_hash latest2 (node1->latest (nano::test_genesis_key.pub));
ASSERT_EQ (latest1, latest2);
nano::keypair key2;
ASSERT_NE (nullptr, system.wallet (0)->send_action (nano::test_genesis_key.pub, key2.pub, 100));
nano::block_hash latest3 (system.nodes[0]->latest (nano::test_genesis_key.pub));
ASSERT_NE (latest1, latest3);
node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint ());
system.deadline_set (10s);
while (node1->latest (nano::test_genesis_key.pub) != system.nodes[0]->latest (nano::test_genesis_key.pub))
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (node1->latest (nano::test_genesis_key.pub), system.nodes[0]->latest (nano::test_genesis_key.pub));
node1->stop ();
}
TEST (bulk, offline_send)
{
nano::system system (24000, 1);
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
ASSERT_FALSE (node1->init_error ());
node1->start ();
system.nodes.push_back (node1);
nano::keypair key2;
auto wallet (node1->wallets.create (nano::random_wallet_id ()));
wallet->insert_adhoc (key2.prv);
ASSERT_NE (nullptr, system.wallet (0)->send_action (nano::test_genesis_key.pub, key2.pub, system.nodes[0]->config.receive_minimum.number ()));
ASSERT_NE (std::numeric_limits<nano::uint256_t>::max (), system.nodes[0]->balance (nano::test_genesis_key.pub));
// Wait to finish election background tasks
system.deadline_set (10s);
while (!system.nodes[0]->active.empty ())
{
ASSERT_NO_ERROR (system.poll ());
}
// Initiate bootstrap
node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint ());
// Nodes should find each other
do
{
ASSERT_NO_ERROR (system.poll ());
} while (system.nodes[0]->network.empty () || node1->network.empty ());
// Send block arrival via bootstrap
while (node1->balance (nano::test_genesis_key.pub) == std::numeric_limits<nano::uint256_t>::max ())
{
ASSERT_NO_ERROR (system.poll ());
}
// Receiving send block
system.deadline_set (20s);
while (node1->balance (key2.pub) != system.nodes[0]->config.receive_minimum.number ())
{
ASSERT_NO_ERROR (system.poll ());
}
node1->stop ();
}
TEST (bulk_pull_account, basics)
{
nano::system system (24000, 1);
system.nodes[0]->config.receive_minimum = 20;
nano::keypair key1;
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
system.wallet (0)->insert_adhoc (key1.prv);
auto send1 (system.wallet (0)->send_action (nano::genesis_account, key1.pub, 25));
auto send2 (system.wallet (0)->send_action (nano::genesis_account, key1.pub, 10));
auto send3 (system.wallet (0)->send_action (nano::genesis_account, key1.pub, 2));
system.deadline_set (5s);
while (system.nodes[0]->balance (key1.pub) != 25)
{
ASSERT_NO_ERROR (system.poll ());
}
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
{
std::unique_ptr<nano::bulk_pull_account> req (new nano::bulk_pull_account{});
req->account = key1.pub;
req->minimum_amount = 5;
req->flags = nano::bulk_pull_account_flags ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::bulk_pull_account_server> (connection, std::move (req)));
ASSERT_FALSE (request->invalid_request);
ASSERT_FALSE (request->pending_include_address);
ASSERT_FALSE (request->pending_address_only);
ASSERT_EQ (request->current_key.account, key1.pub);
ASSERT_EQ (request->current_key.hash, 0);
auto block_data (request->get_next ());
ASSERT_EQ (send2->hash (), block_data.first.get ()->hash);
ASSERT_EQ (nano::uint128_union (10), block_data.second.get ()->amount);
ASSERT_EQ (nano::genesis_account, block_data.second.get ()->source);
ASSERT_EQ (nullptr, request->get_next ().first.get ());
}
{
std::unique_ptr<nano::bulk_pull_account> req (new nano::bulk_pull_account{});
req->account = key1.pub;
req->minimum_amount = 0;
req->flags = nano::bulk_pull_account_flags::pending_address_only;
auto request (std::make_shared<nano::bulk_pull_account_server> (connection, std::move (req)));
ASSERT_TRUE (request->pending_address_only);
auto block_data (request->get_next ());
ASSERT_NE (nullptr, block_data.first.get ());
ASSERT_NE (nullptr, block_data.second.get ());
ASSERT_EQ (nano::genesis_account, block_data.second.get ()->source);
block_data = request->get_next ();
ASSERT_EQ (nullptr, block_data.first.get ());
ASSERT_EQ (nullptr, block_data.second.get ());
}
}

View file

@ -460,662 +460,6 @@ TEST (parse_endpoint, no_colon)
ASSERT_TRUE (nano::parse_endpoint (string, endpoint));
}
// If the account doesn't exist, current == end so there's no iteration
TEST (bulk_pull, no_address)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::bulk_pull> req (new nano::bulk_pull);
req->start = nano::root (1);
req->end = 2;
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::bulk_pull_server> (connection, std::move (req)));
ASSERT_EQ (request->current, request->request->end);
ASSERT_TRUE (request->current.is_zero ());
}
TEST (bulk_pull, genesis_to_end)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::bulk_pull> req (new nano::bulk_pull{});
req->start = nano::test_genesis_key.pub;
req->end.clear ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::bulk_pull_server> (connection, std::move (req)));
ASSERT_EQ (system.nodes[0]->latest (nano::test_genesis_key.pub), request->current);
ASSERT_EQ (request->request->end, request->request->end);
}
// If we can't find the end block, send everything
TEST (bulk_pull, no_end)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::bulk_pull> req (new nano::bulk_pull{});
req->start = nano::test_genesis_key.pub;
req->end = 1;
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::bulk_pull_server> (connection, std::move (req)));
ASSERT_EQ (system.nodes[0]->latest (nano::test_genesis_key.pub), request->current);
ASSERT_TRUE (request->request->end.is_zero ());
}
TEST (bulk_pull, end_not_owned)
{
nano::system system (24000, 1);
nano::keypair key2;
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
ASSERT_NE (nullptr, system.wallet (0)->send_action (nano::test_genesis_key.pub, key2.pub, 100));
nano::block_hash latest (system.nodes[0]->latest (nano::test_genesis_key.pub));
nano::open_block open (0, 1, 2, nano::keypair ().prv, 4, 5);
open.hashables.account = key2.pub;
open.hashables.representative = key2.pub;
open.hashables.source = latest;
open.signature = nano::sign_message (key2.prv, key2.pub, open.hash ());
system.nodes[0]->work_generate_blocking (open);
ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (open).code);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
nano::genesis genesis;
std::unique_ptr<nano::bulk_pull> req (new nano::bulk_pull{});
req->start = key2.pub;
req->end = genesis.hash ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::bulk_pull_server> (connection, std::move (req)));
ASSERT_EQ (request->current, request->request->end);
}
TEST (bulk_pull, none)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
nano::genesis genesis;
std::unique_ptr<nano::bulk_pull> req (new nano::bulk_pull{});
req->start = nano::test_genesis_key.pub;
req->end = genesis.hash ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::bulk_pull_server> (connection, std::move (req)));
auto block (request->get_next ());
ASSERT_EQ (nullptr, block);
}
TEST (bulk_pull, get_next_on_open)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::bulk_pull> req (new nano::bulk_pull{});
req->start = nano::test_genesis_key.pub;
req->end.clear ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::bulk_pull_server> (connection, std::move (req)));
auto block (request->get_next ());
ASSERT_NE (nullptr, block);
ASSERT_TRUE (block->previous ().is_zero ());
ASSERT_FALSE (connection->requests.empty ());
ASSERT_EQ (request->current, request->request->end);
}
TEST (bulk_pull, by_block)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
nano::genesis genesis;
std::unique_ptr<nano::bulk_pull> req (new nano::bulk_pull{});
req->start = genesis.hash ();
req->end.clear ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::bulk_pull_server> (connection, std::move (req)));
auto block (request->get_next ());
ASSERT_NE (nullptr, block);
ASSERT_EQ (block->hash (), genesis.hash ());
block = request->get_next ();
ASSERT_EQ (nullptr, block);
}
TEST (bulk_pull, by_block_single)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
nano::genesis genesis;
std::unique_ptr<nano::bulk_pull> req (new nano::bulk_pull{});
req->start = genesis.hash ();
req->end = genesis.hash ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::bulk_pull_server> (connection, std::move (req)));
auto block (request->get_next ());
ASSERT_NE (nullptr, block);
ASSERT_EQ (block->hash (), genesis.hash ());
block = request->get_next ();
ASSERT_EQ (nullptr, block);
}
TEST (bulk_pull, count_limit)
{
nano::system system (24000, 1);
nano::genesis genesis;
auto send1 (std::make_shared<nano::send_block> (system.nodes[0]->latest (nano::test_genesis_key.pub), nano::test_genesis_key.pub, 1, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (system.nodes[0]->latest (nano::test_genesis_key.pub))));
ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (*send1).code);
auto receive1 (std::make_shared<nano::receive_block> (send1->hash (), send1->hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send1->hash ())));
ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (*receive1).code);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::bulk_pull> req (new nano::bulk_pull{});
req->start = receive1->hash ();
req->set_count_present (true);
req->count = 2;
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::bulk_pull_server> (connection, std::move (req)));
ASSERT_EQ (request->max_count, 2);
ASSERT_EQ (request->sent_count, 0);
auto block (request->get_next ());
ASSERT_EQ (receive1->hash (), block->hash ());
block = request->get_next ();
ASSERT_EQ (send1->hash (), block->hash ());
block = request->get_next ();
ASSERT_EQ (nullptr, block);
}
TEST (bootstrap_processor, DISABLED_process_none)
{
nano::system system (24000, 1);
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
ASSERT_FALSE (node1->init_error ());
auto done (false);
node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint ());
while (!done)
{
system.io_ctx.run_one ();
}
node1->stop ();
}
// Bootstrap can pull one basic block
TEST (bootstrap_processor, process_one)
{
nano::system system (24000, 1);
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
ASSERT_NE (nullptr, system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::test_genesis_key.pub, 100));
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
nano::block_hash hash1 (system.nodes[0]->latest (nano::test_genesis_key.pub));
nano::block_hash hash2 (node1->latest (nano::test_genesis_key.pub));
ASSERT_NE (hash1, hash2);
node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint ());
ASSERT_NE (node1->latest (nano::test_genesis_key.pub), system.nodes[0]->latest (nano::test_genesis_key.pub));
system.deadline_set (10s);
while (node1->latest (nano::test_genesis_key.pub) != system.nodes[0]->latest (nano::test_genesis_key.pub))
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (0, node1->active.size ());
node1->stop ();
}
TEST (bootstrap_processor, process_two)
{
nano::system system (24000, 1);
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
nano::block_hash hash1 (system.nodes[0]->latest (nano::test_genesis_key.pub));
ASSERT_NE (nullptr, system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::test_genesis_key.pub, 50));
nano::block_hash hash2 (system.nodes[0]->latest (nano::test_genesis_key.pub));
ASSERT_NE (nullptr, system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::test_genesis_key.pub, 50));
nano::block_hash hash3 (system.nodes[0]->latest (nano::test_genesis_key.pub));
ASSERT_NE (hash1, hash2);
ASSERT_NE (hash1, hash3);
ASSERT_NE (hash2, hash3);
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
ASSERT_FALSE (node1->init_error ());
node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint ());
ASSERT_NE (node1->latest (nano::test_genesis_key.pub), system.nodes[0]->latest (nano::test_genesis_key.pub));
system.deadline_set (10s);
while (node1->latest (nano::test_genesis_key.pub) != system.nodes[0]->latest (nano::test_genesis_key.pub))
{
ASSERT_NO_ERROR (system.poll ());
}
node1->stop ();
}
// Bootstrap can pull universal blocks
TEST (bootstrap_processor, process_state)
{
nano::system system (24000, 1);
nano::genesis genesis;
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
auto node0 (system.nodes[0]);
auto block1 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, node0->latest (nano::test_genesis_key.pub), nano::test_genesis_key.pub, nano::genesis_amount - 100, nano::test_genesis_key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0));
auto block2 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, block1->hash (), nano::test_genesis_key.pub, nano::genesis_amount, block1->hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0));
node0->work_generate_blocking (*block1);
node0->work_generate_blocking (*block2);
node0->process (*block1);
node0->process (*block2);
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
ASSERT_EQ (node0->latest (nano::test_genesis_key.pub), block2->hash ());
ASSERT_NE (node1->latest (nano::test_genesis_key.pub), block2->hash ());
node1->bootstrap_initiator.bootstrap (node0->network.endpoint ());
ASSERT_NE (node1->latest (nano::test_genesis_key.pub), node0->latest (nano::test_genesis_key.pub));
system.deadline_set (10s);
while (node1->latest (nano::test_genesis_key.pub) != node0->latest (nano::test_genesis_key.pub))
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (0, node1->active.size ());
node1->stop ();
}
TEST (bootstrap_processor, process_new)
{
nano::system system (24000, 2);
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
nano::keypair key2;
system.wallet (1)->insert_adhoc (key2.prv);
ASSERT_NE (nullptr, system.wallet (0)->send_action (nano::test_genesis_key.pub, key2.pub, system.nodes[0]->config.receive_minimum.number ()));
system.deadline_set (10s);
while (system.nodes[0]->balance (key2.pub).is_zero ())
{
ASSERT_NO_ERROR (system.poll ());
}
nano::uint128_t balance1 (system.nodes[0]->balance (nano::test_genesis_key.pub));
nano::uint128_t balance2 (system.nodes[0]->balance (key2.pub));
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24002, nano::unique_path (), system.alarm, system.logging, system.work));
ASSERT_FALSE (node1->init_error ());
node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint ());
system.deadline_set (10s);
while (node1->balance (key2.pub) != balance2)
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (balance1, node1->balance (nano::test_genesis_key.pub));
node1->stop ();
}
TEST (bootstrap_processor, pull_diamond)
{
nano::system system (24000, 1);
nano::keypair key;
auto send1 (std::make_shared<nano::send_block> (system.nodes[0]->latest (nano::test_genesis_key.pub), key.pub, 0, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (system.nodes[0]->latest (nano::test_genesis_key.pub))));
ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (*send1).code);
auto open (std::make_shared<nano::open_block> (send1->hash (), 1, key.pub, key.prv, key.pub, *system.work.generate (key.pub)));
ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (*open).code);
auto send2 (std::make_shared<nano::send_block> (open->hash (), nano::test_genesis_key.pub, std::numeric_limits<nano::uint128_t>::max () - 100, key.prv, key.pub, *system.work.generate (open->hash ())));
ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (*send2).code);
auto receive (std::make_shared<nano::receive_block> (send1->hash (), send2->hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send1->hash ())));
ASSERT_EQ (nano::process_result::progress, system.nodes[0]->process (*receive).code);
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24002, nano::unique_path (), system.alarm, system.logging, system.work));
ASSERT_FALSE (node1->init_error ());
node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint ());
system.deadline_set (10s);
while (node1->balance (nano::test_genesis_key.pub) != 100)
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (100, node1->balance (nano::test_genesis_key.pub));
node1->stop ();
}
TEST (bootstrap_processor, push_diamond)
{
nano::system system (24000, 1);
nano::keypair key;
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24002, nano::unique_path (), system.alarm, system.logging, system.work));
ASSERT_FALSE (node1->init_error ());
auto wallet1 (node1->wallets.create (100));
wallet1->insert_adhoc (nano::test_genesis_key.prv);
wallet1->insert_adhoc (key.prv);
auto send1 (std::make_shared<nano::send_block> (system.nodes[0]->latest (nano::test_genesis_key.pub), key.pub, 0, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (system.nodes[0]->latest (nano::test_genesis_key.pub))));
ASSERT_EQ (nano::process_result::progress, node1->process (*send1).code);
auto open (std::make_shared<nano::open_block> (send1->hash (), 1, key.pub, key.prv, key.pub, *system.work.generate (key.pub)));
ASSERT_EQ (nano::process_result::progress, node1->process (*open).code);
auto send2 (std::make_shared<nano::send_block> (open->hash (), nano::test_genesis_key.pub, std::numeric_limits<nano::uint128_t>::max () - 100, key.prv, key.pub, *system.work.generate (open->hash ())));
ASSERT_EQ (nano::process_result::progress, node1->process (*send2).code);
auto receive (std::make_shared<nano::receive_block> (send1->hash (), send2->hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send1->hash ())));
ASSERT_EQ (nano::process_result::progress, node1->process (*receive).code);
node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint ());
system.deadline_set (10s);
while (system.nodes[0]->balance (nano::test_genesis_key.pub) != 100)
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (100, system.nodes[0]->balance (nano::test_genesis_key.pub));
node1->stop ();
}
TEST (bootstrap_processor, push_one)
{
nano::system system (24000, 1);
nano::keypair key1;
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
auto wallet (node1->wallets.create (nano::random_wallet_id ()));
ASSERT_NE (nullptr, wallet);
wallet->insert_adhoc (nano::test_genesis_key.prv);
nano::uint128_t balance1 (node1->balance (nano::test_genesis_key.pub));
ASSERT_NE (nullptr, wallet->send_action (nano::test_genesis_key.pub, key1.pub, 100));
ASSERT_NE (balance1, node1->balance (nano::test_genesis_key.pub));
node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint ());
system.deadline_set (10s);
while (system.nodes[0]->balance (nano::test_genesis_key.pub) == balance1)
{
ASSERT_NO_ERROR (system.poll ());
}
node1->stop ();
}
TEST (bootstrap_processor, lazy_hash)
{
nano::system system (24000, 1);
nano::genesis genesis;
nano::keypair key1;
nano::keypair key2;
// Generating test chain
auto send1 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key1.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.nodes[0]->work_generate_blocking (genesis.hash ())));
auto receive1 (std::make_shared<nano::state_block> (key1.pub, 0, key1.pub, nano::Gxrb_ratio, send1->hash (), key1.prv, key1.pub, *system.nodes[0]->work_generate_blocking (key1.pub)));
auto send2 (std::make_shared<nano::state_block> (key1.pub, receive1->hash (), key1.pub, 0, key2.pub, key1.prv, key1.pub, *system.nodes[0]->work_generate_blocking (receive1->hash ())));
auto receive2 (std::make_shared<nano::state_block> (key2.pub, 0, key2.pub, nano::Gxrb_ratio, send2->hash (), key2.prv, key2.pub, *system.nodes[0]->work_generate_blocking (key2.pub)));
// Processing test chain
system.nodes[0]->block_processor.add (send1);
system.nodes[0]->block_processor.add (receive1);
system.nodes[0]->block_processor.add (send2);
system.nodes[0]->block_processor.add (receive2);
system.nodes[0]->block_processor.flush ();
// Start lazy bootstrap with last block in chain known
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
node1->network.udp_channels.insert (system.nodes[0]->network.endpoint (), node1->network_params.protocol.protocol_version);
node1->bootstrap_initiator.bootstrap_lazy (receive2->hash ());
// Check processed blocks
system.deadline_set (10s);
while (node1->balance (key2.pub) == 0)
{
ASSERT_NO_ERROR (system.poll ());
}
node1->stop ();
}
TEST (bootstrap_processor, lazy_max_pull_count)
{
nano::system system (24000, 1);
nano::genesis genesis;
nano::keypair key1;
nano::keypair key2;
// Generating test chain
auto send1 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key1.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.nodes[0]->work_generate_blocking (genesis.hash ())));
auto receive1 (std::make_shared<nano::state_block> (key1.pub, 0, key1.pub, nano::Gxrb_ratio, send1->hash (), key1.prv, key1.pub, *system.nodes[0]->work_generate_blocking (key1.pub)));
auto send2 (std::make_shared<nano::state_block> (key1.pub, receive1->hash (), key1.pub, 0, key2.pub, key1.prv, key1.pub, *system.nodes[0]->work_generate_blocking (receive1->hash ())));
auto receive2 (std::make_shared<nano::state_block> (key2.pub, 0, key2.pub, nano::Gxrb_ratio, send2->hash (), key2.prv, key2.pub, *system.nodes[0]->work_generate_blocking (key2.pub)));
auto change1 (std::make_shared<nano::state_block> (key2.pub, receive2->hash (), key1.pub, nano::Gxrb_ratio, 0, key2.prv, key2.pub, *system.nodes[0]->work_generate_blocking (receive2->hash ())));
auto change2 (std::make_shared<nano::state_block> (key2.pub, change1->hash (), nano::test_genesis_key.pub, nano::Gxrb_ratio, 0, key2.prv, key2.pub, *system.nodes[0]->work_generate_blocking (change1->hash ())));
auto change3 (std::make_shared<nano::state_block> (key2.pub, change2->hash (), key2.pub, nano::Gxrb_ratio, 0, key2.prv, key2.pub, *system.nodes[0]->work_generate_blocking (change2->hash ())));
// Processing test chain
system.nodes[0]->block_processor.add (send1);
system.nodes[0]->block_processor.add (receive1);
system.nodes[0]->block_processor.add (send2);
system.nodes[0]->block_processor.add (receive2);
system.nodes[0]->block_processor.add (change1);
system.nodes[0]->block_processor.add (change2);
system.nodes[0]->block_processor.add (change3);
system.nodes[0]->block_processor.flush ();
// Start lazy bootstrap with last block in chain known
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
node1->network.udp_channels.insert (system.nodes[0]->network.endpoint (), node1->network_params.protocol.protocol_version);
node1->bootstrap_initiator.bootstrap_lazy (change3->hash ());
// Check processed blocks
system.deadline_set (10s);
while (node1->block (change3->hash ()) == nullptr)
{
ASSERT_NO_ERROR (system.poll ());
}
node1->stop ();
}
TEST (bootstrap_processor, wallet_lazy_frontier)
{
nano::system system (24000, 1);
nano::genesis genesis;
nano::keypair key1;
nano::keypair key2;
// Generating test chain
auto send1 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key1.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.nodes[0]->work_generate_blocking (genesis.hash ())));
auto receive1 (std::make_shared<nano::state_block> (key1.pub, 0, key1.pub, nano::Gxrb_ratio, send1->hash (), key1.prv, key1.pub, *system.nodes[0]->work_generate_blocking (key1.pub)));
auto send2 (std::make_shared<nano::state_block> (key1.pub, receive1->hash (), key1.pub, 0, key2.pub, key1.prv, key1.pub, *system.nodes[0]->work_generate_blocking (receive1->hash ())));
auto receive2 (std::make_shared<nano::state_block> (key2.pub, 0, key2.pub, nano::Gxrb_ratio, send2->hash (), key2.prv, key2.pub, *system.nodes[0]->work_generate_blocking (key2.pub)));
// Processing test chain
system.nodes[0]->block_processor.add (send1);
system.nodes[0]->block_processor.add (receive1);
system.nodes[0]->block_processor.add (send2);
system.nodes[0]->block_processor.add (receive2);
system.nodes[0]->block_processor.flush ();
// Start wallet lazy bootstrap
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
node1->network.udp_channels.insert (system.nodes[0]->network.endpoint (), node1->network_params.protocol.protocol_version);
auto wallet (node1->wallets.create (nano::random_wallet_id ()));
ASSERT_NE (nullptr, wallet);
wallet->insert_adhoc (key2.prv);
node1->bootstrap_wallet ();
// Check processed blocks
system.deadline_set (10s);
while (!node1->ledger.block_exists (receive2->hash ()))
{
ASSERT_NO_ERROR (system.poll ());
}
node1->stop ();
}
TEST (bootstrap_processor, wallet_lazy_pending)
{
nano::system system (24000, 1);
nano::genesis genesis;
nano::keypair key1;
nano::keypair key2;
// Generating test chain
auto send1 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key1.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.nodes[0]->work_generate_blocking (genesis.hash ())));
auto receive1 (std::make_shared<nano::state_block> (key1.pub, 0, key1.pub, nano::Gxrb_ratio, send1->hash (), key1.prv, key1.pub, *system.nodes[0]->work_generate_blocking (key1.pub)));
auto send2 (std::make_shared<nano::state_block> (key1.pub, receive1->hash (), key1.pub, 0, key2.pub, key1.prv, key1.pub, *system.nodes[0]->work_generate_blocking (receive1->hash ())));
// Processing test chain
system.nodes[0]->block_processor.add (send1);
system.nodes[0]->block_processor.add (receive1);
system.nodes[0]->block_processor.add (send2);
system.nodes[0]->block_processor.flush ();
// Start wallet lazy bootstrap
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
node1->network.udp_channels.insert (system.nodes[0]->network.endpoint (), node1->network_params.protocol.protocol_version);
auto wallet (node1->wallets.create (nano::random_wallet_id ()));
ASSERT_NE (nullptr, wallet);
wallet->insert_adhoc (key2.prv);
node1->bootstrap_wallet ();
// Check processed blocks
system.deadline_set (10s);
while (!node1->ledger.block_exists (send2->hash ()))
{
ASSERT_NO_ERROR (system.poll ());
}
node1->stop ();
}
TEST (frontier_req_response, DISABLED_destruction)
{
{
std::shared_ptr<nano::frontier_req_server> hold; // Destructing tcp acceptor on non-existent io_context
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::frontier_req> req (new nano::frontier_req);
req->start.clear ();
req->age = std::numeric_limits<decltype (req->age)>::max ();
req->count = std::numeric_limits<decltype (req->count)>::max ();
connection->requests.push (std::unique_ptr<nano::message>{});
hold = std::make_shared<nano::frontier_req_server> (connection, std::move (req));
}
}
ASSERT_TRUE (true);
}
TEST (frontier_req, begin)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::frontier_req> req (new nano::frontier_req);
req->start.clear ();
req->age = std::numeric_limits<decltype (req->age)>::max ();
req->count = std::numeric_limits<decltype (req->count)>::max ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::frontier_req_server> (connection, std::move (req)));
ASSERT_EQ (nano::test_genesis_key.pub, request->current);
nano::genesis genesis;
ASSERT_EQ (genesis.hash (), request->frontier);
}
TEST (frontier_req, end)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::frontier_req> req (new nano::frontier_req);
req->start = nano::test_genesis_key.pub.number () + 1;
req->age = std::numeric_limits<decltype (req->age)>::max ();
req->count = std::numeric_limits<decltype (req->count)>::max ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::frontier_req_server> (connection, std::move (req)));
ASSERT_TRUE (request->current.is_zero ());
}
TEST (frontier_req, count)
{
nano::system system (24000, 1);
auto & node1 (*system.nodes[0]);
nano::genesis genesis;
// Public key FB93... after genesis in accounts table
nano::keypair key1 ("ED5AE0A6505B14B67435C29FD9FEEBC26F597D147BC92F6D795FFAD7AFD3D967");
nano::state_block send1 (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key1.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0);
node1.work_generate_blocking (send1);
ASSERT_EQ (nano::process_result::progress, node1.process (send1).code);
nano::state_block receive1 (key1.pub, 0, nano::test_genesis_key.pub, nano::Gxrb_ratio, send1.hash (), key1.prv, key1.pub, 0);
node1.work_generate_blocking (receive1);
ASSERT_EQ (nano::process_result::progress, node1.process (receive1).code);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::frontier_req> req (new nano::frontier_req);
req->start.clear ();
req->age = std::numeric_limits<decltype (req->age)>::max ();
req->count = 1;
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::frontier_req_server> (connection, std::move (req)));
ASSERT_EQ (nano::test_genesis_key.pub, request->current);
ASSERT_EQ (send1.hash (), request->frontier);
}
TEST (frontier_req, time_bound)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::frontier_req> req (new nano::frontier_req);
req->start.clear ();
req->age = 1;
req->count = std::numeric_limits<decltype (req->count)>::max ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::frontier_req_server> (connection, std::move (req)));
ASSERT_EQ (nano::test_genesis_key.pub, request->current);
// Wait 2 seconds until age of account will be > 1 seconds
std::this_thread::sleep_for (std::chrono::milliseconds (2100));
std::unique_ptr<nano::frontier_req> req2 (new nano::frontier_req);
req2->start.clear ();
req2->age = 1;
req2->count = std::numeric_limits<decltype (req2->count)>::max ();
auto connection2 (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
connection2->requests.push (std::unique_ptr<nano::message>{});
auto request2 (std::make_shared<nano::frontier_req_server> (connection, std::move (req2)));
ASSERT_TRUE (request2->current.is_zero ());
}
TEST (frontier_req, time_cutoff)
{
nano::system system (24000, 1);
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
std::unique_ptr<nano::frontier_req> req (new nano::frontier_req);
req->start.clear ();
req->age = 3;
req->count = std::numeric_limits<decltype (req->count)>::max ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::frontier_req_server> (connection, std::move (req)));
ASSERT_EQ (nano::test_genesis_key.pub, request->current);
nano::genesis genesis;
ASSERT_EQ (genesis.hash (), request->frontier);
// Wait 4 seconds until age of account will be > 3 seconds
std::this_thread::sleep_for (std::chrono::milliseconds (4100));
std::unique_ptr<nano::frontier_req> req2 (new nano::frontier_req);
req2->start.clear ();
req2->age = 3;
req2->count = std::numeric_limits<decltype (req2->count)>::max ();
auto connection2 (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
connection2->requests.push (std::unique_ptr<nano::message>{});
auto request2 (std::make_shared<nano::frontier_req_server> (connection, std::move (req2)));
ASSERT_TRUE (request2->frontier.is_zero ());
}
TEST (bulk, genesis)
{
nano::system system (24000, 1);
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
ASSERT_FALSE (node1->init_error ());
nano::block_hash latest1 (system.nodes[0]->latest (nano::test_genesis_key.pub));
nano::block_hash latest2 (node1->latest (nano::test_genesis_key.pub));
ASSERT_EQ (latest1, latest2);
nano::keypair key2;
ASSERT_NE (nullptr, system.wallet (0)->send_action (nano::test_genesis_key.pub, key2.pub, 100));
nano::block_hash latest3 (system.nodes[0]->latest (nano::test_genesis_key.pub));
ASSERT_NE (latest1, latest3);
node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint ());
system.deadline_set (10s);
while (node1->latest (nano::test_genesis_key.pub) != system.nodes[0]->latest (nano::test_genesis_key.pub))
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (node1->latest (nano::test_genesis_key.pub), system.nodes[0]->latest (nano::test_genesis_key.pub));
node1->stop ();
}
TEST (bulk, offline_send)
{
nano::system system (24000, 1);
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
auto node1 (std::make_shared<nano::node> (system.io_ctx, 24001, nano::unique_path (), system.alarm, system.logging, system.work));
ASSERT_FALSE (node1->init_error ());
node1->start ();
system.nodes.push_back (node1);
nano::keypair key2;
auto wallet (node1->wallets.create (nano::random_wallet_id ()));
wallet->insert_adhoc (key2.prv);
ASSERT_NE (nullptr, system.wallet (0)->send_action (nano::test_genesis_key.pub, key2.pub, system.nodes[0]->config.receive_minimum.number ()));
ASSERT_NE (std::numeric_limits<nano::uint256_t>::max (), system.nodes[0]->balance (nano::test_genesis_key.pub));
// Wait to finish election background tasks
system.deadline_set (10s);
while (!system.nodes[0]->active.empty ())
{
ASSERT_NO_ERROR (system.poll ());
}
// Initiate bootstrap
node1->bootstrap_initiator.bootstrap (system.nodes[0]->network.endpoint ());
// Nodes should find each other
do
{
ASSERT_NO_ERROR (system.poll ());
} while (system.nodes[0]->network.empty () || node1->network.empty ());
// Send block arrival via bootstrap
while (node1->balance (nano::test_genesis_key.pub) == std::numeric_limits<nano::uint256_t>::max ())
{
ASSERT_NO_ERROR (system.poll ());
}
// Receiving send block
system.deadline_set (20s);
while (node1->balance (key2.pub) != system.nodes[0]->config.receive_minimum.number ())
{
ASSERT_NO_ERROR (system.poll ());
}
node1->stop ();
}
TEST (network, ipv6)
{
boost::asio::ip::address_v6 address (boost::asio::ip::address_v6::from_string ("::ffff:127.0.0.1"));
@ -1380,60 +724,7 @@ TEST (message_buffer_manager, stats)
ASSERT_EQ (1, stats.count (nano::stat::type::udp, nano::stat::detail::overflow));
}
TEST (bulk_pull_account, basics)
{
nano::system system (24000, 1);
system.nodes[0]->config.receive_minimum = 20;
nano::keypair key1;
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
system.wallet (0)->insert_adhoc (key1.prv);
auto send1 (system.wallet (0)->send_action (nano::genesis_account, key1.pub, 25));
auto send2 (system.wallet (0)->send_action (nano::genesis_account, key1.pub, 10));
auto send3 (system.wallet (0)->send_action (nano::genesis_account, key1.pub, 2));
system.deadline_set (5s);
while (system.nodes[0]->balance (key1.pub) != 25)
{
ASSERT_NO_ERROR (system.poll ());
}
auto connection (std::make_shared<nano::bootstrap_server> (nullptr, system.nodes[0]));
{
std::unique_ptr<nano::bulk_pull_account> req (new nano::bulk_pull_account{});
req->account = key1.pub;
req->minimum_amount = 5;
req->flags = nano::bulk_pull_account_flags ();
connection->requests.push (std::unique_ptr<nano::message>{});
auto request (std::make_shared<nano::bulk_pull_account_server> (connection, std::move (req)));
ASSERT_FALSE (request->invalid_request);
ASSERT_FALSE (request->pending_include_address);
ASSERT_FALSE (request->pending_address_only);
ASSERT_EQ (request->current_key.account, key1.pub);
ASSERT_EQ (request->current_key.hash, 0);
auto block_data (request->get_next ());
ASSERT_EQ (send2->hash (), block_data.first.get ()->hash);
ASSERT_EQ (nano::uint128_union (10), block_data.second.get ()->amount);
ASSERT_EQ (nano::genesis_account, block_data.second.get ()->source);
ASSERT_EQ (nullptr, request->get_next ().first.get ());
}
{
std::unique_ptr<nano::bulk_pull_account> req (new nano::bulk_pull_account{});
req->account = key1.pub;
req->minimum_amount = 0;
req->flags = nano::bulk_pull_account_flags::pending_address_only;
auto request (std::make_shared<nano::bulk_pull_account_server> (connection, std::move (req)));
ASSERT_TRUE (request->pending_address_only);
auto block_data (request->get_next ());
ASSERT_NE (nullptr, block_data.first.get ());
ASSERT_NE (nullptr, block_data.second.get ());
ASSERT_EQ (nano::genesis_account, block_data.second.get ()->source);
block_data = request->get_next ();
ASSERT_EQ (nullptr, block_data.first.get ());
ASSERT_EQ (nullptr, block_data.second.get ());
}
}
TEST (bootstrap, tcp_node_id_handshake)
TEST (tcp_listener, tcp_node_id_handshake)
{
nano::system system (24000, 1);
auto socket (std::make_shared<nano::socket> (system.nodes[0]));
@ -1473,7 +764,7 @@ TEST (bootstrap, tcp_node_id_handshake)
}
}
TEST (bootstrap, tcp_listener_timeout_empty)
TEST (tcp_listener, tcp_listener_timeout_empty)
{
nano::system system (24000, 1);
auto node0 (system.nodes[0]);
@ -1500,7 +791,7 @@ TEST (bootstrap, tcp_listener_timeout_empty)
}
}
TEST (bootstrap, tcp_listener_timeout_node_id_handshake)
TEST (tcp_listener, tcp_listener_timeout_node_id_handshake)
{
nano::system system (24000, 1);
auto node0 (system.nodes[0]);

View file

@ -827,39 +827,19 @@ nano::hash_or_account::operator nano::uint256_union const & () const
return raw;
}
nano::link::link (uint64_t value_a) :
hash_or_account (value_a)
{
}
nano::root::root (uint64_t value_a) :
hash_or_account (value_a)
{
}
nano::block_hash const & nano::root::previous () const
{
return hash;
}
bool nano::root::operator== (nano::root const & root_a) const
bool nano::hash_or_account::operator== (nano::hash_or_account const & hash_or_account_a) const
{
return bytes == root_a.bytes;
return bytes == hash_or_account_a.bytes;
}
bool nano::root::operator!= (nano::root const & root_a) const
bool nano::hash_or_account::operator!= (nano::hash_or_account const & hash_or_account_a) const
{
return !(*this == root_a);
}
bool nano::link::operator== (nano::link const & link_a) const
{
return bytes == link_a.bytes;
}
bool nano::link::operator!= (nano::link const & link_a) const
{
return !(*this == link_a);
return !(*this == hash_or_account_a);
}
std::string nano::to_string_hex (uint64_t const value_a)
@ -942,20 +922,30 @@ double nano::difficulty::to_multiplier (uint64_t const difficulty_a, uint64_t co
nano::public_key::operator nano::link const & () const
{
return nano::to_link (*this);
return reinterpret_cast<nano::link const &> (*this);
}
nano::public_key::operator nano::root const & () const
{
return nano::to_root (*this);
return reinterpret_cast<nano::root const &> (*this);
}
nano::public_key::operator nano::hash_or_account const & () const
{
return reinterpret_cast<nano::hash_or_account const &> (*this);
}
nano::block_hash::operator nano::link const & () const
{
return nano::to_link (*this);
return reinterpret_cast<nano::link const &> (*this);
}
nano::block_hash::operator nano::root const & () const
{
return nano::to_root (*this);
return reinterpret_cast<nano::root const &> (*this);
}
nano::block_hash::operator nano::hash_or_account const & () const
{
return reinterpret_cast<nano::hash_or_account const &> (*this);
}

View file

@ -100,6 +100,7 @@ static_assert (std::is_nothrow_move_constructible<uint256_union>::value, "uint25
class link;
class root;
class hash_or_account;
// All keys and hashes are 256 bit.
class block_hash final : public uint256_union
@ -108,6 +109,7 @@ public:
using uint256_union::uint256_union;
operator nano::link const & () const;
operator nano::root const & () const;
operator nano::hash_or_account const & () const;
};
class public_key final : public uint256_union
@ -122,6 +124,7 @@ public:
operator nano::link const & () const;
operator nano::root const & () const;
operator nano::hash_or_account const & () const;
};
class wallet_id : public uint256_union
@ -135,6 +138,9 @@ using account = public_key;
class hash_or_account
{
public:
hash_or_account () = default;
hash_or_account (uint64_t value_a);
bool is_zero () const;
void clear ();
std::string to_string () const;
@ -146,6 +152,9 @@ public:
operator nano::account const & () const;
operator nano::uint256_union const & () const;
bool operator== (nano::hash_or_account const &) const;
bool operator!= (nano::hash_or_account const &) const;
union
{
std::array<uint8_t, 32> bytes;
@ -153,33 +162,20 @@ public:
nano::account account;
nano::block_hash hash;
};
protected:
// Cannot instantiate a concrete version of this class
hash_or_account () = default;
hash_or_account (uint64_t value_a);
};
// A link can either be a destination account or source hash
class link final : public hash_or_account
{
public:
link () = default;
link (uint64_t value_a);
bool operator== (nano::link const &) const;
bool operator!= (nano::link const &) const;
using hash_or_account::hash_or_account;
};
// A root can either be an open block hash or a previous hash
class root final : public hash_or_account
{
public:
root () = default;
root (uint64_t value_a);
bool operator== (nano::root const &) const;
bool operator!= (nano::root const &) const;
using hash_or_account::hash_or_account;
nano::block_hash const & previous () const;
};
@ -227,33 +223,6 @@ public:
};
static_assert (std::is_nothrow_move_constructible<uint512_union>::value, "uint512_union should be noexcept MoveConstructible");
inline nano::link const & to_link (nano::block_hash const & hash_a)
{
return reinterpret_cast<nano::link const &> (hash_a);
}
inline nano::link const & to_link (nano::account const & account_a)
{
return reinterpret_cast<nano::link const &> (account_a);
}
inline nano::root const & to_root (nano::block_hash const & hash_a)
{
return reinterpret_cast<nano::root const &> (hash_a);
}
inline nano::root const & to_root (nano::account const & account_a)
{
return reinterpret_cast<nano::root const &> (account_a);
}
inline nano::account const & root_as_account (nano::root const & root_a)
{
static_assert (sizeof (nano::root) == sizeof (nano::account), "Sizes do not match");
static_assert (std::is_standard_layout<nano::root>::value && std::is_standard_layout<nano::account>::value, "Both types must have standard layout");
return reinterpret_cast<nano::account const &> (root_a);
}
class signature : public uint512_union
{
public:

View file

@ -226,7 +226,7 @@ void nano::block_processor::verify_state_blocks (nano::unique_lock<std::mutex> &
else
{
blocks_filter.erase (filter_item (hashes[i], blocks_signatures[i]));
requeue_invalid (hashes[i]);
requeue_invalid (hashes[i], item);
}
items.pop_front ();
}
@ -456,7 +456,7 @@ nano::process_return nano::block_processor::process_one (nano::write_transaction
{
node.logger.try_log (boost::str (boost::format ("Bad signature for: %1%") % hash.to_string ()));
}
requeue_invalid (hash);
requeue_invalid (hash, info_a);
break;
}
case nano::process_result::negative_spend:
@ -552,11 +552,12 @@ nano::block_hash nano::block_processor::filter_item (nano::block_hash const & ha
return result;
}
void nano::block_processor::requeue_invalid (nano::block_hash const & hash_a)
void nano::block_processor::requeue_invalid (nano::block_hash const & hash_a, nano::unchecked_info const & info_a)
{
assert (hash_a == info_a.block->hash ());
auto attempt (node.bootstrap_initiator.current_attempt ());
if (attempt != nullptr && attempt->mode == nano::bootstrap_mode::lazy)
{
attempt->lazy_requeue (hash_a);
attempt->lazy_requeue (hash_a, info_a.block->previous (), info_a.confirmed);
}
}

View file

@ -59,7 +59,7 @@ private:
void verify_state_blocks (nano::unique_lock<std::mutex> &, size_t = std::numeric_limits<size_t>::max ());
void process_batch (nano::unique_lock<std::mutex> &);
void process_live (nano::block_hash const &, std::shared_ptr<nano::block>, const bool = false);
void requeue_invalid (nano::block_hash const &);
void requeue_invalid (nano::block_hash const &, nano::unchecked_info const &);
bool stopped;
bool active;
bool awaiting_write{ false };

View file

@ -14,9 +14,12 @@
constexpr double nano::bootstrap_limits::bootstrap_connection_scale_target_blocks;
constexpr double nano::bootstrap_limits::bootstrap_minimum_blocks_per_sec;
constexpr unsigned nano::bootstrap_limits::bootstrap_frontier_retry_limit;
constexpr unsigned nano::bootstrap_limits::bootstrap_lazy_retry_limit;
constexpr double nano::bootstrap_limits::bootstrap_minimum_termination_time_sec;
constexpr unsigned nano::bootstrap_limits::bootstrap_max_new_connections;
constexpr std::chrono::seconds nano::bootstrap_limits::lazy_flush_delay_sec;
constexpr unsigned nano::bootstrap_limits::bootstrap_lazy_destinations_request_limit;
constexpr std::chrono::seconds nano::bootstrap_limits::lazy_destinations_flush_delay_sec;
nano::bootstrap_client::bootstrap_client (std::shared_ptr<nano::node> node_a, std::shared_ptr<nano::bootstrap_attempt> attempt_a, std::shared_ptr<nano::transport::channel_tcp> channel_a) :
node (node_a),
@ -142,7 +145,7 @@ void nano::bootstrap_attempt::request_pull (nano::unique_lock<std::mutex> & lock
if (mode != nano::bootstrap_mode::legacy)
{
// Check if pull is obsolete (head was processed)
nano::unique_lock<std::mutex> lock (lazy_mutex);
nano::lock_guard<std::mutex> lazy_lock (lazy_mutex);
auto transaction (node->store.tx_begin_read ());
while (!pulls.empty () && !pull.head.is_zero () && (lazy_blocks.find (pull.head) != lazy_blocks.end () || node->store.block_exists (transaction, pull.head)))
{
@ -525,20 +528,20 @@ void nano::bootstrap_attempt::add_pull (nano::pull_info const & pull_a)
void nano::bootstrap_attempt::requeue_pull (nano::pull_info const & pull_a)
{
auto pull (pull_a);
if (++pull.attempts < (nano::bootstrap_limits::bootstrap_frontier_retry_limit + (pull.processed / 10000)))
++pull.attempts;
if (pull.attempts < (!node->network_params.network.is_test_network () ? nano::bootstrap_limits::bootstrap_frontier_retry_limit : (nano::bootstrap_limits::bootstrap_frontier_retry_limit / 8) + (pull.processed / 10000)))
{
nano::lock_guard<std::mutex> lock (mutex);
pulls.push_front (pull);
condition.notify_all ();
}
else if (mode == nano::bootstrap_mode::lazy)
else if (mode == nano::bootstrap_mode::lazy && (pull.confirmed_head || pull.attempts <= (!node->network_params.network.is_test_network () ? nano::bootstrap_limits::bootstrap_lazy_retry_limit : (nano::bootstrap_limits::bootstrap_frontier_retry_limit / 8) + (pull.processed / node->network_params.bootstrap.lazy_max_pull_blocks))))
{
assert (pull.root == pull.head);
if (!lazy_processed_or_exists (pull.root))
assert (pull.account_or_head == pull.head);
if (!lazy_processed_or_exists (pull.account_or_head))
{
// Retry for lazy pulls (not weak state block link assumptions)
// Retry for lazy pulls
nano::lock_guard<std::mutex> lock (mutex);
pull.attempts++;
pulls.push_back (pull);
condition.notify_all ();
}
@ -547,52 +550,58 @@ void nano::bootstrap_attempt::requeue_pull (nano::pull_info const & pull_a)
{
if (node->config.logging.bulk_pull_logging ())
{
node->logger.try_log (boost::str (boost::format ("Failed to pull account %1% down to %2% after %3% attempts and %4% blocks processed") % pull.root.to_account () % pull.end.to_string () % pull.attempts % pull.processed));
node->logger.try_log (boost::str (boost::format ("Failed to pull account %1% down to %2% after %3% attempts and %4% blocks processed") % pull.account_or_head.to_account () % pull.end.to_string () % pull.attempts % pull.processed));
}
node->stats.inc (nano::stat::type::bootstrap, nano::stat::detail::bulk_pull_failed_account, nano::stat::dir::in);
node->bootstrap_initiator.cache.add (pull);
if (mode == nano::bootstrap_mode::lazy && pull.processed > 0)
{
assert (pull.account_or_head == pull.head);
nano::lock_guard<std::mutex> lazy_lock (lazy_mutex);
lazy_add (pull.account_or_head, pull.confirmed_head);
}
}
}
void nano::bootstrap_attempt::add_bulk_push_target (nano::block_hash const & head, nano::block_hash const & end)
{
nano::lock_guard<std::mutex> lock (mutex);
bulk_push_targets.push_back (std::make_pair (head, end));
bulk_push_targets.emplace_back (head, end);
}
void nano::bootstrap_attempt::lazy_start (nano::block_hash const & hash_a)
void nano::bootstrap_attempt::lazy_start (nano::hash_or_account const & hash_or_account_a, bool confirmed)
{
nano::unique_lock<std::mutex> lock (lazy_mutex);
nano::lock_guard<std::mutex> lazy_lock (lazy_mutex);
// Add start blocks, limit 1024 (32k with disabled legacy bootstrap)
size_t max_keys (node->flags.disable_legacy_bootstrap ? 32 * 1024 : 1024);
if (lazy_keys.size () < max_keys && lazy_keys.find (hash_a) == lazy_keys.end () && lazy_blocks.find (hash_a) == lazy_blocks.end ())
if (lazy_keys.size () < max_keys && lazy_keys.find (hash_or_account_a) == lazy_keys.end () && lazy_blocks.find (hash_or_account_a) == lazy_blocks.end ())
{
lazy_keys.insert (hash_a);
lazy_pulls.push_back (hash_a);
lazy_keys.insert (hash_or_account_a);
lazy_pulls.emplace_back (hash_or_account_a, confirmed);
}
}
void nano::bootstrap_attempt::lazy_add (nano::block_hash const & hash_a)
void nano::bootstrap_attempt::lazy_add (nano::hash_or_account const & hash_or_account_a, bool confirmed_head)
{
// Add only unknown blocks
assert (!lazy_mutex.try_lock ());
if (lazy_blocks.find (hash_a) == lazy_blocks.end ())
if (lazy_blocks.find (hash_or_account_a) == lazy_blocks.end ())
{
lazy_pulls.push_back (hash_a);
lazy_pulls.emplace_back (hash_or_account_a, confirmed_head);
}
}
void nano::bootstrap_attempt::lazy_requeue (nano::block_hash const & hash_a)
void nano::bootstrap_attempt::lazy_requeue (nano::block_hash const & hash_a, nano::block_hash const & previous_a, bool confirmed_a)
{
nano::unique_lock<std::mutex> lock (lazy_mutex);
nano::lock_guard<std::mutex> lazy_lock (lazy_mutex);
// Add only known blocks
auto existing (lazy_blocks.find (hash_a));
if (existing != lazy_blocks.end ())
{
lazy_blocks.erase (existing);
lazy_mutex.unlock ();
requeue_pull (nano::pull_info (hash_a, hash_a, nano::block_hash (0), static_cast<nano::pull_info::count_t> (1)));
requeue_pull (nano::pull_info (hash_a, hash_a, previous_a, static_cast<nano::pull_info::count_t> (1), confirmed_a));
}
}
@ -600,15 +609,15 @@ void nano::bootstrap_attempt::lazy_pull_flush ()
{
assert (!mutex.try_lock ());
last_lazy_flush = std::chrono::steady_clock::now ();
nano::unique_lock<std::mutex> lazy_lock (lazy_mutex);
nano::lock_guard<std::mutex> lazy_lock (lazy_mutex);
auto transaction (node->store.tx_begin_read ());
for (auto & pull_start : lazy_pulls)
{
// Recheck if block was already processed
if (lazy_blocks.find (pull_start) == lazy_blocks.end () && !node->store.block_exists (transaction, pull_start))
if (lazy_blocks.find (pull_start.first) == lazy_blocks.end () && !node->store.block_exists (transaction, pull_start.first))
{
assert (node->network_params.bootstrap.lazy_max_pull_blocks <= std::numeric_limits<nano::pull_info::count_t>::max ());
pulls.push_back (nano::pull_info (pull_start, pull_start, nano::block_hash (0), static_cast<nano::pull_info::count_t> (node->network_params.bootstrap.lazy_max_pull_blocks)));
pulls.emplace_back (pull_start.first, pull_start.first, nano::block_hash (0), static_cast<nano::pull_info::count_t> (node->network_params.bootstrap.lazy_max_pull_blocks), pull_start.second);
}
}
lazy_pulls.clear ();
@ -618,7 +627,7 @@ bool nano::bootstrap_attempt::lazy_finished ()
{
bool result (true);
auto transaction (node->store.tx_begin_read ());
nano::unique_lock<std::mutex> lock (lazy_mutex);
nano::lock_guard<std::mutex> lazy_lock (lazy_mutex);
for (auto it (lazy_keys.begin ()), end (lazy_keys.end ()); it != end && !stopped;)
{
if (node->store.block_exists (transaction, *it))
@ -648,6 +657,7 @@ void nano::bootstrap_attempt::lazy_clear ()
lazy_pulls.clear ();
lazy_state_backlog.clear ();
lazy_balances.clear ();
lazy_destinations.clear ();
}
void nano::bootstrap_attempt::lazy_run ()
@ -680,6 +690,12 @@ void nano::bootstrap_attempt::lazy_run ()
{
lazy_pull_flush ();
}
// Start destinations check & backlog cleanup
if (iterations % 200 == 0 && pulls.empty ())
{
lazy_backlog_cleanup ();
lazy_destinations_flush ();
}
}
// Flushing lazy pulls
lazy_pull_flush ();
@ -687,6 +703,7 @@ void nano::bootstrap_attempt::lazy_run ()
if (pulls.empty ())
{
lazy_backlog_cleanup ();
lazy_destinations_flush ();
}
}
if (!stopped)
@ -722,12 +739,12 @@ void nano::bootstrap_attempt::lazy_run ()
idle.clear ();
}
bool nano::bootstrap_attempt::process_block (std::shared_ptr<nano::block> block_a, nano::account const & known_account_a, uint64_t pull_blocks, bool block_expected)
bool nano::bootstrap_attempt::process_block (std::shared_ptr<nano::block> block_a, nano::account const & known_account_a, uint64_t pull_blocks, bool block_expected, bool confirmed_head)
{
bool stop_pull (false);
if (mode != nano::bootstrap_mode::legacy && block_expected)
{
stop_pull = process_block_lazy (block_a, known_account_a, pull_blocks);
stop_pull = process_block_lazy (block_a, known_account_a, pull_blocks, confirmed_head);
}
else if (mode != nano::bootstrap_mode::legacy)
{
@ -742,30 +759,38 @@ bool nano::bootstrap_attempt::process_block (std::shared_ptr<nano::block> block_
return stop_pull;
}
bool nano::bootstrap_attempt::process_block_lazy (std::shared_ptr<nano::block> block_a, nano::account const & known_account_a, uint64_t pull_blocks)
bool nano::bootstrap_attempt::process_block_lazy (std::shared_ptr<nano::block> block_a, nano::account const & known_account_a, uint64_t pull_blocks, bool confirmed_head)
{
bool stop_pull (false);
auto hash (block_a->hash ());
nano::unique_lock<std::mutex> lock (lazy_mutex);
nano::lock_guard<std::mutex> lazy_lock (lazy_mutex);
// Processing new blocks
if (lazy_blocks.find (hash) == lazy_blocks.end ())
{
nano::unchecked_info info (block_a, known_account_a, 0, nano::signature_verification::unknown);
nano::unchecked_info info (block_a, known_account_a, 0, nano::signature_verification::unknown, confirmed_head);
node->block_processor.add (info);
// Search for new dependencies
if (!block_a->source ().is_zero () && !node->ledger.block_exists (block_a->source ()) && block_a->source () != node->network_params.ledger.genesis_account)
{
lazy_add (block_a->source ());
lazy_add (block_a->source (), confirmed_head);
}
else if (block_a->type () == nano::block_type::state)
{
lazy_block_state (block_a);
lazy_block_state (block_a, confirmed_head);
}
else if (block_a->type () == nano::block_type::send)
{
std::shared_ptr<nano::send_block> block_l (std::static_pointer_cast<nano::send_block> (block_a));
if (block_l != nullptr && !block_l->hashables.destination.is_zero ())
{
lazy_destinations_increment (block_l->hashables.destination);
}
}
lazy_blocks.insert (hash);
// Adding lazy balances for first processed block in pull
if (pull_blocks == 0 && (block_a->type () == nano::block_type::state || block_a->type () == nano::block_type::send))
{
lazy_balances.insert (std::make_pair (hash, block_a->balance ().number ()));
lazy_balances.emplace (hash, block_a->balance ().number ());
}
// Clearing lazy balances for previous block
if (!block_a->previous ().is_zero () && lazy_balances.find (block_a->previous ()) != lazy_balances.end ())
@ -782,7 +807,7 @@ bool nano::bootstrap_attempt::process_block_lazy (std::shared_ptr<nano::block> b
return stop_pull;
}
void nano::bootstrap_attempt::lazy_block_state (std::shared_ptr<nano::block> block_a)
void nano::bootstrap_attempt::lazy_block_state (std::shared_ptr<nano::block> block_a, bool confirmed_head)
{
std::shared_ptr<nano::state_block> block_l (std::static_pointer_cast<nano::state_block> (block_a));
if (block_l != nullptr)
@ -797,14 +822,18 @@ void nano::bootstrap_attempt::lazy_block_state (std::shared_ptr<nano::block> blo
// If state block previous is 0 then source block required
if (previous.is_zero ())
{
lazy_add (link);
lazy_add (link, confirmed_head);
}
// In other cases previous block balance required to find out subtype of state block
else if (node->store.block_exists (transaction, previous))
{
if (node->ledger.balance (transaction, previous) <= balance)
{
lazy_add (link);
lazy_add (link, confirmed_head);
}
else
{
lazy_destinations_increment (link);
}
}
// Search balance of already processed previous blocks
@ -815,7 +844,11 @@ void nano::bootstrap_attempt::lazy_block_state (std::shared_ptr<nano::block> blo
{
if (previous_balance->second <= balance)
{
lazy_add (link);
lazy_add (link, confirmed_head);
}
else
{
lazy_destinations_increment (link);
}
lazy_balances.erase (previous_balance);
}
@ -823,7 +856,7 @@ void nano::bootstrap_attempt::lazy_block_state (std::shared_ptr<nano::block> blo
// Insert in backlog state blocks if previous wasn't already processed
else
{
lazy_state_backlog.insert (std::make_pair (previous, std::make_pair (link, balance)));
lazy_state_backlog.emplace (previous, nano::lazy_state_backlog_item{ link, balance, confirmed_head });
}
}
}
@ -839,15 +872,20 @@ void nano::bootstrap_attempt::lazy_block_state_backlog_check (std::shared_ptr<na
// Retrieve balance for previous state & send blocks
if (block_a->type () == nano::block_type::state || block_a->type () == nano::block_type::send)
{
if (block_a->balance ().number () <= next_block.second) // balance
if (block_a->balance ().number () <= next_block.balance) // balance
{
lazy_add (next_block.first); // link
lazy_add (next_block.link, next_block.confirmed); // link
}
else
{
lazy_destinations_increment (next_block.link);
}
}
// Assumption for other legacy block types
else
else if (lazy_undefined_links.find (next_block.link) == lazy_undefined_links.end ())
{
// Disabled
lazy_add (next_block.link, false); // Head is not confirmed. It can be account or hash or non-existing
lazy_undefined_links.insert (next_block.link);
}
lazy_state_backlog.erase (find_state);
}
@ -856,25 +894,65 @@ void nano::bootstrap_attempt::lazy_block_state_backlog_check (std::shared_ptr<na
void nano::bootstrap_attempt::lazy_backlog_cleanup ()
{
auto transaction (node->store.tx_begin_read ());
nano::lock_guard<std::mutex> lock (lazy_mutex);
nano::lock_guard<std::mutex> lazy_lock (lazy_mutex);
for (auto it (lazy_state_backlog.begin ()), end (lazy_state_backlog.end ()); it != end && !stopped;)
{
if (node->store.block_exists (transaction, it->first))
{
auto next_block (it->second);
if (node->ledger.balance (transaction, it->first) <= next_block.second) // balance
if (node->ledger.balance (transaction, it->first) <= next_block.balance) // balance
{
lazy_add (next_block.first); // link
lazy_add (next_block.link, next_block.confirmed); // link
}
else
{
lazy_destinations_increment (next_block.link);
}
it = lazy_state_backlog.erase (it);
}
else
{
lazy_add (it->first, it->second.confirmed);
++it;
}
}
}
void nano::bootstrap_attempt::lazy_destinations_increment (nano::account const & destination_a)
{
// Update accounts counter for send blocks
auto existing (lazy_destinations.get<account_tag> ().find (destination_a));
if (existing != lazy_destinations.get<account_tag> ().end ())
{
lazy_destinations.get<account_tag> ().modify (existing, [](nano::lazy_destinations_item & item_a) {
++item_a.count;
});
}
else
{
lazy_destinations.insert (nano::lazy_destinations_item{ destination_a, 1 });
}
}
void nano::bootstrap_attempt::lazy_destinations_flush ()
{
size_t count (0);
nano::lock_guard<std::mutex> lazy_lock (lazy_mutex);
if (last_lazy_destinations_flush + nano::bootstrap_limits::lazy_destinations_flush_delay_sec < std::chrono::steady_clock::now ())
{
for (auto it (lazy_destinations.get<count_tag> ().begin ()), end (lazy_destinations.get<count_tag> ().end ()); it != end && count < nano::bootstrap_limits::bootstrap_lazy_destinations_request_limit && !stopped;)
{
lazy_add (it->account, false);
it = lazy_destinations.get<count_tag> ().erase (it);
++count;
}
if (count > nano::bootstrap_limits::bootstrap_lazy_destinations_request_limit / 4)
{
last_lazy_destinations_flush = std::chrono::steady_clock::now ();
}
}
}
bool nano::bootstrap_attempt::lazy_processed_or_exists (nano::block_hash const & hash_a)
{
bool result (false);
@ -1020,7 +1098,7 @@ void nano::bootstrap_initiator::bootstrap (nano::endpoint const & endpoint_a, bo
}
}
void nano::bootstrap_initiator::bootstrap_lazy (nano::block_hash const & hash_a, bool force)
void nano::bootstrap_initiator::bootstrap_lazy (nano::hash_or_account const & hash_or_account_a, bool force, bool confirmed)
{
{
nano::unique_lock<std::mutex> lock (mutex);
@ -1039,7 +1117,7 @@ void nano::bootstrap_initiator::bootstrap_lazy (nano::block_hash const & hash_a,
{
attempt = std::make_shared<nano::bootstrap_attempt> (node.shared (), nano::bootstrap_mode::lazy);
}
attempt->lazy_start (hash_a);
attempt->lazy_start (hash_or_account_a, confirmed);
}
condition.notify_all ();
}
@ -1170,7 +1248,7 @@ void nano::pulls_cache::add (nano::pull_info const & pull_a)
cache.erase (cache.begin ());
}
assert (cache.size () <= cache_size_max);
nano::uint512_union head_512 (pull_a.root, pull_a.head_original);
nano::uint512_union head_512 (pull_a.account_or_head, pull_a.head_original);
auto existing (cache.get<account_head_tag> ().find (head_512));
if (existing == cache.get<account_head_tag> ().end ())
{
@ -1193,7 +1271,7 @@ void nano::pulls_cache::add (nano::pull_info const & pull_a)
void nano::pulls_cache::update_pull (nano::pull_info & pull_a)
{
nano::lock_guard<std::mutex> guard (pulls_cache_mutex);
nano::uint512_union head_512 (pull_a.root, pull_a.head_original);
nano::uint512_union head_512 (pull_a.account_or_head, pull_a.head_original);
auto existing (cache.get<account_head_tag> ().find (head_512));
if (existing != cache.get<account_head_tag> ().end ())
{
@ -1204,7 +1282,7 @@ void nano::pulls_cache::update_pull (nano::pull_info & pull_a)
void nano::pulls_cache::remove (nano::pull_info const & pull_a)
{
nano::lock_guard<std::mutex> guard (pulls_cache_mutex);
nano::uint512_union head_512 (pull_a.root, pull_a.head_original);
nano::uint512_union head_512 (pull_a.account_or_head, pull_a.head_original);
cache.get<account_head_tag> ().erase (head_512);
}

View file

@ -43,6 +43,19 @@ enum class bootstrap_mode
lazy,
wallet_lazy
};
class lazy_state_backlog_item final
{
public:
nano::link link{ 0 };
nano::uint128_t balance{ 0 };
bool confirmed{ false };
};
class lazy_destinations_item final
{
public:
nano::account account{ 0 };
uint64_t count{ 0 };
};
class frontier_req_client;
class bulk_push_client;
class bootstrap_attempt final : public std::enable_shared_from_this<bootstrap_attempt>
@ -67,19 +80,21 @@ public:
unsigned target_connections (size_t pulls_remaining);
bool should_log ();
void add_bulk_push_target (nano::block_hash const &, nano::block_hash const &);
bool process_block (std::shared_ptr<nano::block>, nano::account const &, uint64_t, bool);
bool process_block (std::shared_ptr<nano::block>, nano::account const &, uint64_t, bool, bool);
/** Lazy bootstrap */
void lazy_run ();
void lazy_start (nano::block_hash const &);
void lazy_add (nano::block_hash const &);
void lazy_requeue (nano::block_hash const &);
void lazy_start (nano::hash_or_account const &, bool confirmed = true);
void lazy_add (nano::hash_or_account const &, bool = true);
void lazy_requeue (nano::block_hash const &, nano::block_hash const &, bool);
bool lazy_finished ();
void lazy_pull_flush ();
void lazy_clear ();
bool process_block_lazy (std::shared_ptr<nano::block>, nano::account const &, uint64_t);
void lazy_block_state (std::shared_ptr<nano::block>);
bool process_block_lazy (std::shared_ptr<nano::block>, nano::account const &, uint64_t, bool);
void lazy_block_state (std::shared_ptr<nano::block>, bool);
void lazy_block_state_backlog_check (std::shared_ptr<nano::block>, nano::block_hash const &);
void lazy_backlog_cleanup ();
void lazy_destinations_increment (nano::account const &);
void lazy_destinations_flush ();
bool lazy_processed_or_exists (nano::block_hash const &);
/** Lazy bootstrap */
/** Wallet bootstrap */
@ -105,16 +120,31 @@ public:
std::atomic<unsigned> runs_count;
std::vector<std::pair<nano::block_hash, nano::block_hash>> bulk_push_targets;
std::atomic<bool> stopped;
std::chrono::steady_clock::time_point attempt_start{ std::chrono::steady_clock::now () };
nano::bootstrap_mode mode;
std::mutex mutex;
nano::condition_variable condition;
// Lazy bootstrap
std::unordered_set<nano::block_hash> lazy_blocks;
std::unordered_map<nano::block_hash, std::pair<nano::block_hash, nano::uint128_t>> lazy_state_backlog;
std::unordered_map<nano::block_hash, nano::lazy_state_backlog_item> lazy_state_backlog;
std::unordered_set<nano::block_hash> lazy_undefined_links;
std::unordered_map<nano::block_hash, nano::uint128_t> lazy_balances;
std::unordered_set<nano::block_hash> lazy_keys;
std::deque<nano::block_hash> lazy_pulls;
std::deque<std::pair<nano::hash_or_account, bool>> lazy_pulls;
std::chrono::steady_clock::time_point last_lazy_flush{ std::chrono::steady_clock::now () };
class account_tag
{
};
class count_tag
{
};
boost::multi_index_container<
lazy_destinations_item,
boost::multi_index::indexed_by<
boost::multi_index::ordered_non_unique<boost::multi_index::tag<count_tag>, boost::multi_index::member<lazy_destinations_item, uint64_t, &lazy_destinations_item::count>, std::greater<uint64_t>>,
boost::multi_index::hashed_unique<boost::multi_index::tag<account_tag>, boost::multi_index::member<lazy_destinations_item, nano::account, &lazy_destinations_item::account>>>>
lazy_destinations;
std::chrono::steady_clock::time_point last_lazy_destinations_flush{ std::chrono::steady_clock::time_point{} };
std::mutex lazy_mutex;
// Wallet lazy bootstrap
std::deque<nano::account> wallet_accounts;
@ -170,7 +200,7 @@ public:
~bootstrap_initiator ();
void bootstrap (nano::endpoint const &, bool add_to_peers = true);
void bootstrap ();
void bootstrap_lazy (nano::block_hash const &, bool = false);
void bootstrap_lazy (nano::hash_or_account const &, bool force = false, bool confirmed = true);
void bootstrap_wallet (std::deque<nano::account> &);
void run_bootstrap ();
void notify_listeners (bool);
@ -203,9 +233,12 @@ public:
static constexpr double bootstrap_minimum_elapsed_seconds_blockrate = 0.02;
static constexpr double bootstrap_minimum_frontier_blocks_per_sec = 1000.0;
static constexpr unsigned bootstrap_frontier_retry_limit = 16;
static constexpr unsigned bootstrap_lazy_retry_limit = bootstrap_frontier_retry_limit * 10;
static constexpr double bootstrap_minimum_termination_time_sec = 30.0;
static constexpr unsigned bootstrap_max_new_connections = 10;
static constexpr unsigned bulk_push_cost_limit = 200;
static constexpr std::chrono::seconds lazy_flush_delay_sec = std::chrono::seconds (5);
static constexpr unsigned bootstrap_lazy_destinations_request_limit = 200;
static constexpr std::chrono::seconds lazy_destinations_flush_delay_sec = std::chrono::minutes (2);
};
}

View file

@ -3,12 +3,13 @@
#include <nano/node/node.hpp>
#include <nano/node/transport/tcp.hpp>
nano::pull_info::pull_info (nano::root const & root_a, nano::block_hash const & head_a, nano::block_hash const & end_a, count_t count_a) :
root (root_a),
nano::pull_info::pull_info (nano::hash_or_account const & account_or_head_a, nano::block_hash const & head_a, nano::block_hash const & end_a, count_t count_a, bool confirmed_head_a) :
account_or_head (account_or_head_a),
head (head_a),
head_original (head_a),
end (end_a),
count (count_a)
count (count_a),
confirmed_head (confirmed_head_a)
{
}
@ -30,13 +31,13 @@ nano::bulk_pull_client::~bulk_pull_client ()
pull.head = expected;
if (connection->attempt->mode != nano::bootstrap_mode::legacy)
{
pull.root = expected;
pull.account_or_head = expected;
}
pull.processed += pull_blocks - unexpected_count;
connection->attempt->requeue_pull (pull);
if (connection->node->config.logging.bulk_pull_logging ())
{
connection->node->logger.try_log (boost::str (boost::format ("Bulk pull end block is not expected %1% for account %2%") % pull.end.to_string () % pull.root.to_account ()));
connection->node->logger.try_log (boost::str (boost::format ("Bulk pull end block is not expected %1% for account %2%") % pull.end.to_string () % pull.account_or_head.to_account ()));
}
}
else
@ -52,12 +53,13 @@ nano::bulk_pull_client::~bulk_pull_client ()
void nano::bulk_pull_client::request ()
{
assert (!pull.head.is_zero () || !pull.confirmed_head);
expected = pull.head;
nano::bulk_pull req;
if (pull.head == pull.head_original)
{
// Account for new pulls
req.start = pull.root;
req.start = pull.account_or_head;
}
else
{
@ -71,7 +73,7 @@ void nano::bulk_pull_client::request ()
if (connection->node->config.logging.bulk_pull_logging ())
{
nano::unique_lock<std::mutex> lock (connection->attempt->mutex);
connection->node->logger.try_log (boost::str (boost::format ("Requesting account %1% from %2%. %3% accounts in queue") % pull.root.to_account () % connection->channel->to_string () % connection->attempt->pulls.size ()));
connection->node->logger.try_log (boost::str (boost::format ("Requesting account %1% from %2%. %3% accounts in queue") % pull.account_or_head.to_account () % connection->channel->to_string () % connection->attempt->pulls.size ()));
}
else if (connection->node->config.logging.network_logging () && connection->attempt->should_log ())
{
@ -212,7 +214,8 @@ void nano::bulk_pull_client::received_block (boost::system::error_code const & e
}
// Is block expected?
bool block_expected (false);
if (hash == expected)
bool unconfirmed_account_head (pull_blocks == 0 && !pull.confirmed_head && expected == pull.account_or_head && block->account () == pull.account_or_head);
if (hash == expected || unconfirmed_account_head)
{
expected = block->previous ();
block_expected = true;
@ -230,7 +233,7 @@ void nano::bulk_pull_client::received_block (boost::system::error_code const & e
connection->start_time = std::chrono::steady_clock::now ();
}
connection->attempt->total_blocks++;
bool stop_pull (connection->attempt->process_block (block, known_account, pull_blocks, block_expected));
bool stop_pull (connection->attempt->process_block (block, known_account, pull_blocks, block_expected, pull.confirmed_head));
pull_blocks++;
if (!stop_pull && !connection->hard_stop.load ())
{
@ -244,7 +247,6 @@ void nano::bulk_pull_client::received_block (boost::system::error_code const & e
}
else if (stop_pull && block_expected)
{
expected = pull.end;
connection->attempt->pool_connection (connection);
}
}

View file

@ -12,14 +12,15 @@ class pull_info
public:
using count_t = nano::bulk_pull::count_t;
pull_info () = default;
pull_info (nano::root const &, nano::block_hash const &, nano::block_hash const &, count_t = 0);
nano::root root{ 0 };
pull_info (nano::hash_or_account const &, nano::block_hash const &, nano::block_hash const &, count_t = 0, bool = false);
nano::hash_or_account account_or_head{ 0 };
nano::block_hash head{ 0 };
nano::block_hash head_original{ 0 };
nano::block_hash end{ 0 };
count_t count{ 0 };
unsigned attempts{ 0 };
uint64_t processed{ 0 };
bool confirmed_head{ false };
};
class bootstrap_client;
class bulk_pull_client final : public std::enable_shared_from_this<nano::bulk_pull_client>
@ -34,7 +35,7 @@ public:
void received_block (boost::system::error_code const &, size_t, nano::block_type);
nano::block_hash first ();
std::shared_ptr<nano::bootstrap_client> connection;
nano::root expected;
nano::block_hash expected;
nano::account known_account;
nano::pull_info pull;
uint64_t pull_blocks;
@ -65,7 +66,7 @@ public:
void no_block_sent (boost::system::error_code const &, size_t);
std::shared_ptr<nano::bootstrap_server> connection;
std::unique_ptr<nano::bulk_pull> request;
nano::root current;
nano::block_hash current;
bool include_start;
nano::bulk_pull::count_t max_count;
nano::bulk_pull::count_t sent_count;

View file

@ -80,6 +80,14 @@ std::unique_ptr<seq_con_info_component> collect_seq_con_info (bootstrap_listener
}
}
nano::bootstrap_server::bootstrap_server (std::shared_ptr<nano::socket> socket_a, std::shared_ptr<nano::node> node_a) :
receive_buffer (std::make_shared<std::vector<uint8_t>> ()),
socket (socket_a),
node (node_a)
{
receive_buffer->resize (1024);
}
nano::bootstrap_server::~bootstrap_server ()
{
if (node->config.logging.bulk_pull_logging ())
@ -117,14 +125,6 @@ void nano::bootstrap_server::stop ()
}
}
nano::bootstrap_server::bootstrap_server (std::shared_ptr<nano::socket> socket_a, std::shared_ptr<nano::node> node_a) :
receive_buffer (std::make_shared<std::vector<uint8_t>> ()),
socket (socket_a),
node (node_a)
{
receive_buffer->resize (512);
}
void nano::bootstrap_server::receive ()
{
// Increase timeout to receive TCP header (idle server socket)
@ -153,12 +153,12 @@ void nano::bootstrap_server::receive_header_action (boost::system::error_code co
nano::message_header header (error, type_stream);
if (!error)
{
auto this_l (shared_from_this ());
switch (header.type)
{
case nano::message_type::bulk_pull:
{
node->stats.inc (nano::stat::type::bootstrap, nano::stat::detail::bulk_pull, nano::stat::dir::in);
auto this_l (shared_from_this ());
socket->async_read (receive_buffer, header.payload_length_bytes (), [this_l, header](boost::system::error_code const & ec, size_t size_a) {
this_l->receive_bulk_pull_action (ec, size_a, header);
});
@ -167,7 +167,6 @@ void nano::bootstrap_server::receive_header_action (boost::system::error_code co
case nano::message_type::bulk_pull_account:
{
node->stats.inc (nano::stat::type::bootstrap, nano::stat::detail::bulk_pull_account, nano::stat::dir::in);
auto this_l (shared_from_this ());
socket->async_read (receive_buffer, header.payload_length_bytes (), [this_l, header](boost::system::error_code const & ec, size_t size_a) {
this_l->receive_bulk_pull_account_action (ec, size_a, header);
});
@ -176,7 +175,6 @@ void nano::bootstrap_server::receive_header_action (boost::system::error_code co
case nano::message_type::frontier_req:
{
node->stats.inc (nano::stat::type::bootstrap, nano::stat::detail::frontier_req, nano::stat::dir::in);
auto this_l (shared_from_this ());
socket->async_read (receive_buffer, header.payload_length_bytes (), [this_l, header](boost::system::error_code const & ec, size_t size_a) {
this_l->receive_frontier_req_action (ec, size_a, header);
});
@ -193,7 +191,6 @@ void nano::bootstrap_server::receive_header_action (boost::system::error_code co
}
case nano::message_type::keepalive:
{
auto this_l (shared_from_this ());
socket->async_read (receive_buffer, header.payload_length_bytes (), [this_l, header](boost::system::error_code const & ec, size_t size_a) {
this_l->receive_keepalive_action (ec, size_a, header);
});
@ -201,7 +198,6 @@ void nano::bootstrap_server::receive_header_action (boost::system::error_code co
}
case nano::message_type::publish:
{
auto this_l (shared_from_this ());
socket->async_read (receive_buffer, header.payload_length_bytes (), [this_l, header](boost::system::error_code const & ec, size_t size_a) {
this_l->receive_publish_action (ec, size_a, header);
});
@ -209,7 +205,6 @@ void nano::bootstrap_server::receive_header_action (boost::system::error_code co
}
case nano::message_type::confirm_ack:
{
auto this_l (shared_from_this ());
socket->async_read (receive_buffer, header.payload_length_bytes (), [this_l, header](boost::system::error_code const & ec, size_t size_a) {
this_l->receive_confirm_ack_action (ec, size_a, header);
});
@ -217,7 +212,6 @@ void nano::bootstrap_server::receive_header_action (boost::system::error_code co
}
case nano::message_type::confirm_req:
{
auto this_l (shared_from_this ());
socket->async_read (receive_buffer, header.payload_length_bytes (), [this_l, header](boost::system::error_code const & ec, size_t size_a) {
this_l->receive_confirm_req_action (ec, size_a, header);
});
@ -225,7 +219,6 @@ void nano::bootstrap_server::receive_header_action (boost::system::error_code co
}
case nano::message_type::node_id_handshake:
{
auto this_l (shared_from_this ());
socket->async_read (receive_buffer, header.payload_length_bytes (), [this_l, header](boost::system::error_code const & ec, size_t size_a) {
this_l->receive_node_id_handshake_action (ec, size_a, header);
});
@ -334,7 +327,7 @@ void nano::bootstrap_server::receive_keepalive_action (boost::system::error_code
std::unique_ptr<nano::keepalive> request (new nano::keepalive (error, stream, header_a));
if (!error)
{
if (type == nano::bootstrap_server_type::realtime || type == nano::bootstrap_server_type::realtime_response_server)
if (is_realtime_connection ())
{
add_request (std::unique_ptr<nano::message> (request.release ()));
}
@ -359,7 +352,7 @@ void nano::bootstrap_server::receive_publish_action (boost::system::error_code c
std::unique_ptr<nano::publish> request (new nano::publish (error, stream, header_a));
if (!error)
{
if (type == nano::bootstrap_server_type::realtime || type == nano::bootstrap_server_type::realtime_response_server)
if (is_realtime_connection ())
{
add_request (std::unique_ptr<nano::message> (request.release ()));
}
@ -384,7 +377,7 @@ void nano::bootstrap_server::receive_confirm_req_action (boost::system::error_co
std::unique_ptr<nano::confirm_req> request (new nano::confirm_req (error, stream, header_a));
if (!error)
{
if (type == nano::bootstrap_server_type::realtime || type == nano::bootstrap_server_type::realtime_response_server)
if (is_realtime_connection ())
{
add_request (std::unique_ptr<nano::message> (request.release ()));
}
@ -406,7 +399,7 @@ void nano::bootstrap_server::receive_confirm_ack_action (boost::system::error_co
std::unique_ptr<nano::confirm_ack> request (new nano::confirm_ack (error, stream, header_a));
if (!error)
{
if (type == nano::bootstrap_server_type::realtime || type == nano::bootstrap_server_type::realtime_response_server)
if (is_realtime_connection ())
{
add_request (std::unique_ptr<nano::message> (request.release ()));
}
@ -650,3 +643,8 @@ bool nano::bootstrap_server::is_bootstrap_connection ()
}
return type == nano::bootstrap_server_type::bootstrap;
}
bool nano::bootstrap_server::is_realtime_connection ()
{
return type == nano::bootstrap_server_type::realtime || type == nano::bootstrap_server_type::realtime_response_server;
}

View file

@ -60,9 +60,10 @@ public:
void add_request (std::unique_ptr<nano::message>);
void finish_request ();
void finish_request_async ();
void run_next ();
void timeout ();
void run_next ();
bool is_bootstrap_connection ();
bool is_realtime_connection ();
std::shared_ptr<std::vector<uint8_t>> receive_buffer;
std::shared_ptr<nano::socket> socket;
std::shared_ptr<nano::node> node;

View file

@ -355,7 +355,7 @@ public:
void serialize (nano::stream &) const override;
bool deserialize (nano::stream &);
void visit (nano::message_visitor &) const override;
nano::root start;
nano::hash_or_account start;
nano::block_hash end;
count_t count;
bool is_count_present () const;

View file

@ -228,6 +228,20 @@ nano::account nano::json_handler::account_impl (std::string account_text, std::e
return result;
}
nano::account_info nano::json_handler::account_info_impl (nano::transaction const & transaction_a, nano::account const & account_a)
{
nano::account_info result;
if (!ec)
{
if (node.store.account_get (transaction_a, account_a, result))
{
ec = nano::error_common::account_not_found;
node.bootstrap_initiator.bootstrap_lazy (account_a, false, false);
}
}
return result;
}
nano::amount nano::json_handler::amount_impl ()
{
nano::amount result (0);
@ -442,15 +456,11 @@ void nano::json_handler::account_block_count ()
if (!ec)
{
auto transaction (node.store.tx_begin_read ());
nano::account_info info;
if (!node.store.account_get (transaction, account, info))
auto info (account_info_impl (transaction, account));
if (!ec)
{
response_l.put ("block_count", std::to_string (info.block_count));
}
else
{
ec = nano::error_common::account_not_found;
}
}
response_errors ();
}
@ -522,10 +532,13 @@ void nano::json_handler::account_info ()
const bool weight = request.get<bool> ("weight", false);
const bool pending = request.get<bool> ("pending", false);
auto transaction (node.store.tx_begin_read ());
nano::account_info info;
auto info (account_info_impl (transaction, account));
uint64_t confirmation_height;
auto error = node.store.account_get (transaction, account, info) | node.store.confirmation_height_get (transaction, account, confirmation_height);
if (!error)
if (node.store.confirmation_height_get (transaction, account, confirmation_height))
{
ec = nano::error_common::account_not_found;
}
if (!ec)
{
response_l.put ("frontier", info.head.to_string ());
response_l.put ("open_block", info.open_block.to_string ());
@ -552,10 +565,6 @@ void nano::json_handler::account_info ()
response_l.put ("pending", account_pending.convert_to<std::string> ());
}
}
else
{
ec = nano::error_common::account_not_found;
}
}
response_errors ();
}
@ -655,15 +664,11 @@ void nano::json_handler::account_representative ()
if (!ec)
{
auto transaction (node.store.tx_begin_read ());
nano::account_info info;
if (!node.store.account_get (transaction, account, info))
auto info (account_info_impl (transaction, account));
if (!ec)
{
response_l.put ("representative", info.representative.to_account ());
}
else
{
ec = nano::error_common::account_not_found;
}
}
response_errors ();
}
@ -688,19 +693,15 @@ void nano::json_handler::account_representative_set ()
rpc_l->wallet_account_impl (transaction, wallet, account);
if (!rpc_l->ec)
{
nano::account_info info;
auto block_transaction (rpc_l->node.store.tx_begin_read ());
if (!rpc_l->node.store.account_get (block_transaction, account, info))
auto info (rpc_l->account_info_impl (block_transaction, account));
if (!rpc_l->ec)
{
if (nano::work_validate (info.head, work))
{
rpc_l->ec = nano::error_common::invalid_work;
}
}
else
{
rpc_l->ec = nano::error_common::account_not_found;
}
}
}
else if (!rpc_l->ec) // work == 0
@ -1695,12 +1696,15 @@ void nano::json_handler::bootstrap_status ()
response_l.put ("lazy_blocks", std::to_string (attempt->lazy_blocks.size ()));
response_l.put ("lazy_state_backlog", std::to_string (attempt->lazy_state_backlog.size ()));
response_l.put ("lazy_balances", std::to_string (attempt->lazy_balances.size ()));
response_l.put ("lazy_destinations", std::to_string (attempt->lazy_destinations.size ()));
response_l.put ("lazy_undefined_links", std::to_string (attempt->lazy_undefined_links.size ()));
response_l.put ("lazy_pulls", std::to_string (attempt->lazy_pulls.size ()));
response_l.put ("lazy_keys", std::to_string (attempt->lazy_keys.size ()));
if (!attempt->lazy_keys.empty ())
{
response_l.put ("lazy_key_1", (*(attempt->lazy_keys.begin ())).to_string ());
}
response_l.put ("duration", std::chrono::duration_cast<std::chrono::seconds> (std::chrono::steady_clock::now () - attempt->attempt_start).count ());
}
else
{
@ -2527,15 +2531,11 @@ void nano::json_handler::account_history ()
{
if (reverse)
{
nano::account_info info;
if (!node.store.account_get (transaction, account, info))
auto info (account_info_impl (transaction, account));
if (!ec)
{
hash = info.open_block;
}
else
{
ec = nano::error_common::account_not_found;
}
}
else
{
@ -3687,35 +3687,19 @@ void nano::json_handler::send ()
{
auto transaction (node.wallets.tx_begin_read ());
auto block_transaction (node.store.tx_begin_read ());
if (wallet->store.valid_password (transaction))
wallet_locked_impl (transaction, wallet);
wallet_account_impl (transaction, wallet, source);
auto info (account_info_impl (block_transaction, source));
if (!ec)
{
if (wallet->store.find (transaction, source) != wallet->store.end ())
{
nano::account_info info;
if (!node.store.account_get (block_transaction, source, info))
{
balance = (info.balance).number ();
}
else
{
ec = nano::error_common::account_not_found;
}
if (!ec && work)
{
if (nano::work_validate (info.head, work))
{
ec = nano::error_common::invalid_work;
}
}
}
else
{
ec = nano::error_common::account_not_found_wallet;
}
balance = (info.balance).number ();
}
else
if (!ec && work)
{
ec = nano::error_common::wallet_locked;
if (nano::work_validate (info.head, work))
{
ec = nano::error_common::invalid_work;
}
}
}
if (!ec)

View file

@ -145,6 +145,7 @@ public:
bool wallet_locked_impl (nano::transaction const &, std::shared_ptr<nano::wallet>);
bool wallet_account_impl (nano::transaction const &, std::shared_ptr<nano::wallet>, nano::account const &);
nano::account account_impl (std::string = "", std::error_code = nano::error_common::bad_account_number);
nano::account_info account_info_impl (nano::transaction const &, nano::account const &);
nano::amount amount_impl ();
std::shared_ptr<nano::block> block_impl (bool = true);
std::shared_ptr<nano::block> block_json_impl (bool = true);

View file

@ -7268,3 +7268,51 @@ TEST (rpc, epoch_upgrade)
ASSERT_FALSE (node->store.account_exists (transaction, 0));
}
}
TEST (rpc, account_lazy_start)
{
nano::system system;
nano::node_flags node_flags;
node_flags.disable_legacy_bootstrap = true;
auto node1 = system.add_node (nano::node_config (24000, system.logging), node_flags);
nano::genesis genesis;
nano::keypair key;
// Generating test chain
auto send1 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (genesis.hash ())));
ASSERT_EQ (nano::process_result::progress, node1->process (*send1).code);
auto open (std::make_shared<nano::open_block> (send1->hash (), key.pub, key.pub, key.prv, key.pub, *system.work.generate (key.pub)));
ASSERT_EQ (nano::process_result::progress, node1->process (*open).code);
// Start lazy bootstrap with account
auto node2 = system.add_node (nano::node_config (24001, system.logging), node_flags);
node2->network.udp_channels.insert (node1->network.endpoint (), node1->network_params.protocol.protocol_version);
enable_ipc_transport_tcp (node2->config.ipc_config.transport_tcp);
nano::node_rpc_config node_rpc_config;
nano::ipc::ipc_server ipc_server (*node2, node_rpc_config);
nano::rpc_config rpc_config (true);
nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config);
nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor);
rpc.start ();
boost::property_tree::ptree request;
request.put ("action", "account_info");
request.put ("account", key.pub.to_account ());
test_response response (request, rpc.config.port, system.io_ctx);
system.deadline_set (5s);
while (response.status == 0)
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (200, response.status);
boost::optional<std::string> account_error (response.json.get_optional<std::string> ("error"));
ASSERT_TRUE (account_error.is_initialized ());
// Check processed blocks
system.deadline_set (10s);
while (node2->bootstrap_initiator.in_progress ())
{
ASSERT_NO_ERROR (system.poll ());
}
node2->block_processor.flush ();
ASSERT_TRUE (node2->ledger.block_exists (send1->hash ()));
ASSERT_TRUE (node2->ledger.block_exists (open->hash ()));
}

View file

@ -319,11 +319,12 @@ nano::account const & nano::pending_key::key () const
return account;
}
nano::unchecked_info::unchecked_info (std::shared_ptr<nano::block> block_a, nano::account const & account_a, uint64_t modified_a, nano::signature_verification verified_a) :
nano::unchecked_info::unchecked_info (std::shared_ptr<nano::block> block_a, nano::account const & account_a, uint64_t modified_a, nano::signature_verification verified_a, bool confirmed_a) :
block (block_a),
account (account_a),
modified (modified_a),
verified (verified_a)
verified (verified_a),
confirmed (confirmed_a)
{
}

View file

@ -188,7 +188,7 @@ class unchecked_info final
{
public:
unchecked_info () = default;
unchecked_info (std::shared_ptr<nano::block>, nano::account const &, uint64_t, nano::signature_verification = nano::signature_verification::unknown);
unchecked_info (std::shared_ptr<nano::block>, nano::account const &, uint64_t, nano::signature_verification = nano::signature_verification::unknown, bool = false);
void serialize (nano::stream &) const;
bool deserialize (nano::stream &);
std::shared_ptr<nano::block> block;
@ -196,6 +196,7 @@ public:
/** Seconds since posix epoch */
uint64_t modified{ 0 };
nano::signature_verification verified{ nano::signature_verification::unknown };
bool confirmed{ false };
};
class block_info final