Remove legacy bootstrap
This commit is contained in:
parent
e12ce8f568
commit
cbadf5315c
30 changed files with 17 additions and 6962 deletions
|
|
@ -9,7 +9,6 @@ add_executable(
|
|||
block.cpp
|
||||
block_store.cpp
|
||||
blockprocessor.cpp
|
||||
bootstrap.cpp
|
||||
bootstrap_ascending.cpp
|
||||
bootstrap_server.cpp
|
||||
cli.cpp
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -265,8 +265,6 @@ TEST (network, send_valid_publish)
|
|||
nano::test::system system (2, type, node_flags);
|
||||
auto & node1 (*system.nodes[0]);
|
||||
auto & node2 (*system.nodes[1]);
|
||||
node1.bootstrap_initiator.stop ();
|
||||
node2.bootstrap_initiator.stop ();
|
||||
system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv);
|
||||
nano::keypair key2;
|
||||
system.wallet (1)->insert_adhoc (key2.prv);
|
||||
|
|
|
|||
|
|
@ -278,7 +278,6 @@ TEST (node, auto_bootstrap)
|
|||
system.nodes.push_back (node1);
|
||||
ASSERT_NE (nullptr, nano::test::establish_tcp (system, *node1, node0->network.endpoint ()));
|
||||
ASSERT_TIMELY_EQ (10s, node1->balance (key2.pub), node0->config.receive_minimum.number ());
|
||||
ASSERT_TIMELY (10s, !node1->bootstrap_initiator.in_progress ());
|
||||
ASSERT_TRUE (node1->block_or_pruned_exists (send1->hash ()));
|
||||
// Wait block receive
|
||||
ASSERT_TIMELY_EQ (5s, node1->ledger.block_count (), 3);
|
||||
|
|
@ -307,27 +306,6 @@ TEST (node, auto_bootstrap_reverse)
|
|||
ASSERT_TIMELY_EQ (10s, node1->balance (key2.pub), node0->config.receive_minimum.number ());
|
||||
}
|
||||
|
||||
TEST (node, auto_bootstrap_age)
|
||||
{
|
||||
nano::test::system system;
|
||||
nano::node_config config (system.get_available_port ());
|
||||
config.backlog_population.enable = false;
|
||||
nano::node_flags node_flags;
|
||||
node_flags.disable_bootstrap_bulk_push_client = true;
|
||||
node_flags.disable_lazy_bootstrap = true;
|
||||
node_flags.bootstrap_interval = 1;
|
||||
auto node0 = system.add_node (config, node_flags);
|
||||
auto node1 (std::make_shared<nano::node> (system.io_ctx, system.get_available_port (), nano::unique_path (), system.work, node_flags));
|
||||
ASSERT_FALSE (node1->init_error ());
|
||||
node1->start ();
|
||||
system.nodes.push_back (node1);
|
||||
ASSERT_NE (nullptr, nano::test::establish_tcp (system, *node1, node0->network.endpoint ()));
|
||||
// 4 bootstraps with frontiers age
|
||||
ASSERT_TIMELY (10s, node0->stats.count (nano::stat::type::bootstrap, nano::stat::detail::initiate_legacy_age, nano::stat::dir::out) >= 3);
|
||||
// More attempts with frontiers age
|
||||
ASSERT_GE (node0->stats.count (nano::stat::type::bootstrap, nano::stat::detail::initiate_legacy_age, nano::stat::dir::out), node0->stats.count (nano::stat::type::bootstrap, nano::stat::detail::initiate, nano::stat::dir::out));
|
||||
}
|
||||
|
||||
TEST (node, merge_peers)
|
||||
{
|
||||
nano::test::system system (1);
|
||||
|
|
@ -1107,7 +1085,6 @@ TEST (node, DISABLED_fork_stale)
|
|||
nano::test::system system2 (1);
|
||||
auto & node1 (*system1.nodes[0]);
|
||||
auto & node2 (*system2.nodes[0]);
|
||||
node2.bootstrap_initiator.bootstrap (node1.network.endpoint (), false);
|
||||
|
||||
auto channel = nano::test::establish_tcp (system1, node2, node1.network.endpoint ());
|
||||
auto vote = std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, 0, 0, std::vector<nano::block_hash> ());
|
||||
|
|
@ -1162,7 +1139,6 @@ TEST (node, DISABLED_fork_stale)
|
|||
node1.process_active (send2);
|
||||
node2.process_active (send1);
|
||||
node2.process_active (send2);
|
||||
node2.bootstrap_initiator.bootstrap (node1.network.endpoint (), false);
|
||||
while (node2.block (send1->hash ()) == nullptr)
|
||||
{
|
||||
system1.poll ();
|
||||
|
|
@ -1376,8 +1352,6 @@ TEST (node, DISABLED_bootstrap_no_publish)
|
|||
auto transaction = node0->ledger.tx_begin_write ();
|
||||
ASSERT_EQ (nano::block_status::progress, node0->ledger.process (transaction, send0));
|
||||
}
|
||||
ASSERT_FALSE (node1->bootstrap_initiator.in_progress ());
|
||||
node1->bootstrap_initiator.bootstrap (node0->network.endpoint (), false);
|
||||
ASSERT_TRUE (node1->active.empty ());
|
||||
system1.deadline_set (10s);
|
||||
while (node1->block (send0->hash ()) == nullptr)
|
||||
|
|
@ -1391,51 +1365,6 @@ TEST (node, DISABLED_bootstrap_no_publish)
|
|||
}
|
||||
}
|
||||
|
||||
// Check that an outgoing bootstrap request can push blocks
|
||||
// Test disabled because it's failing intermittently.
|
||||
// PR in which it got disabled: https://github.com/nanocurrency/nano-node/pull/3512
|
||||
// Issue for investigating it: https://github.com/nanocurrency/nano-node/issues/3515
|
||||
TEST (node, DISABLED_bootstrap_bulk_push)
|
||||
{
|
||||
nano::test::system system;
|
||||
nano::test::system system0;
|
||||
nano::test::system system1;
|
||||
nano::node_config config0 (system.get_available_port ());
|
||||
config0.backlog_population.enable = false;
|
||||
auto node0 (system0.add_node (config0));
|
||||
nano::node_config config1 (system.get_available_port ());
|
||||
config1.backlog_population.enable = false;
|
||||
auto node1 (system1.add_node (config1));
|
||||
nano::keypair key0;
|
||||
// node0 knows about send0 but node1 doesn't.
|
||||
auto send0 = nano::send_block_builder ()
|
||||
.previous (nano::dev::genesis->hash ())
|
||||
.destination (key0.pub)
|
||||
.balance (500)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*node0->work_generate_blocking (nano::dev::genesis->hash ()))
|
||||
.build ();
|
||||
ASSERT_EQ (nano::block_status::progress, node0->process (send0));
|
||||
|
||||
ASSERT_FALSE (node0->bootstrap_initiator.in_progress ());
|
||||
ASSERT_FALSE (node1->bootstrap_initiator.in_progress ());
|
||||
ASSERT_TRUE (node1->active.empty ());
|
||||
node0->bootstrap_initiator.bootstrap (node1->network.endpoint (), false);
|
||||
system1.deadline_set (10s);
|
||||
while (node1->block (send0->hash ()) == nullptr)
|
||||
{
|
||||
ASSERT_NO_ERROR (system0.poll ());
|
||||
ASSERT_NO_ERROR (system1.poll ());
|
||||
}
|
||||
// since this uses bulk_push, the new block should be republished
|
||||
system1.deadline_set (10s);
|
||||
while (node1->active.empty ())
|
||||
{
|
||||
ASSERT_NO_ERROR (system0.poll ());
|
||||
ASSERT_NO_ERROR (system1.poll ());
|
||||
}
|
||||
}
|
||||
|
||||
// Bootstrapping a forked open block should succeed.
|
||||
TEST (node, bootstrap_fork_open)
|
||||
{
|
||||
|
|
@ -1486,8 +1415,6 @@ TEST (node, bootstrap_fork_open)
|
|||
ASSERT_EQ (nano::block_status::progress, node1->process (open1));
|
||||
system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv);
|
||||
ASSERT_FALSE (node1->block_or_pruned_exists (open0->hash ()));
|
||||
ASSERT_FALSE (node1->bootstrap_initiator.in_progress ());
|
||||
node1->bootstrap_initiator.bootstrap (node0->network.endpoint (), false);
|
||||
ASSERT_TIMELY (1s, node1->active.empty ());
|
||||
ASSERT_TIMELY (10s, !node1->block_or_pruned_exists (open1->hash ()) && node1->block_or_pruned_exists (open0->hash ()));
|
||||
}
|
||||
|
|
@ -1495,12 +1422,10 @@ TEST (node, bootstrap_fork_open)
|
|||
// Unconfirmed blocks from bootstrap should be confirmed
|
||||
TEST (node, bootstrap_confirm_frontiers)
|
||||
{
|
||||
// create 2 separate systems, the 2 system do not interact with each other automatically
|
||||
nano::test::system system0 (1);
|
||||
nano::test::system system1 (1);
|
||||
auto node0 = system0.nodes[0];
|
||||
auto node1 = system1.nodes[0];
|
||||
system0.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv);
|
||||
nano::test::system system;
|
||||
auto node0 = system.add_node ();
|
||||
auto node1 = system.add_node ();
|
||||
system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv);
|
||||
nano::keypair key0;
|
||||
|
||||
// create block to send 500 raw from genesis to key0 and save into node0 ledger without immediately triggering an election
|
||||
|
|
@ -1512,25 +1437,7 @@ TEST (node, bootstrap_confirm_frontiers)
|
|||
.work (*node0->work_generate_blocking (nano::dev::genesis->hash ()))
|
||||
.build ();
|
||||
ASSERT_EQ (nano::block_status::progress, node0->process (send0));
|
||||
|
||||
// each system only has one node, so there should be no bootstrapping going on
|
||||
ASSERT_FALSE (node0->bootstrap_initiator.in_progress ());
|
||||
ASSERT_FALSE (node1->bootstrap_initiator.in_progress ());
|
||||
ASSERT_TRUE (node1->active.empty ());
|
||||
|
||||
// create a bootstrap connection from node1 to node0
|
||||
// this also has the side effect of adding node0 to node1's list of peers, which will trigger realtime connections too
|
||||
node1->bootstrap_initiator.bootstrap (node0->network.endpoint ());
|
||||
|
||||
// Wait until the block is confirmed on node1. Poll more than usual because we are polling
|
||||
// on 2 different systems at once and in sequence and there might be strange timing effects.
|
||||
system0.deadline_set (10s);
|
||||
system1.deadline_set (10s);
|
||||
while (!node1->ledger.confirmed.block_exists_or_pruned (node1->ledger.tx_begin_read (), send0->hash ()))
|
||||
{
|
||||
ASSERT_NO_ERROR (system0.poll (std::chrono::milliseconds (1)));
|
||||
ASSERT_NO_ERROR (system1.poll (std::chrono::milliseconds (1)));
|
||||
}
|
||||
ASSERT_TIMELY (10s, node1->block_confirmed (send0->hash ()));
|
||||
}
|
||||
|
||||
// Test that if we create a block that isn't confirmed, the bootstrapping processes sync the missing block.
|
||||
|
|
@ -1644,37 +1551,6 @@ TEST (node, balance_observer)
|
|||
}
|
||||
}
|
||||
|
||||
TEST (node, bootstrap_connection_scaling)
|
||||
{
|
||||
nano::test::system system (1);
|
||||
auto & node1 (*system.nodes[0]);
|
||||
ASSERT_EQ (34, node1.bootstrap_initiator.connections->target_connections (5000, 1));
|
||||
ASSERT_EQ (4, node1.bootstrap_initiator.connections->target_connections (0, 1));
|
||||
ASSERT_EQ (64, node1.bootstrap_initiator.connections->target_connections (50000, 1));
|
||||
ASSERT_EQ (64, node1.bootstrap_initiator.connections->target_connections (10000000000, 1));
|
||||
ASSERT_EQ (32, node1.bootstrap_initiator.connections->target_connections (5000, 0));
|
||||
ASSERT_EQ (1, node1.bootstrap_initiator.connections->target_connections (0, 0));
|
||||
ASSERT_EQ (64, node1.bootstrap_initiator.connections->target_connections (50000, 0));
|
||||
ASSERT_EQ (64, node1.bootstrap_initiator.connections->target_connections (10000000000, 0));
|
||||
ASSERT_EQ (36, node1.bootstrap_initiator.connections->target_connections (5000, 2));
|
||||
ASSERT_EQ (8, node1.bootstrap_initiator.connections->target_connections (0, 2));
|
||||
ASSERT_EQ (64, node1.bootstrap_initiator.connections->target_connections (50000, 2));
|
||||
ASSERT_EQ (64, node1.bootstrap_initiator.connections->target_connections (10000000000, 2));
|
||||
node1.config.bootstrap_connections = 128;
|
||||
ASSERT_EQ (64, node1.bootstrap_initiator.connections->target_connections (0, 1));
|
||||
ASSERT_EQ (64, node1.bootstrap_initiator.connections->target_connections (50000, 1));
|
||||
ASSERT_EQ (64, node1.bootstrap_initiator.connections->target_connections (0, 2));
|
||||
ASSERT_EQ (64, node1.bootstrap_initiator.connections->target_connections (50000, 2));
|
||||
node1.config.bootstrap_connections_max = 256;
|
||||
ASSERT_EQ (128, node1.bootstrap_initiator.connections->target_connections (0, 1));
|
||||
ASSERT_EQ (256, node1.bootstrap_initiator.connections->target_connections (50000, 1));
|
||||
ASSERT_EQ (256, node1.bootstrap_initiator.connections->target_connections (0, 2));
|
||||
ASSERT_EQ (256, node1.bootstrap_initiator.connections->target_connections (50000, 2));
|
||||
node1.config.bootstrap_connections_max = 0;
|
||||
ASSERT_EQ (1, node1.bootstrap_initiator.connections->target_connections (0, 1));
|
||||
ASSERT_EQ (1, node1.bootstrap_initiator.connections->target_connections (50000, 1));
|
||||
}
|
||||
|
||||
TEST (node, online_reps)
|
||||
{
|
||||
nano::test::system system (1);
|
||||
|
|
|
|||
|
|
@ -840,121 +840,6 @@ TEST (websocket, work)
|
|||
ASSERT_EQ (contents.get<std::string> ("reason"), "");
|
||||
}
|
||||
|
||||
// Test client subscribing to notifications for bootstrap
|
||||
TEST (websocket, bootstrap)
|
||||
{
|
||||
nano::test::system system;
|
||||
nano::node_config config = system.default_config ();
|
||||
config.websocket_config.enabled = true;
|
||||
config.websocket_config.port = system.get_available_port ();
|
||||
auto node1 (system.add_node (config));
|
||||
|
||||
ASSERT_EQ (0, node1->websocket.server->subscriber_count (nano::websocket::topic::bootstrap));
|
||||
|
||||
// Subscribe to bootstrap and wait for response asynchronously
|
||||
std::atomic<bool> ack_ready{ false };
|
||||
auto task = ([&ack_ready, config, &node1] () {
|
||||
fake_websocket_client client (node1->websocket.server->listening_port ());
|
||||
client.send_message (R"json({"action": "subscribe", "topic": "bootstrap", "ack": true})json");
|
||||
client.await_ack ();
|
||||
ack_ready = true;
|
||||
EXPECT_EQ (1, node1->websocket.server->subscriber_count (nano::websocket::topic::bootstrap));
|
||||
return client.get_response ();
|
||||
});
|
||||
auto future = std::async (std::launch::async, task);
|
||||
|
||||
// Wait for acknowledge
|
||||
ASSERT_TIMELY (5s, ack_ready);
|
||||
|
||||
// Start bootstrap attempt
|
||||
node1->bootstrap_initiator.bootstrap (true, "123abc");
|
||||
ASSERT_TIMELY_EQ (5s, nullptr, node1->bootstrap_initiator.current_attempt ());
|
||||
|
||||
// Wait for the bootstrap notification
|
||||
ASSERT_TIMELY_EQ (5s, future.wait_for (0s), std::future_status::ready);
|
||||
|
||||
// Check the bootstrap notification message
|
||||
auto response = future.get ();
|
||||
ASSERT_TRUE (response);
|
||||
std::stringstream stream;
|
||||
stream << response;
|
||||
boost::property_tree::ptree event;
|
||||
boost::property_tree::read_json (stream, event);
|
||||
ASSERT_EQ (event.get<std::string> ("topic"), "bootstrap");
|
||||
|
||||
auto & contents = event.get_child ("message");
|
||||
ASSERT_EQ (contents.get<std::string> ("reason"), "started");
|
||||
ASSERT_EQ (contents.get<std::string> ("id"), "123abc");
|
||||
ASSERT_EQ (contents.get<std::string> ("mode"), "legacy");
|
||||
|
||||
// Wait for bootstrap finish
|
||||
ASSERT_TIMELY (5s, !node1->bootstrap_initiator.in_progress ());
|
||||
}
|
||||
|
||||
TEST (websocket, bootstrap_exited)
|
||||
{
|
||||
nano::test::system system;
|
||||
nano::node_config config = system.default_config ();
|
||||
config.websocket_config.enabled = true;
|
||||
config.websocket_config.port = system.get_available_port ();
|
||||
auto node1 (system.add_node (config));
|
||||
|
||||
// Start bootstrap, exit after subscription
|
||||
std::atomic<bool> bootstrap_started{ false };
|
||||
nano::test::counted_completion subscribed_completion (1);
|
||||
std::thread bootstrap_thread ([node1, &system, &bootstrap_started, &subscribed_completion] () {
|
||||
std::shared_ptr<nano::bootstrap_attempt> attempt;
|
||||
while (attempt == nullptr)
|
||||
{
|
||||
std::this_thread::sleep_for (50ms);
|
||||
node1->bootstrap_initiator.bootstrap (true, "123abc");
|
||||
attempt = node1->bootstrap_initiator.current_attempt ();
|
||||
}
|
||||
ASSERT_NE (nullptr, attempt);
|
||||
bootstrap_started = true;
|
||||
EXPECT_FALSE (subscribed_completion.await_count_for (5s));
|
||||
});
|
||||
|
||||
// Wait for bootstrap start
|
||||
ASSERT_TIMELY (5s, bootstrap_started);
|
||||
|
||||
// Subscribe to bootstrap and wait for response asynchronously
|
||||
std::atomic<bool> ack_ready{ false };
|
||||
auto task = ([&ack_ready, config, &node1] () {
|
||||
fake_websocket_client client (node1->websocket.server->listening_port ());
|
||||
client.send_message (R"json({"action": "subscribe", "topic": "bootstrap", "ack": true})json");
|
||||
client.await_ack ();
|
||||
ack_ready = true;
|
||||
EXPECT_EQ (1, node1->websocket.server->subscriber_count (nano::websocket::topic::bootstrap));
|
||||
return client.get_response ();
|
||||
});
|
||||
auto future = std::async (std::launch::async, task);
|
||||
|
||||
// Wait for acknowledge
|
||||
ASSERT_TIMELY (5s, ack_ready);
|
||||
|
||||
// Wait for the bootstrap notification
|
||||
subscribed_completion.increment ();
|
||||
bootstrap_thread.join ();
|
||||
ASSERT_TIMELY_EQ (5s, future.wait_for (0s), std::future_status::ready);
|
||||
|
||||
// Check the bootstrap notification message
|
||||
auto response = future.get ();
|
||||
ASSERT_TRUE (response);
|
||||
std::stringstream stream;
|
||||
stream << response;
|
||||
boost::property_tree::ptree event;
|
||||
boost::property_tree::read_json (stream, event);
|
||||
ASSERT_EQ (event.get<std::string> ("topic"), "bootstrap");
|
||||
|
||||
auto & contents = event.get_child ("message");
|
||||
ASSERT_EQ (contents.get<std::string> ("reason"), "exited");
|
||||
ASSERT_EQ (contents.get<std::string> ("id"), "123abc");
|
||||
ASSERT_EQ (contents.get<std::string> ("mode"), "legacy");
|
||||
ASSERT_EQ (contents.get<unsigned> ("total_blocks"), 0U);
|
||||
ASSERT_LT (contents.get<unsigned> ("duration"), 15000U);
|
||||
}
|
||||
|
||||
// Tests sending keepalive
|
||||
TEST (websocket, ws_keepalive)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -26,24 +26,8 @@ add_library(
|
|||
bootstrap_weights_live.hpp
|
||||
bootstrap/block_deserializer.hpp
|
||||
bootstrap/block_deserializer.cpp
|
||||
bootstrap/bootstrap_attempt.hpp
|
||||
bootstrap/bootstrap_attempt.cpp
|
||||
bootstrap/bootstrap_bulk_pull.hpp
|
||||
bootstrap/bootstrap_bulk_pull.cpp
|
||||
bootstrap/bootstrap_bulk_push.hpp
|
||||
bootstrap/bootstrap_bulk_push.cpp
|
||||
bootstrap/bootstrap_config.hpp
|
||||
bootstrap/bootstrap_config.cpp
|
||||
bootstrap/bootstrap_connections.hpp
|
||||
bootstrap/bootstrap_connections.cpp
|
||||
bootstrap/bootstrap_frontier.hpp
|
||||
bootstrap/bootstrap_frontier.cpp
|
||||
bootstrap/bootstrap_lazy.hpp
|
||||
bootstrap/bootstrap_lazy.cpp
|
||||
bootstrap/bootstrap_legacy.hpp
|
||||
bootstrap/bootstrap_legacy.cpp
|
||||
bootstrap/bootstrap.hpp
|
||||
bootstrap/bootstrap.cpp
|
||||
bootstrap/bootstrap_server.hpp
|
||||
bootstrap/bootstrap_server.cpp
|
||||
bootstrap_ascending/account_sets.hpp
|
||||
|
|
|
|||
|
|
@ -1,390 +0,0 @@
|
|||
#include <nano/node/bootstrap/bootstrap.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_lazy.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_legacy.hpp>
|
||||
#include <nano/node/common.hpp>
|
||||
#include <nano/node/node.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
nano::bootstrap_initiator::bootstrap_initiator (nano::node & node_a) :
|
||||
node (node_a)
|
||||
{
|
||||
connections = std::make_shared<nano::bootstrap_connections> (node);
|
||||
bootstrap_initiator_threads.push_back (boost::thread ([this] () {
|
||||
nano::thread_role::set (nano::thread_role::name::bootstrap_connections);
|
||||
connections->run ();
|
||||
}));
|
||||
for (std::size_t i = 0; i < node.config.bootstrap_initiator_threads; ++i)
|
||||
{
|
||||
bootstrap_initiator_threads.push_back (boost::thread ([this] () {
|
||||
nano::thread_role::set (nano::thread_role::name::bootstrap_initiator);
|
||||
run_bootstrap ();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
nano::bootstrap_initiator::~bootstrap_initiator ()
|
||||
{
|
||||
stop ();
|
||||
}
|
||||
|
||||
void nano::bootstrap_initiator::bootstrap (bool force, std::string id_a, uint32_t const frontiers_age_a, nano::account const & start_account_a)
|
||||
{
|
||||
if (force)
|
||||
{
|
||||
stop_attempts ();
|
||||
}
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
if (!stopped && find_attempt (nano::bootstrap_mode::legacy) == nullptr)
|
||||
{
|
||||
node.stats.inc (nano::stat::type::bootstrap, frontiers_age_a == std::numeric_limits<uint32_t>::max () ? nano::stat::detail::initiate : nano::stat::detail::initiate_legacy_age, nano::stat::dir::out);
|
||||
auto legacy_attempt (std::make_shared<nano::bootstrap_attempt_legacy> (node.shared (), attempts.incremental++, id_a, frontiers_age_a, start_account_a));
|
||||
attempts_list.push_back (legacy_attempt);
|
||||
attempts.add (legacy_attempt);
|
||||
lock.unlock ();
|
||||
condition.notify_all ();
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bootstrap_initiator::bootstrap (nano::endpoint const & endpoint_a, bool add_to_peers, std::string id_a)
|
||||
{
|
||||
if (add_to_peers)
|
||||
{
|
||||
if (!node.flags.disable_tcp_realtime)
|
||||
{
|
||||
node.network.merge_peer (nano::transport::map_endpoint_to_v6 (endpoint_a));
|
||||
}
|
||||
}
|
||||
if (!stopped)
|
||||
{
|
||||
stop_attempts ();
|
||||
node.stats.inc (nano::stat::type::bootstrap, nano::stat::detail::initiate, nano::stat::dir::out);
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
auto legacy_attempt (std::make_shared<nano::bootstrap_attempt_legacy> (node.shared (), attempts.incremental++, id_a, std::numeric_limits<uint32_t>::max (), 0));
|
||||
attempts_list.push_back (legacy_attempt);
|
||||
attempts.add (legacy_attempt);
|
||||
if (!node.network.excluded_peers.check (nano::transport::map_endpoint_to_tcp (endpoint_a)))
|
||||
{
|
||||
connections->add_connection (endpoint_a);
|
||||
}
|
||||
}
|
||||
condition.notify_all ();
|
||||
}
|
||||
|
||||
bool nano::bootstrap_initiator::bootstrap_lazy (nano::hash_or_account const & hash_or_account_a, bool force, std::string id_a)
|
||||
{
|
||||
bool key_inserted (false);
|
||||
auto lazy_attempt (current_lazy_attempt ());
|
||||
if (lazy_attempt == nullptr || force)
|
||||
{
|
||||
if (force)
|
||||
{
|
||||
stop_attempts ();
|
||||
}
|
||||
node.stats.inc (nano::stat::type::bootstrap, nano::stat::detail::initiate_lazy, nano::stat::dir::out);
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
if (!stopped && find_attempt (nano::bootstrap_mode::lazy) == nullptr)
|
||||
{
|
||||
lazy_attempt = std::make_shared<nano::bootstrap_attempt_lazy> (node.shared (), attempts.incremental++, id_a.empty () ? hash_or_account_a.to_string () : id_a);
|
||||
attempts_list.push_back (lazy_attempt);
|
||||
attempts.add (lazy_attempt);
|
||||
key_inserted = lazy_attempt->lazy_start (hash_or_account_a);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
key_inserted = lazy_attempt->lazy_start (hash_or_account_a);
|
||||
}
|
||||
condition.notify_all ();
|
||||
return key_inserted;
|
||||
}
|
||||
|
||||
void nano::bootstrap_initiator::bootstrap_wallet (std::deque<nano::account> & accounts_a)
|
||||
{
|
||||
debug_assert (!accounts_a.empty ());
|
||||
auto wallet_attempt (current_wallet_attempt ());
|
||||
node.stats.inc (nano::stat::type::bootstrap, nano::stat::detail::initiate_wallet_lazy, nano::stat::dir::out);
|
||||
if (wallet_attempt == nullptr)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
std::string id (!accounts_a.empty () ? accounts_a[0].to_account () : "");
|
||||
wallet_attempt = std::make_shared<nano::bootstrap_attempt_wallet> (node.shared (), attempts.incremental++, id);
|
||||
attempts_list.push_back (wallet_attempt);
|
||||
attempts.add (wallet_attempt);
|
||||
wallet_attempt->wallet_start (accounts_a);
|
||||
}
|
||||
else
|
||||
{
|
||||
wallet_attempt->wallet_start (accounts_a);
|
||||
}
|
||||
condition.notify_all ();
|
||||
}
|
||||
|
||||
void nano::bootstrap_initiator::run_bootstrap ()
|
||||
{
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
while (!stopped)
|
||||
{
|
||||
if (has_new_attempts ())
|
||||
{
|
||||
auto attempt (new_attempt ());
|
||||
lock.unlock ();
|
||||
if (attempt != nullptr)
|
||||
{
|
||||
attempt->run ();
|
||||
remove_attempt (attempt);
|
||||
}
|
||||
lock.lock ();
|
||||
}
|
||||
else
|
||||
{
|
||||
condition.wait (lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bootstrap_initiator::lazy_requeue (nano::block_hash const & hash_a, nano::block_hash const & previous_a)
|
||||
{
|
||||
auto lazy_attempt (current_lazy_attempt ());
|
||||
if (lazy_attempt != nullptr)
|
||||
{
|
||||
lazy_attempt->lazy_requeue (hash_a, previous_a);
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bootstrap_initiator::add_observer (std::function<void (bool)> const & observer_a)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ observers_mutex };
|
||||
observers.push_back (observer_a);
|
||||
}
|
||||
|
||||
bool nano::bootstrap_initiator::in_progress ()
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
return !attempts_list.empty ();
|
||||
}
|
||||
|
||||
void nano::bootstrap_initiator::block_processed (store::transaction const & tx, nano::block_status const & result, nano::block const & block)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
for (auto & i : attempts_list)
|
||||
{
|
||||
i->block_processed (tx, result, block);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<nano::bootstrap_attempt> nano::bootstrap_initiator::find_attempt (nano::bootstrap_mode mode_a)
|
||||
{
|
||||
for (auto & i : attempts_list)
|
||||
{
|
||||
if (i->mode == mode_a)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void nano::bootstrap_initiator::remove_attempt (std::shared_ptr<nano::bootstrap_attempt> attempt_a)
|
||||
{
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
auto attempt (std::find (attempts_list.begin (), attempts_list.end (), attempt_a));
|
||||
if (attempt != attempts_list.end ())
|
||||
{
|
||||
auto attempt_ptr (*attempt);
|
||||
attempts.remove (attempt_ptr->incremental_id);
|
||||
attempts_list.erase (attempt);
|
||||
debug_assert (attempts.size () == attempts_list.size ());
|
||||
lock.unlock ();
|
||||
attempt_ptr->stop ();
|
||||
}
|
||||
else
|
||||
{
|
||||
lock.unlock ();
|
||||
}
|
||||
condition.notify_all ();
|
||||
}
|
||||
|
||||
std::shared_ptr<nano::bootstrap_attempt> nano::bootstrap_initiator::new_attempt ()
|
||||
{
|
||||
for (auto & i : attempts_list)
|
||||
{
|
||||
if (!i->started.exchange (true))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool nano::bootstrap_initiator::has_new_attempts ()
|
||||
{
|
||||
for (auto & i : attempts_list)
|
||||
{
|
||||
if (!i->started)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<nano::bootstrap_attempt> nano::bootstrap_initiator::current_attempt ()
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
return find_attempt (nano::bootstrap_mode::legacy);
|
||||
}
|
||||
|
||||
std::shared_ptr<nano::bootstrap_attempt_lazy> nano::bootstrap_initiator::current_lazy_attempt ()
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
return std::dynamic_pointer_cast<nano::bootstrap_attempt_lazy> (find_attempt (nano::bootstrap_mode::lazy));
|
||||
}
|
||||
|
||||
std::shared_ptr<nano::bootstrap_attempt_wallet> nano::bootstrap_initiator::current_wallet_attempt ()
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
return std::dynamic_pointer_cast<nano::bootstrap_attempt_wallet> (find_attempt (nano::bootstrap_mode::wallet_lazy));
|
||||
}
|
||||
|
||||
void nano::bootstrap_initiator::stop_attempts ()
|
||||
{
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
std::vector<std::shared_ptr<nano::bootstrap_attempt>> copy_attempts;
|
||||
copy_attempts.swap (attempts_list);
|
||||
attempts.clear ();
|
||||
lock.unlock ();
|
||||
for (auto & i : copy_attempts)
|
||||
{
|
||||
i->stop ();
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bootstrap_initiator::stop ()
|
||||
{
|
||||
if (!stopped.exchange (true))
|
||||
{
|
||||
stop_attempts ();
|
||||
connections->stop ();
|
||||
condition.notify_all ();
|
||||
|
||||
for (auto & thread : bootstrap_initiator_threads)
|
||||
{
|
||||
if (thread.joinable ())
|
||||
{
|
||||
thread.join ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bootstrap_initiator::notify_listeners (bool in_progress_a)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ observers_mutex };
|
||||
for (auto & i : observers)
|
||||
{
|
||||
i (in_progress_a);
|
||||
}
|
||||
}
|
||||
|
||||
nano::container_info nano::bootstrap_initiator::container_info () const
|
||||
{
|
||||
nano::container_info info;
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ observers_mutex };
|
||||
info.put ("observers", observers.size ());
|
||||
}
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ cache.pulls_cache_mutex };
|
||||
info.put ("pulls_cache", cache.cache.size ());
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
void nano::pulls_cache::add (nano::pull_info const & pull_a)
|
||||
{
|
||||
if (pull_a.processed > 500)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ pulls_cache_mutex };
|
||||
// Clean old pull
|
||||
if (cache.size () > cache_size_max)
|
||||
{
|
||||
cache.erase (cache.begin ());
|
||||
}
|
||||
debug_assert (cache.size () <= cache_size_max);
|
||||
nano::uint512_union head_512 (pull_a.account_or_head.as_union (), pull_a.head_original);
|
||||
auto existing (cache.get<account_head_tag> ().find (head_512));
|
||||
if (existing == cache.get<account_head_tag> ().end ())
|
||||
{
|
||||
// Insert new pull
|
||||
auto inserted (cache.emplace (nano::cached_pulls{ std::chrono::steady_clock::now (), head_512, pull_a.head }));
|
||||
(void)inserted;
|
||||
debug_assert (inserted.second);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update existing pull
|
||||
cache.get<account_head_tag> ().modify (existing, [pull_a] (nano::cached_pulls & cache_a) {
|
||||
cache_a.time = std::chrono::steady_clock::now ();
|
||||
cache_a.new_head = pull_a.head;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nano::pulls_cache::update_pull (nano::pull_info & pull_a)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ pulls_cache_mutex };
|
||||
nano::uint512_union head_512 (pull_a.account_or_head.as_union (), pull_a.head_original);
|
||||
auto existing (cache.get<account_head_tag> ().find (head_512));
|
||||
if (existing != cache.get<account_head_tag> ().end ())
|
||||
{
|
||||
pull_a.head = existing->new_head;
|
||||
}
|
||||
}
|
||||
|
||||
void nano::pulls_cache::remove (nano::pull_info const & pull_a)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ pulls_cache_mutex };
|
||||
nano::uint512_union head_512 (pull_a.account_or_head.as_union (), pull_a.head_original);
|
||||
cache.get<account_head_tag> ().erase (head_512);
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempts::add (std::shared_ptr<nano::bootstrap_attempt> attempt_a)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ bootstrap_attempts_mutex };
|
||||
attempts.emplace (attempt_a->incremental_id, attempt_a);
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempts::remove (uint64_t incremental_id_a)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ bootstrap_attempts_mutex };
|
||||
attempts.erase (incremental_id_a);
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempts::clear ()
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ bootstrap_attempts_mutex };
|
||||
attempts.clear ();
|
||||
}
|
||||
|
||||
std::shared_ptr<nano::bootstrap_attempt> nano::bootstrap_attempts::find (uint64_t incremental_id_a)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ bootstrap_attempts_mutex };
|
||||
auto find_attempt (attempts.find (incremental_id_a));
|
||||
if (find_attempt != attempts.end ())
|
||||
{
|
||||
return find_attempt->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t nano::bootstrap_attempts::size ()
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ bootstrap_attempts_mutex };
|
||||
return attempts.size ();
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/node/bootstrap/bootstrap_connections.hpp>
|
||||
#include <nano/node/common.hpp>
|
||||
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/member.hpp>
|
||||
#include <boost/multi_index/ordered_index.hpp>
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/thread/thread.hpp>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace mi = boost::multi_index;
|
||||
|
||||
namespace nano::store
|
||||
{
|
||||
class transaction;
|
||||
}
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class node;
|
||||
|
||||
class bootstrap_connections;
|
||||
namespace transport
|
||||
{
|
||||
class tcp_channel;
|
||||
}
|
||||
enum class bootstrap_mode
|
||||
{
|
||||
legacy,
|
||||
lazy,
|
||||
wallet_lazy,
|
||||
ascending
|
||||
};
|
||||
enum class sync_result
|
||||
{
|
||||
success,
|
||||
error,
|
||||
fork
|
||||
};
|
||||
class cached_pulls final
|
||||
{
|
||||
public:
|
||||
std::chrono::steady_clock::time_point time;
|
||||
nano::uint512_union account_head;
|
||||
nano::block_hash new_head;
|
||||
};
|
||||
class pulls_cache final
|
||||
{
|
||||
public:
|
||||
void add (nano::pull_info const &);
|
||||
void update_pull (nano::pull_info &);
|
||||
void remove (nano::pull_info const &);
|
||||
mutable nano::mutex pulls_cache_mutex;
|
||||
class account_head_tag
|
||||
{
|
||||
};
|
||||
// clang-format off
|
||||
boost::multi_index_container<nano::cached_pulls,
|
||||
mi::indexed_by<
|
||||
mi::ordered_non_unique<
|
||||
mi::member<nano::cached_pulls, std::chrono::steady_clock::time_point, &nano::cached_pulls::time>>,
|
||||
mi::hashed_unique<mi::tag<account_head_tag>,
|
||||
mi::member<nano::cached_pulls, nano::uint512_union, &nano::cached_pulls::account_head>>>>
|
||||
cache;
|
||||
// clang-format on
|
||||
constexpr static std::size_t cache_size_max = 10000;
|
||||
};
|
||||
|
||||
/**
|
||||
* Container for bootstrap sessions that are active. Owned by bootstrap_initiator.
|
||||
*/
|
||||
class bootstrap_attempts final
|
||||
{
|
||||
public:
|
||||
void add (std::shared_ptr<nano::bootstrap_attempt>);
|
||||
void remove (uint64_t);
|
||||
void clear ();
|
||||
std::shared_ptr<nano::bootstrap_attempt> find (uint64_t);
|
||||
std::size_t size ();
|
||||
std::atomic<uint64_t> incremental{ 0 };
|
||||
nano::mutex bootstrap_attempts_mutex;
|
||||
std::map<uint64_t, std::shared_ptr<nano::bootstrap_attempt>> attempts;
|
||||
};
|
||||
|
||||
class bootstrap_attempt_lazy;
|
||||
class bootstrap_attempt_wallet;
|
||||
/**
|
||||
* Client side portion to initiate bootstrap sessions. Prevents multiple legacy-type bootstrap sessions from being started at the same time. Does permit
|
||||
* lazy/wallet bootstrap sessions to overlap with legacy sessions.
|
||||
*/
|
||||
class bootstrap_initiator final
|
||||
{
|
||||
public:
|
||||
explicit bootstrap_initiator (nano::node &);
|
||||
~bootstrap_initiator ();
|
||||
void bootstrap (nano::endpoint const &, bool add_to_peers = true, std::string id_a = "");
|
||||
void bootstrap (bool force = false, std::string id_a = "", uint32_t const frontiers_age_a = std::numeric_limits<uint32_t>::max (), nano::account const & start_account_a = nano::account{});
|
||||
bool bootstrap_lazy (nano::hash_or_account const &, bool force = false, std::string id_a = "");
|
||||
void bootstrap_wallet (std::deque<nano::account> &);
|
||||
void run_bootstrap ();
|
||||
void lazy_requeue (nano::block_hash const &, nano::block_hash const &);
|
||||
void notify_listeners (bool);
|
||||
void add_observer (std::function<void (bool)> const &);
|
||||
bool in_progress ();
|
||||
void block_processed (store::transaction const & tx, nano::block_status const & result, nano::block const & block);
|
||||
std::shared_ptr<nano::bootstrap_connections> connections;
|
||||
std::shared_ptr<nano::bootstrap_attempt> new_attempt ();
|
||||
bool has_new_attempts ();
|
||||
void remove_attempt (std::shared_ptr<nano::bootstrap_attempt>);
|
||||
std::shared_ptr<nano::bootstrap_attempt> current_attempt ();
|
||||
std::shared_ptr<nano::bootstrap_attempt_lazy> current_lazy_attempt ();
|
||||
std::shared_ptr<nano::bootstrap_attempt_wallet> current_wallet_attempt ();
|
||||
nano::pulls_cache cache;
|
||||
nano::bootstrap_attempts attempts;
|
||||
void stop ();
|
||||
nano::container_info container_info () const;
|
||||
|
||||
private:
|
||||
nano::node & node;
|
||||
std::shared_ptr<nano::bootstrap_attempt> find_attempt (nano::bootstrap_mode);
|
||||
void stop_attempts ();
|
||||
std::vector<std::shared_ptr<nano::bootstrap_attempt>> attempts_list;
|
||||
std::atomic<bool> stopped{ false };
|
||||
mutable nano::mutex mutex;
|
||||
nano::condition_variable condition;
|
||||
mutable nano::mutex observers_mutex;
|
||||
std::vector<std::function<void (bool)>> observers;
|
||||
std::vector<boost::thread> bootstrap_initiator_threads;
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines the numeric values for the bootstrap feature.
|
||||
*/
|
||||
class bootstrap_limits final
|
||||
{
|
||||
public:
|
||||
static constexpr double bootstrap_connection_scale_target_blocks = 10000.0;
|
||||
static constexpr double bootstrap_connection_warmup_time_sec = 5.0;
|
||||
static constexpr double bootstrap_minimum_blocks_per_sec = 10.0;
|
||||
static constexpr double bootstrap_minimum_elapsed_seconds_blockrate = 0.02;
|
||||
static constexpr double bootstrap_minimum_frontier_blocks_per_sec = 1000.0;
|
||||
static constexpr double bootstrap_minimum_termination_time_sec = 30.0;
|
||||
static constexpr unsigned bootstrap_max_new_connections = 32;
|
||||
static constexpr unsigned requeued_pulls_limit = 256;
|
||||
static constexpr unsigned requeued_pulls_limit_dev = 1;
|
||||
static constexpr unsigned requeued_pulls_processed_blocks_factor = 4096;
|
||||
static constexpr uint64_t pull_count_per_check = 8 * 1024;
|
||||
static constexpr unsigned bulk_push_cost_limit = 200;
|
||||
static constexpr std::chrono::seconds lazy_flush_delay_sec = std::chrono::seconds (5);
|
||||
static constexpr uint64_t lazy_batch_pull_count_resize_blocks_limit = 4 * 1024 * 1024;
|
||||
static constexpr double lazy_batch_pull_count_resize_ratio = 2.0;
|
||||
static constexpr std::size_t lazy_blocks_restart_limit = 1024 * 1024;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_attempt.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_bulk_push.hpp>
|
||||
#include <nano/node/node.hpp>
|
||||
#include <nano/node/websocket.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
constexpr unsigned nano::bootstrap_limits::requeued_pulls_limit;
|
||||
constexpr unsigned nano::bootstrap_limits::requeued_pulls_limit_dev;
|
||||
|
||||
nano::bootstrap_attempt::bootstrap_attempt (std::shared_ptr<nano::node> const & node_a, nano::bootstrap_mode mode_a, uint64_t incremental_id_a, std::string id_a) :
|
||||
node (node_a),
|
||||
incremental_id (incremental_id_a),
|
||||
id (id_a),
|
||||
mode (mode_a)
|
||||
{
|
||||
if (id.empty ())
|
||||
{
|
||||
id = nano::hardened_constants::get ().random_128.to_string ();
|
||||
}
|
||||
|
||||
node_a->logger.debug (nano::log::type::bootstrap, "Starting bootstrap attempt with ID: {} (mode: {})", mode_text (), id);
|
||||
|
||||
node_a->bootstrap_initiator.notify_listeners (true);
|
||||
if (node_a->websocket.server)
|
||||
{
|
||||
nano::websocket::message_builder builder;
|
||||
node_a->websocket.server->broadcast (builder.bootstrap_started (id, mode_text ()));
|
||||
}
|
||||
}
|
||||
|
||||
nano::bootstrap_attempt::~bootstrap_attempt ()
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
node->logger.debug (nano::log::type::bootstrap, "Exiting bootstrap attempt with ID: {} (mode: {})", mode_text (), id);
|
||||
|
||||
node->bootstrap_initiator.notify_listeners (false);
|
||||
if (node->websocket.server)
|
||||
{
|
||||
nano::websocket::message_builder builder;
|
||||
node->websocket.server->broadcast (builder.bootstrap_exited (id, mode_text (), attempt_start, total_blocks));
|
||||
}
|
||||
}
|
||||
|
||||
bool nano::bootstrap_attempt::should_log ()
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ next_log_mutex };
|
||||
auto result (false);
|
||||
auto now (std::chrono::steady_clock::now ());
|
||||
if (next_log < now)
|
||||
{
|
||||
result = true;
|
||||
next_log = now + std::chrono::seconds (15);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool nano::bootstrap_attempt::still_pulling ()
|
||||
{
|
||||
debug_assert (!mutex.try_lock ());
|
||||
auto running (!stopped);
|
||||
auto still_pulling (pulling > 0);
|
||||
return running && still_pulling;
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt::pull_started ()
|
||||
{
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ mutex };
|
||||
++pulling;
|
||||
}
|
||||
condition.notify_all ();
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt::pull_finished ()
|
||||
{
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ mutex };
|
||||
--pulling;
|
||||
}
|
||||
condition.notify_all ();
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt::stop ()
|
||||
{
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
stopped = true;
|
||||
}
|
||||
condition.notify_all ();
|
||||
auto node_l = node.lock ();
|
||||
if (!node_l)
|
||||
{
|
||||
return;
|
||||
}
|
||||
node_l->bootstrap_initiator.connections->clear_pulls (incremental_id);
|
||||
}
|
||||
|
||||
char const * nano::bootstrap_attempt::mode_text ()
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case nano::bootstrap_mode::legacy:
|
||||
return "legacy";
|
||||
case nano::bootstrap_mode::lazy:
|
||||
return "lazy";
|
||||
case nano::bootstrap_mode::wallet_lazy:
|
||||
return "wallet_lazy";
|
||||
case nano::bootstrap_mode::ascending:
|
||||
return "ascending";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
bool nano::bootstrap_attempt::process_block (std::shared_ptr<nano::block> const & block_a, nano::account const & known_account_a, uint64_t pull_blocks_processed, nano::bulk_pull::count_t max_blocks, bool block_expected, unsigned retry_limit)
|
||||
{
|
||||
auto node_l = node.lock ();
|
||||
if (!node_l)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool stop_pull (false);
|
||||
// If block already exists in the ledger, then we can avoid next part of long account chain
|
||||
if (pull_blocks_processed % nano::bootstrap_limits::pull_count_per_check == 0 && node_l->block_or_pruned_exists (block_a->hash ()))
|
||||
{
|
||||
stop_pull = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
node_l->block_processor.add (block_a, nano::block_source::bootstrap_legacy);
|
||||
}
|
||||
return stop_pull;
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt::block_processed (store::transaction const & tx, nano::block_status const & result, nano::block const & block)
|
||||
{
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/node/bootstrap/bootstrap.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
|
||||
namespace nano::store
|
||||
{
|
||||
class transaction;
|
||||
}
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class node;
|
||||
|
||||
class frontier_req_client;
|
||||
class bulk_push_client;
|
||||
|
||||
/**
|
||||
* Polymorphic base class for bootstrap sessions.
|
||||
*/
|
||||
class bootstrap_attempt : public std::enable_shared_from_this<bootstrap_attempt>
|
||||
{
|
||||
public:
|
||||
explicit bootstrap_attempt (std::shared_ptr<nano::node> const & node_a, nano::bootstrap_mode mode_a, uint64_t incremental_id_a, std::string id_a);
|
||||
virtual ~bootstrap_attempt ();
|
||||
virtual void run () = 0;
|
||||
virtual void stop ();
|
||||
bool still_pulling ();
|
||||
void pull_started ();
|
||||
void pull_finished ();
|
||||
bool should_log ();
|
||||
char const * mode_text ();
|
||||
virtual bool process_block (std::shared_ptr<nano::block> const &, nano::account const &, uint64_t, nano::bulk_pull::count_t, bool, unsigned);
|
||||
virtual void get_information (boost::property_tree::ptree &) = 0;
|
||||
virtual void block_processed (store::transaction const & tx, nano::block_status const & result, nano::block const & block);
|
||||
nano::mutex next_log_mutex;
|
||||
std::chrono::steady_clock::time_point next_log{ std::chrono::steady_clock::now () };
|
||||
std::atomic<unsigned> pulling{ 0 };
|
||||
std::weak_ptr<nano::node> node;
|
||||
std::atomic<uint64_t> total_blocks{ 0 };
|
||||
std::atomic<unsigned> requeued_pulls{ 0 };
|
||||
std::atomic<bool> started{ false };
|
||||
std::atomic<bool> stopped{ false };
|
||||
uint64_t incremental_id{ 0 };
|
||||
std::string id;
|
||||
std::chrono::steady_clock::time_point attempt_start{ std::chrono::steady_clock::now () };
|
||||
std::atomic<bool> frontiers_received{ false };
|
||||
nano::bootstrap_mode mode;
|
||||
nano::mutex mutex;
|
||||
nano::condition_variable condition;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,912 +0,0 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/node/bootstrap/block_deserializer.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_bulk_pull.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_connections.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_lazy.hpp>
|
||||
#include <nano/node/node.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
#include <nano/secure/ledger_set_any.hpp>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
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, uint64_t bootstrap_id_a, count_t count_a, unsigned retry_limit_a) :
|
||||
account_or_head (account_or_head_a),
|
||||
head (head_a),
|
||||
head_original (head_a),
|
||||
end (end_a),
|
||||
count (count_a),
|
||||
retry_limit (retry_limit_a),
|
||||
bootstrap_id (bootstrap_id_a)
|
||||
{
|
||||
}
|
||||
|
||||
nano::bulk_pull_client::bulk_pull_client (std::shared_ptr<nano::bootstrap_client> const & connection_a, std::shared_ptr<nano::bootstrap_attempt> const & attempt_a, nano::pull_info const & pull_a) :
|
||||
connection{ connection_a },
|
||||
attempt{ attempt_a },
|
||||
pull{ pull_a },
|
||||
block_deserializer{ std::make_shared<nano::bootstrap::block_deserializer> () }
|
||||
{
|
||||
attempt->condition.notify_all ();
|
||||
}
|
||||
|
||||
nano::bulk_pull_client::~bulk_pull_client ()
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* If received end block is not expected end block
|
||||
Or if given start and end blocks are from different chains (i.e. forked node or malicious node) */
|
||||
if (expected != pull.end && !expected.is_zero ())
|
||||
{
|
||||
pull.head = expected;
|
||||
if (attempt->mode != nano::bootstrap_mode::legacy)
|
||||
{
|
||||
pull.account_or_head = expected;
|
||||
}
|
||||
pull.processed += pull_blocks - unexpected_count;
|
||||
node->bootstrap_initiator.connections->requeue_pull (pull, network_error);
|
||||
|
||||
node->logger.debug (nano::log::type::bulk_pull_client, "Bulk pull end block is not expected {} for account {} or head block {}", pull.end.to_string (), pull.account_or_head.to_account (), pull.account_or_head.to_string ());
|
||||
}
|
||||
else
|
||||
{
|
||||
node->bootstrap_initiator.cache.remove (pull);
|
||||
}
|
||||
attempt->pull_finished ();
|
||||
}
|
||||
|
||||
void nano::bulk_pull_client::request ()
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
debug_assert (!pull.head.is_zero () || pull.retry_limit <= node->network_params.bootstrap.lazy_retry_limit);
|
||||
expected = pull.head;
|
||||
nano::bulk_pull req{ node->network_params.network };
|
||||
if (pull.head == pull.head_original && pull.attempts % 4 < 3)
|
||||
{
|
||||
// Account for new pulls
|
||||
req.start = pull.account_or_head;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Head for cached pulls or accounts with public key equal to existing block hash (25% of attempts)
|
||||
req.start = pull.head;
|
||||
}
|
||||
req.end = pull.end;
|
||||
req.count = pull.count;
|
||||
req.set_count_present (pull.count != 0);
|
||||
|
||||
node->logger.trace (nano::log::type::bulk_pull_client, nano::log::detail::requesting_account_or_head,
|
||||
nano::log::arg{ "account_or_head", pull.account_or_head },
|
||||
nano::log::arg{ "channel", connection->channel });
|
||||
|
||||
if (attempt->should_log ())
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_pull_client, "Accounts in pull queue: {}", attempt->pulling.load ());
|
||||
}
|
||||
|
||||
auto this_l (shared_from_this ());
|
||||
connection->channel->send (
|
||||
req, [this_l] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
auto node = this_l->connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!ec)
|
||||
{
|
||||
this_l->throttled_receive_block ();
|
||||
}
|
||||
else
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_pull_client, "Error sending bulk pull request to: {} ({})", this_l->connection->channel->to_string (), ec.message ());
|
||||
node->stats.inc (nano::stat::type::bootstrap, nano::stat::detail::bulk_pull_request_failure, nano::stat::dir::in);
|
||||
}
|
||||
},
|
||||
nano::transport::buffer_drop_policy::no_limiter_drop);
|
||||
}
|
||||
|
||||
void nano::bulk_pull_client::throttled_receive_block ()
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
debug_assert (!network_error);
|
||||
if (node->block_processor.size (nano::block_source::bootstrap_legacy) < 1024 && !node->block_processor.flushing)
|
||||
{
|
||||
receive_block ();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto this_l (shared_from_this ());
|
||||
node->workers.post_delayed (std::chrono::seconds (1), [this_l] () {
|
||||
if (!this_l->connection->pending_stop && !this_l->attempt->stopped)
|
||||
{
|
||||
this_l->throttled_receive_block ();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bulk_pull_client::receive_block ()
|
||||
{
|
||||
block_deserializer->read (*connection->socket, [this_l = shared_from_this ()] (boost::system::error_code ec, std::shared_ptr<nano::block> block) {
|
||||
this_l->received_block (ec, block);
|
||||
});
|
||||
}
|
||||
|
||||
void nano::bulk_pull_client::received_block (boost::system::error_code ec, std::shared_ptr<nano::block> block)
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (ec)
|
||||
{
|
||||
network_error = true;
|
||||
return;
|
||||
}
|
||||
if (block == nullptr)
|
||||
{
|
||||
// Avoid re-using slow peers, or peers that sent the wrong blocks.
|
||||
if (!connection->pending_stop && (expected == pull.end || (pull.count != 0 && pull.count == pull_blocks)))
|
||||
{
|
||||
node->bootstrap_initiator.connections->pool_connection (connection);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (node->network_params.work.validate_entry (*block))
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_pull_client, "Insufficient work for bulk pull block: {}", block->hash ().to_string ());
|
||||
node->stats.inc (nano::stat::type::error, nano::stat::detail::insufficient_work);
|
||||
return;
|
||||
}
|
||||
auto hash = block->hash ();
|
||||
|
||||
node->logger.trace (nano::log::type::bulk_pull_client, nano::log::detail::pulled_block, nano::log::arg{ "block", block });
|
||||
|
||||
// Is block expected?
|
||||
bool block_expected (false);
|
||||
// Unconfirmed head is used only for lazy destinations if legacy bootstrap is not available, see nano::bootstrap_attempt::lazy_destinations_increment (...)
|
||||
bool unconfirmed_account_head = node->flags.disable_legacy_bootstrap && pull_blocks == 0 && pull.retry_limit <= node->network_params.bootstrap.lazy_retry_limit && (expected == pull.account_or_head.as_block_hash ()) && (block->account_field ().value_or (0) == pull.account_or_head.as_account ());
|
||||
if (hash == expected || unconfirmed_account_head)
|
||||
{
|
||||
expected = block->previous ();
|
||||
block_expected = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
unexpected_count++;
|
||||
}
|
||||
if (pull_blocks == 0 && block_expected)
|
||||
{
|
||||
known_account = block->account_field ().value_or (0);
|
||||
}
|
||||
if (connection->block_count++ == 0)
|
||||
{
|
||||
connection->set_start_time (std::chrono::steady_clock::now ());
|
||||
}
|
||||
attempt->total_blocks++;
|
||||
pull_blocks++;
|
||||
bool stop_pull (attempt->process_block (block, known_account, pull_blocks, pull.count, block_expected, pull.retry_limit));
|
||||
if (!stop_pull && !connection->hard_stop.load ())
|
||||
{
|
||||
/* Process block in lazy pull if not stopped
|
||||
Stop usual pull request with unexpected block & more than 16k blocks processed
|
||||
to prevent spam */
|
||||
if (attempt->mode != nano::bootstrap_mode::legacy || unexpected_count < 16384)
|
||||
{
|
||||
throttled_receive_block ();
|
||||
}
|
||||
}
|
||||
else if (!stop_pull && block_expected)
|
||||
{
|
||||
node->bootstrap_initiator.connections->pool_connection (connection);
|
||||
}
|
||||
}
|
||||
|
||||
nano::bulk_pull_account_client::bulk_pull_account_client (std::shared_ptr<nano::bootstrap_client> const & connection_a, std::shared_ptr<nano::bootstrap_attempt_wallet> const & attempt_a, nano::account const & account_a) :
|
||||
connection (connection_a),
|
||||
attempt (attempt_a),
|
||||
account (account_a),
|
||||
pull_blocks (0)
|
||||
{
|
||||
attempt->condition.notify_all ();
|
||||
}
|
||||
|
||||
nano::bulk_pull_account_client::~bulk_pull_account_client ()
|
||||
{
|
||||
attempt->pull_finished ();
|
||||
}
|
||||
|
||||
void nano::bulk_pull_account_client::request ()
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
nano::bulk_pull_account req{ node->network_params.network };
|
||||
req.account = account;
|
||||
req.minimum_amount = node->config.receive_minimum;
|
||||
req.flags = nano::bulk_pull_account_flags::pending_hash_and_amount;
|
||||
|
||||
node->logger.trace (nano::log::type::bulk_pull_account_client, nano::log::detail::requesting_pending,
|
||||
nano::log::arg{ "account", req.account.to_account () }, // TODO: Convert to lazy eval
|
||||
nano::log::arg{ "connection", connection->channel });
|
||||
|
||||
if (attempt->should_log ())
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_pull_account_client, "Accounts in pull queue: {}", attempt->wallet_size ());
|
||||
}
|
||||
|
||||
auto this_l (shared_from_this ());
|
||||
connection->channel->send (
|
||||
req, [this_l] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
auto node = this_l->connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!ec)
|
||||
{
|
||||
this_l->receive_pending ();
|
||||
}
|
||||
else
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_pull_account_client, "Error starting bulk pull request to: {} ({})", this_l->connection->channel->to_string (), ec.message ());
|
||||
node->stats.inc (nano::stat::type::bootstrap, nano::stat::detail::bulk_pull_error_starting_request, nano::stat::dir::in);
|
||||
|
||||
this_l->attempt->requeue_pending (this_l->account);
|
||||
}
|
||||
},
|
||||
nano::transport::buffer_drop_policy::no_limiter_drop);
|
||||
}
|
||||
|
||||
void nano::bulk_pull_account_client::receive_pending ()
|
||||
{
|
||||
auto this_l (shared_from_this ());
|
||||
std::size_t size_l (sizeof (nano::uint256_union) + sizeof (nano::uint128_union));
|
||||
connection->socket->async_read (connection->receive_buffer, size_l, [this_l, size_l] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
auto node = this_l->connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// An issue with asio is that sometimes, instead of reporting a bad file descriptor during disconnect,
|
||||
// we simply get a size of 0.
|
||||
if (size_a == size_l)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
nano::block_hash pending;
|
||||
nano::bufferstream frontier_stream (this_l->connection->receive_buffer->data (), sizeof (nano::uint256_union));
|
||||
auto error1 (nano::try_read (frontier_stream, pending));
|
||||
(void)error1;
|
||||
debug_assert (!error1);
|
||||
nano::amount balance;
|
||||
nano::bufferstream balance_stream (this_l->connection->receive_buffer->data () + sizeof (nano::uint256_union), sizeof (nano::uint128_union));
|
||||
auto error2 (nano::try_read (balance_stream, balance));
|
||||
(void)error2;
|
||||
debug_assert (!error2);
|
||||
if (this_l->pull_blocks == 0 || !pending.is_zero ())
|
||||
{
|
||||
if (this_l->pull_blocks == 0 || balance.number () >= node->config.receive_minimum.number ())
|
||||
{
|
||||
this_l->pull_blocks++;
|
||||
{
|
||||
if (!pending.is_zero ())
|
||||
{
|
||||
if (!node->block_or_pruned_exists (pending))
|
||||
{
|
||||
node->bootstrap_initiator.bootstrap_lazy (pending, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
this_l->receive_pending ();
|
||||
}
|
||||
else
|
||||
{
|
||||
this_l->attempt->requeue_pending (this_l->account);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
node->bootstrap_initiator.connections->pool_connection (this_l->connection);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_pull_account_client, "Error while receiving bulk pull account frontier: {}", ec.message ());
|
||||
|
||||
this_l->attempt->requeue_pending (this_l->account);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_pull_account_client, "Invalid size: Expected {}, got: {}", size_l, size_a);
|
||||
|
||||
this_l->attempt->requeue_pending (this_l->account);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a request for the pull of all blocks associated with an account
|
||||
* The account is supplied as the "start" member, and the final block to
|
||||
* send is the "end" member. The "start" member may also be a block
|
||||
* hash, in which case the that hash is used as the start of a chain
|
||||
* to send. To determine if "start" is interpreted as an account or
|
||||
* hash, the ledger is checked to see if the block specified exists,
|
||||
* if not then it is interpreted as an account.
|
||||
*
|
||||
* Additionally, if "start" is specified as a block hash the range
|
||||
* is inclusive of that block hash, that is the range will be:
|
||||
* [start, end); In the case that a block hash is not specified the
|
||||
* range will be exclusive of the frontier for that account with
|
||||
* a range of (frontier, end)
|
||||
*/
|
||||
void nano::bulk_pull_server::set_current_end ()
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
include_start = false;
|
||||
debug_assert (request != nullptr);
|
||||
auto transaction = node->ledger.tx_begin_read ();
|
||||
if (!node->ledger.any.block_exists (transaction, request->end))
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_pull_server, "Bulk pull end block doesn't exist: {}, sending everything", request->end.to_string ());
|
||||
|
||||
request->end.clear ();
|
||||
}
|
||||
|
||||
if (node->ledger.any.block_exists (transaction, request->start.as_block_hash ()))
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_pull_server, "Bulk pull request for block hash: {}", request->start.to_string ());
|
||||
|
||||
current = ascending () ? node->ledger.any.block_successor (transaction, request->start.as_block_hash ()).value_or (0) : request->start.as_block_hash ();
|
||||
include_start = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto info = node->ledger.any.account_get (transaction, request->start.as_account ());
|
||||
if (!info)
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_pull_server, "Request for unknown account: {}", request->start.to_account ());
|
||||
|
||||
current = request->end;
|
||||
}
|
||||
else
|
||||
{
|
||||
current = ascending () ? info->open_block : info->head;
|
||||
if (!request->end.is_zero ())
|
||||
{
|
||||
auto account (node->ledger.any.block_account (transaction, request->end));
|
||||
if (account.value_or (0) != request->start.as_account ())
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_pull_server, "Request for block that is not on account chain: {} not on {}", request->end.to_string (), request->start.to_account ());
|
||||
|
||||
current = request->end;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sent_count = 0;
|
||||
if (request->is_count_present ())
|
||||
{
|
||||
max_count = request->count;
|
||||
}
|
||||
else
|
||||
{
|
||||
max_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bulk_pull_server::send_next ()
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto block = get_next ();
|
||||
if (block != nullptr)
|
||||
{
|
||||
node->logger.trace (nano::log::type::bulk_pull_server, nano::log::detail::sending_block,
|
||||
nano::log::arg{ "block", block },
|
||||
nano::log::arg{ "socket", connection->socket });
|
||||
|
||||
std::vector<uint8_t> send_buffer;
|
||||
{
|
||||
nano::vectorstream stream (send_buffer);
|
||||
nano::serialize_block (stream, *block);
|
||||
}
|
||||
|
||||
connection->socket->async_write (nano::shared_const_buffer (std::move (send_buffer)), [this_l = shared_from_this ()] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
this_l->sent_action (ec, size_a);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
send_finished ();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<nano::block> nano::bulk_pull_server::get_next ()
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
std::shared_ptr<nano::block> result;
|
||||
bool send_current = false, set_current_to_end = false;
|
||||
|
||||
/*
|
||||
* Determine if we should reply with a block
|
||||
*
|
||||
* If our cursor is on the final block, we should signal that we
|
||||
* are done by returning a null result.
|
||||
*
|
||||
* Unless we are including the "start" member and this is the
|
||||
* start member, then include it anyway.
|
||||
*/
|
||||
if (current != request->end)
|
||||
{
|
||||
send_current = true;
|
||||
}
|
||||
else if (current == request->end && include_start == true)
|
||||
{
|
||||
send_current = true;
|
||||
|
||||
/*
|
||||
* We also need to ensure that the next time
|
||||
* are invoked that we return a null result
|
||||
*/
|
||||
set_current_to_end = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Account for how many blocks we have provided. If this
|
||||
* exceeds the requested maximum, return an empty object
|
||||
* to signal the end of results
|
||||
*/
|
||||
if (max_count != 0 && sent_count >= max_count)
|
||||
{
|
||||
send_current = false;
|
||||
}
|
||||
|
||||
if (send_current)
|
||||
{
|
||||
result = node->block (current);
|
||||
if (result != nullptr && set_current_to_end == false)
|
||||
{
|
||||
auto next = ascending () ? result->sideband ().successor : result->previous ();
|
||||
if (!next.is_zero ())
|
||||
{
|
||||
current = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
current = request->end;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
current = request->end;
|
||||
}
|
||||
|
||||
sent_count++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Once we have processed "get_next()" once our cursor is no longer on
|
||||
* the "start" member, so this flag is not relevant is always false.
|
||||
*/
|
||||
include_start = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void nano::bulk_pull_server::sent_action (boost::system::error_code const & ec, std::size_t size_a)
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!ec)
|
||||
{
|
||||
node->bootstrap_workers.post ([this_l = shared_from_this ()] () {
|
||||
this_l->send_next ();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_pull_server, "Unable to bulk send block: {}", ec.message ());
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bulk_pull_server::send_finished ()
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
nano::shared_const_buffer send_buffer (static_cast<uint8_t> (nano::block_type::not_a_block));
|
||||
auto this_l (shared_from_this ());
|
||||
|
||||
node->logger.debug (nano::log::type::bulk_pull_server, "Bulk sending finished");
|
||||
|
||||
connection->socket->async_write (send_buffer, [this_l] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
this_l->no_block_sent (ec, size_a);
|
||||
});
|
||||
}
|
||||
|
||||
void nano::bulk_pull_server::no_block_sent (boost::system::error_code const & ec, std::size_t size_a)
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!ec)
|
||||
{
|
||||
debug_assert (size_a == 1);
|
||||
connection->start ();
|
||||
}
|
||||
else
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_pull_server, "Unable to bulk send not-a-block: {}", ec.message ());
|
||||
}
|
||||
}
|
||||
|
||||
bool nano::bulk_pull_server::ascending () const
|
||||
{
|
||||
return request->header.bulk_pull_ascending ();
|
||||
}
|
||||
|
||||
nano::bulk_pull_server::bulk_pull_server (std::shared_ptr<nano::transport::tcp_server> const & connection_a, std::unique_ptr<nano::bulk_pull> request_a) :
|
||||
connection (connection_a),
|
||||
request (std::move (request_a))
|
||||
{
|
||||
set_current_end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk pull blocks related to an account
|
||||
*/
|
||||
void nano::bulk_pull_account_server::set_params ()
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
debug_assert (request != nullptr);
|
||||
|
||||
/*
|
||||
* Parse the flags
|
||||
*/
|
||||
invalid_request = false;
|
||||
pending_include_address = false;
|
||||
pending_address_only = false;
|
||||
if (request->flags == nano::bulk_pull_account_flags::pending_address_only)
|
||||
{
|
||||
pending_address_only = true;
|
||||
}
|
||||
else if (request->flags == nano::bulk_pull_account_flags::pending_hash_amount_and_address)
|
||||
{
|
||||
/**
|
||||
** This is the same as "pending_hash_and_amount" but with the
|
||||
** sending address appended, for UI purposes mainly.
|
||||
**/
|
||||
pending_include_address = true;
|
||||
}
|
||||
else if (request->flags == nano::bulk_pull_account_flags::pending_hash_and_amount)
|
||||
{
|
||||
/** The defaults are set above **/
|
||||
}
|
||||
else
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_pull_account_server, "Invalid bulk_pull_account flags supplied: {}", static_cast<uint8_t> (request->flags));
|
||||
|
||||
invalid_request = true;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the current item from the requested account
|
||||
*/
|
||||
current_key.account = request->account;
|
||||
current_key.hash = 0;
|
||||
}
|
||||
|
||||
void nano::bulk_pull_account_server::send_frontier ()
|
||||
{
|
||||
/*
|
||||
* This function is really the entry point into this class,
|
||||
* so handle the invalid_request case by terminating the
|
||||
* request without any response
|
||||
*/
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!invalid_request)
|
||||
{
|
||||
auto stream_transaction = node->ledger.tx_begin_read ();
|
||||
|
||||
// Get account balance and frontier block hash
|
||||
auto account_frontier_hash (node->ledger.any.account_head (stream_transaction, request->account));
|
||||
auto account_frontier_balance_int (node->ledger.any.account_balance (stream_transaction, request->account).value_or (0));
|
||||
nano::uint128_union account_frontier_balance (account_frontier_balance_int);
|
||||
|
||||
// Write the frontier block hash and balance into a buffer
|
||||
std::vector<uint8_t> send_buffer;
|
||||
{
|
||||
nano::vectorstream output_stream (send_buffer);
|
||||
write (output_stream, account_frontier_hash.bytes);
|
||||
write (output_stream, account_frontier_balance.bytes);
|
||||
}
|
||||
|
||||
// Send the buffer to the requestor
|
||||
auto this_l (shared_from_this ());
|
||||
connection->socket->async_write (nano::shared_const_buffer (std::move (send_buffer)), [this_l] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
this_l->sent_action (ec, size_a);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bulk_pull_account_server::send_next_block ()
|
||||
{
|
||||
/*
|
||||
* Get the next item from the queue, it is a tuple with the key (which
|
||||
* contains the account and hash) and data (which contains the amount)
|
||||
*/
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto block_data (get_next ());
|
||||
auto block_info_key (block_data.first.get ());
|
||||
auto block_info (block_data.second.get ());
|
||||
|
||||
if (block_info_key != nullptr)
|
||||
{
|
||||
/*
|
||||
* If we have a new item, emit it to the socket
|
||||
*/
|
||||
|
||||
std::vector<uint8_t> send_buffer;
|
||||
if (pending_address_only)
|
||||
{
|
||||
node->logger.trace (nano::log::type::bulk_pull_account_server, nano::log::detail::sending_pending,
|
||||
nano::log::arg{ "pending", block_info->source });
|
||||
|
||||
nano::vectorstream output_stream (send_buffer);
|
||||
write (output_stream, block_info->source.bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
node->logger.trace (nano::log::type::bulk_pull_account_server, nano::log::detail::sending_block,
|
||||
nano::log::arg{ "block", block_info_key->hash });
|
||||
|
||||
nano::vectorstream output_stream (send_buffer);
|
||||
write (output_stream, block_info_key->hash.bytes);
|
||||
write (output_stream, block_info->amount.bytes);
|
||||
|
||||
if (pending_include_address)
|
||||
{
|
||||
/**
|
||||
** Write the source address as well, if requested
|
||||
**/
|
||||
write (output_stream, block_info->source.bytes);
|
||||
}
|
||||
}
|
||||
|
||||
auto this_l (shared_from_this ());
|
||||
connection->socket->async_write (nano::shared_const_buffer (std::move (send_buffer)), [this_l] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
this_l->sent_action (ec, size_a);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Otherwise, finalize the connection
|
||||
*/
|
||||
node->logger.debug (nano::log::type::bulk_pull_account_server, "Done sending blocks");
|
||||
|
||||
send_finished ();
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<std::unique_ptr<nano::pending_key>, std::unique_ptr<nano::pending_info>> nano::bulk_pull_account_server::get_next ()
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return { nullptr, nullptr };
|
||||
}
|
||||
std::pair<std::unique_ptr<nano::pending_key>, std::unique_ptr<nano::pending_info>> result;
|
||||
|
||||
while (true)
|
||||
{
|
||||
/*
|
||||
* For each iteration of this loop, establish and then
|
||||
* destroy a database transaction, to avoid locking the
|
||||
* database for a prolonged period.
|
||||
*/
|
||||
auto tx = node->ledger.tx_begin_read ();
|
||||
auto & ledger = node->ledger;
|
||||
auto stream = ledger.any.receivable_upper_bound (tx, current_key.account, current_key.hash);
|
||||
|
||||
if (stream == ledger.any.receivable_end ())
|
||||
{
|
||||
break;
|
||||
}
|
||||
auto const & [key, info] = *stream;
|
||||
current_key = key;
|
||||
|
||||
/*
|
||||
* Skip entries where the amount is less than the requested
|
||||
* minimum
|
||||
*/
|
||||
if (info.amount < request->minimum_amount)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the pending_address_only flag is set, de-duplicate the
|
||||
* responses. The responses are the address of the sender,
|
||||
* so they are part of the pending table's information
|
||||
* and not key, so we have to de-duplicate them manually.
|
||||
*/
|
||||
if (pending_address_only)
|
||||
{
|
||||
if (!deduplication.insert (info.source).second)
|
||||
{
|
||||
/*
|
||||
* If the deduplication map gets too
|
||||
* large, clear it out. This may
|
||||
* result in some duplicates getting
|
||||
* sent to the client, but we do not
|
||||
* want to commit too much memory
|
||||
*/
|
||||
if (deduplication.size () > 4096)
|
||||
{
|
||||
deduplication.clear ();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
result.first = std::make_unique<nano::pending_key> (key);
|
||||
result.second = std::make_unique<nano::pending_info> (info);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void nano::bulk_pull_account_server::sent_action (boost::system::error_code const & ec, std::size_t size_a)
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!ec)
|
||||
{
|
||||
node->bootstrap_workers.post ([this_l = shared_from_this ()] () {
|
||||
this_l->send_next_block ();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_pull_account_server, "Unable to bulk send block: {}", ec.message ());
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bulk_pull_account_server::send_finished ()
|
||||
{
|
||||
/*
|
||||
* The "bulk_pull_account" final sequence is a final block of all
|
||||
* zeros. If we are sending only account public keys (with the
|
||||
* "pending_address_only" flag) then it will be 256-bits of zeros,
|
||||
* otherwise it will be either 384-bits of zeros (if the
|
||||
* "pending_include_address" flag is not set) or 640-bits of zeros
|
||||
* (if that flag is set).
|
||||
*/
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::vector<uint8_t> send_buffer;
|
||||
{
|
||||
nano::vectorstream output_stream (send_buffer);
|
||||
nano::uint256_union account_zero (0);
|
||||
nano::uint128_union balance_zero (0);
|
||||
|
||||
write (output_stream, account_zero.bytes);
|
||||
|
||||
if (!pending_address_only)
|
||||
{
|
||||
write (output_stream, balance_zero.bytes);
|
||||
if (pending_include_address)
|
||||
{
|
||||
write (output_stream, account_zero.bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node->logger.debug (nano::log::type::bulk_pull_account_server, "Bulk sending for an account finished");
|
||||
|
||||
auto this_l (shared_from_this ());
|
||||
connection->socket->async_write (nano::shared_const_buffer (std::move (send_buffer)), [this_l] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
this_l->complete (ec, size_a);
|
||||
});
|
||||
}
|
||||
|
||||
void nano::bulk_pull_account_server::complete (boost::system::error_code const & ec, std::size_t size_a)
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!ec)
|
||||
{
|
||||
if (pending_address_only)
|
||||
{
|
||||
debug_assert (size_a == 32);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pending_include_address)
|
||||
{
|
||||
debug_assert (size_a == 80);
|
||||
}
|
||||
else
|
||||
{
|
||||
debug_assert (size_a == 48);
|
||||
}
|
||||
}
|
||||
|
||||
connection->start ();
|
||||
}
|
||||
else
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_pull_account_server, "Unable to pending-as-zero: {}", ec.message ());
|
||||
}
|
||||
}
|
||||
|
||||
nano::bulk_pull_account_server::bulk_pull_account_server (std::shared_ptr<nano::transport::tcp_server> const & connection_a, std::unique_ptr<nano::bulk_pull_account> request_a) :
|
||||
connection (connection_a),
|
||||
request (std::move (request_a)),
|
||||
current_key (0, 0)
|
||||
{
|
||||
/*
|
||||
* Setup the streaming response for the first call to "send_frontier" and "send_next_block"
|
||||
*/
|
||||
set_params ();
|
||||
}
|
||||
|
|
@ -1,140 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/node/messages.hpp>
|
||||
#include <nano/node/transport/tcp_socket.hpp>
|
||||
#include <nano/secure/pending_info.hpp>
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class bootstrap_attempt;
|
||||
namespace transport
|
||||
{
|
||||
class tcp_server;
|
||||
}
|
||||
namespace bootstrap
|
||||
{
|
||||
class block_deserializer;
|
||||
};
|
||||
|
||||
class pull_info
|
||||
{
|
||||
public:
|
||||
using count_t = nano::bulk_pull::count_t;
|
||||
pull_info () = default;
|
||||
pull_info (nano::hash_or_account const &, nano::block_hash const &, nano::block_hash const &, uint64_t, count_t = 0, unsigned = 16);
|
||||
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 };
|
||||
unsigned retry_limit{ 0 };
|
||||
uint64_t bootstrap_id{ 0 };
|
||||
};
|
||||
class bootstrap_client;
|
||||
|
||||
/**
|
||||
* Client side of a bulk_pull request. Created when the bootstrap_attempt wants to make a bulk_pull request to the remote side.
|
||||
*/
|
||||
class bulk_pull_client final : public std::enable_shared_from_this<nano::bulk_pull_client>
|
||||
{
|
||||
public:
|
||||
bulk_pull_client (std::shared_ptr<nano::bootstrap_client> const &, std::shared_ptr<nano::bootstrap_attempt> const &, nano::pull_info const &);
|
||||
~bulk_pull_client ();
|
||||
void request ();
|
||||
void receive_block ();
|
||||
void throttled_receive_block ();
|
||||
void received_block (boost::system::error_code ec, std::shared_ptr<nano::block> block);
|
||||
nano::block_hash first ();
|
||||
std::shared_ptr<nano::bootstrap_client> connection;
|
||||
std::shared_ptr<nano::bootstrap_attempt> attempt;
|
||||
bool network_error{ false };
|
||||
|
||||
private:
|
||||
/**
|
||||
* Tracks the next block expected to be received starting with the block hash that was expected and followed by previous blocks for this account chain
|
||||
*/
|
||||
nano::block_hash expected{ 0 };
|
||||
/**
|
||||
* Tracks the account number for this account chain
|
||||
* Used when an account chain has a mix between state blocks and legacy blocks which do not encode the account number in the block
|
||||
* 0 if the account is unknown
|
||||
*/
|
||||
nano::account known_account{ 0 };
|
||||
/**
|
||||
* Original pull request
|
||||
*/
|
||||
nano::pull_info pull;
|
||||
/**
|
||||
* Tracks the number of blocks successfully deserialized
|
||||
*/
|
||||
uint64_t pull_blocks{ 0 };
|
||||
/**
|
||||
* Tracks the number of times an unexpected block was received
|
||||
*/
|
||||
uint64_t unexpected_count{ 0 };
|
||||
std::shared_ptr<nano::bootstrap::block_deserializer> block_deserializer;
|
||||
};
|
||||
class bootstrap_attempt_wallet;
|
||||
class bulk_pull_account_client final : public std::enable_shared_from_this<nano::bulk_pull_account_client>
|
||||
{
|
||||
public:
|
||||
bulk_pull_account_client (std::shared_ptr<nano::bootstrap_client> const &, std::shared_ptr<nano::bootstrap_attempt_wallet> const &, nano::account const &);
|
||||
~bulk_pull_account_client ();
|
||||
void request ();
|
||||
void receive_pending ();
|
||||
std::shared_ptr<nano::bootstrap_client> connection;
|
||||
std::shared_ptr<nano::bootstrap_attempt_wallet> attempt;
|
||||
nano::account account;
|
||||
uint64_t pull_blocks;
|
||||
};
|
||||
|
||||
class bulk_pull;
|
||||
|
||||
/**
|
||||
* Server side of a bulk_pull request. Created when tcp_server receives a bulk_pull message and is exited after the contents
|
||||
* have been sent. If the 'start' in the bulk_pull message is an account, send blocks for that account down to 'end'. If the 'start'
|
||||
* is a block hash, send blocks for that chain down to 'end'. If end doesn't exist, send all accounts in the chain.
|
||||
*/
|
||||
class bulk_pull_server final : public std::enable_shared_from_this<nano::bulk_pull_server>
|
||||
{
|
||||
public:
|
||||
bulk_pull_server (std::shared_ptr<nano::transport::tcp_server> const &, std::unique_ptr<nano::bulk_pull>);
|
||||
void set_current_end ();
|
||||
std::shared_ptr<nano::block> get_next ();
|
||||
void send_next ();
|
||||
void sent_action (boost::system::error_code const &, std::size_t);
|
||||
void send_finished ();
|
||||
void no_block_sent (boost::system::error_code const &, std::size_t);
|
||||
bool ascending () const;
|
||||
std::shared_ptr<nano::transport::tcp_server> connection;
|
||||
std::unique_ptr<nano::bulk_pull> request;
|
||||
nano::block_hash current;
|
||||
bool include_start;
|
||||
nano::bulk_pull::count_t max_count;
|
||||
nano::bulk_pull::count_t sent_count;
|
||||
};
|
||||
class bulk_pull_account;
|
||||
class bulk_pull_account_server final : public std::enable_shared_from_this<nano::bulk_pull_account_server>
|
||||
{
|
||||
public:
|
||||
bulk_pull_account_server (std::shared_ptr<nano::transport::tcp_server> const &, std::unique_ptr<nano::bulk_pull_account>);
|
||||
void set_params ();
|
||||
std::pair<std::unique_ptr<nano::pending_key>, std::unique_ptr<nano::pending_info>> get_next ();
|
||||
void send_frontier ();
|
||||
void send_next_block ();
|
||||
void sent_action (boost::system::error_code const &, std::size_t);
|
||||
void send_finished ();
|
||||
void complete (boost::system::error_code const &, std::size_t);
|
||||
std::shared_ptr<nano::transport::tcp_server> connection;
|
||||
std::unique_ptr<nano::bulk_pull_account> request;
|
||||
std::unordered_set<nano::uint256_union> deduplication;
|
||||
nano::pending_key current_key;
|
||||
bool pending_address_only;
|
||||
bool pending_include_address;
|
||||
bool invalid_request;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,279 +0,0 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_attempt.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_bulk_push.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_legacy.hpp>
|
||||
#include <nano/node/node.hpp>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
nano::bulk_push_client::bulk_push_client (std::shared_ptr<nano::bootstrap_client> const & connection_a, std::shared_ptr<nano::bootstrap_attempt_legacy> const & attempt_a) :
|
||||
connection (connection_a),
|
||||
attempt (attempt_a)
|
||||
{
|
||||
}
|
||||
|
||||
nano::bulk_push_client::~bulk_push_client ()
|
||||
{
|
||||
}
|
||||
|
||||
void nano::bulk_push_client::start ()
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
nano::bulk_push message{ node->network_params.network };
|
||||
auto this_l (shared_from_this ());
|
||||
connection->channel->send (
|
||||
message, [this_l] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
auto node = this_l->connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!ec)
|
||||
{
|
||||
this_l->push ();
|
||||
}
|
||||
else
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_push_client, "Unable to send bulk push request: {}", ec.message ());
|
||||
}
|
||||
},
|
||||
nano::transport::buffer_drop_policy::no_limiter_drop);
|
||||
}
|
||||
|
||||
void nano::bulk_push_client::push ()
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<nano::block> block;
|
||||
bool finished (false);
|
||||
while (block == nullptr && !finished)
|
||||
{
|
||||
if (current_target.first.is_zero () || current_target.first == current_target.second)
|
||||
{
|
||||
finished = attempt->request_bulk_push_target (current_target);
|
||||
}
|
||||
if (!finished)
|
||||
{
|
||||
block = node->block (current_target.first);
|
||||
if (block == nullptr)
|
||||
{
|
||||
current_target.first = nano::block_hash (0);
|
||||
}
|
||||
else
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_push_client, "Bulk pushing range: [{}:{}]", current_target.first.to_string (), current_target.second.to_string ());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (finished)
|
||||
{
|
||||
send_finished ();
|
||||
}
|
||||
else
|
||||
{
|
||||
current_target.first = block->previous ();
|
||||
push_block (*block);
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bulk_push_client::send_finished ()
|
||||
{
|
||||
nano::shared_const_buffer buffer (static_cast<uint8_t> (nano::block_type::not_a_block));
|
||||
auto this_l (shared_from_this ());
|
||||
connection->channel->send_buffer (buffer, [this_l] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
try
|
||||
{
|
||||
this_l->promise.set_value (false);
|
||||
}
|
||||
catch (std::future_error &)
|
||||
{
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void nano::bulk_push_client::push_block (nano::block const & block_a)
|
||||
{
|
||||
std::vector<uint8_t> buffer;
|
||||
{
|
||||
nano::vectorstream stream (buffer);
|
||||
nano::serialize_block (stream, block_a);
|
||||
}
|
||||
auto this_l (shared_from_this ());
|
||||
connection->channel->send_buffer (nano::shared_const_buffer (std::move (buffer)), [this_l] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
auto node = this_l->connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!ec)
|
||||
{
|
||||
this_l->push ();
|
||||
}
|
||||
else
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_push_client, "Error sending block during bulk push: {}", ec.message ());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
nano::bulk_push_server::bulk_push_server (std::shared_ptr<nano::transport::tcp_server> const & connection_a) :
|
||||
receive_buffer (std::make_shared<std::vector<uint8_t>> ()),
|
||||
connection (connection_a)
|
||||
{
|
||||
receive_buffer->resize (256);
|
||||
}
|
||||
|
||||
void nano::bulk_push_server::throttled_receive ()
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (node->block_processor.size (nano::block_source::bootstrap_legacy) < 1024)
|
||||
{
|
||||
receive ();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto this_l (shared_from_this ());
|
||||
node->workers.post_delayed (std::chrono::seconds (1), [this_l] () {
|
||||
if (!this_l->connection->stopped)
|
||||
{
|
||||
this_l->throttled_receive ();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bulk_push_server::receive ()
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (node->bootstrap_initiator.in_progress ())
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_push_server, "Aborting bulk push because a bootstrap attempt is in progress");
|
||||
}
|
||||
else
|
||||
{
|
||||
auto this_l (shared_from_this ());
|
||||
connection->socket->async_read (receive_buffer, 1, [this_l] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
auto node = this_l->connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!ec)
|
||||
{
|
||||
this_l->received_type ();
|
||||
}
|
||||
else
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_push_server, "Error receiving block type: {}", ec.message ());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bulk_push_server::received_type ()
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto this_l (shared_from_this ());
|
||||
nano::block_type type (static_cast<nano::block_type> (receive_buffer->data ()[0]));
|
||||
switch (type)
|
||||
{
|
||||
case nano::block_type::send:
|
||||
{
|
||||
node->stats.inc (nano::stat::type::bootstrap, nano::stat::detail::send, nano::stat::dir::in);
|
||||
connection->socket->async_read (receive_buffer, nano::send_block::size, [this_l, type] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
this_l->received_block (ec, size_a, type);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case nano::block_type::receive:
|
||||
{
|
||||
node->stats.inc (nano::stat::type::bootstrap, nano::stat::detail::receive, nano::stat::dir::in);
|
||||
connection->socket->async_read (receive_buffer, nano::receive_block::size, [this_l, type] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
this_l->received_block (ec, size_a, type);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case nano::block_type::open:
|
||||
{
|
||||
node->stats.inc (nano::stat::type::bootstrap, nano::stat::detail::open, nano::stat::dir::in);
|
||||
connection->socket->async_read (receive_buffer, nano::open_block::size, [this_l, type] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
this_l->received_block (ec, size_a, type);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case nano::block_type::change:
|
||||
{
|
||||
node->stats.inc (nano::stat::type::bootstrap, nano::stat::detail::change, nano::stat::dir::in);
|
||||
connection->socket->async_read (receive_buffer, nano::change_block::size, [this_l, type] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
this_l->received_block (ec, size_a, type);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case nano::block_type::state:
|
||||
{
|
||||
node->stats.inc (nano::stat::type::bootstrap, nano::stat::detail::state_block, nano::stat::dir::in);
|
||||
connection->socket->async_read (receive_buffer, nano::state_block::size, [this_l, type] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
this_l->received_block (ec, size_a, type);
|
||||
});
|
||||
break;
|
||||
}
|
||||
case nano::block_type::not_a_block:
|
||||
{
|
||||
connection->start ();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_push_server, "Unknown type received as block type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bulk_push_server::received_block (boost::system::error_code const & ec, std::size_t size_a, nano::block_type type_a)
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!ec)
|
||||
{
|
||||
nano::bufferstream stream (receive_buffer->data (), size_a);
|
||||
auto block (nano::deserialize_block (stream, type_a));
|
||||
if (block != nullptr)
|
||||
{
|
||||
if (node->network_params.work.validate_entry (*block))
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_push_server, "Insufficient work for bulk push block: {}", block->hash ().to_string ());
|
||||
node->stats.inc (nano::stat::type::error, nano::stat::detail::insufficient_work);
|
||||
return;
|
||||
}
|
||||
node->process_active (std::move (block));
|
||||
throttled_receive ();
|
||||
}
|
||||
else
|
||||
{
|
||||
node->logger.debug (nano::log::type::bulk_push_server, "Error deserializing block received from pull request");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/node/common.hpp>
|
||||
|
||||
#include <future>
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class bootstrap_attempt_legacy;
|
||||
class bootstrap_client;
|
||||
namespace transport
|
||||
{
|
||||
class tcp_server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Client side of a bulk_push request. Sends a sequence of blocks the other side did not report in their frontier_req response.
|
||||
*/
|
||||
class bulk_push_client final : public std::enable_shared_from_this<nano::bulk_push_client>
|
||||
{
|
||||
public:
|
||||
explicit bulk_push_client (std::shared_ptr<nano::bootstrap_client> const &, std::shared_ptr<nano::bootstrap_attempt_legacy> const &);
|
||||
~bulk_push_client ();
|
||||
void start ();
|
||||
void push ();
|
||||
void push_block (nano::block const &);
|
||||
void send_finished ();
|
||||
std::shared_ptr<nano::bootstrap_client> connection;
|
||||
std::shared_ptr<nano::bootstrap_attempt_legacy> attempt;
|
||||
std::promise<bool> promise;
|
||||
std::pair<nano::block_hash, nano::block_hash> current_target;
|
||||
};
|
||||
|
||||
/**
|
||||
* Server side of a bulk_push request. Receives blocks and puts them in the block processor to be processed.
|
||||
*/
|
||||
class bulk_push_server final : public std::enable_shared_from_this<nano::bulk_push_server>
|
||||
{
|
||||
public:
|
||||
explicit bulk_push_server (std::shared_ptr<nano::transport::tcp_server> const &);
|
||||
void throttled_receive ();
|
||||
void receive ();
|
||||
void received_type ();
|
||||
void received_block (boost::system::error_code const &, std::size_t, nano::block_type);
|
||||
std::shared_ptr<std::vector<uint8_t>> receive_buffer;
|
||||
std::shared_ptr<nano::transport::tcp_server> connection;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,489 +0,0 @@
|
|||
#include <nano/node/bootstrap/bootstrap.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_attempt.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_connections.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_lazy.hpp>
|
||||
#include <nano/node/common.hpp>
|
||||
#include <nano/node/node.hpp>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
constexpr double nano::bootstrap_limits::bootstrap_connection_scale_target_blocks;
|
||||
constexpr double nano::bootstrap_limits::bootstrap_minimum_blocks_per_sec;
|
||||
constexpr double nano::bootstrap_limits::bootstrap_minimum_termination_time_sec;
|
||||
constexpr unsigned nano::bootstrap_limits::bootstrap_max_new_connections;
|
||||
constexpr unsigned nano::bootstrap_limits::requeued_pulls_processed_blocks_factor;
|
||||
|
||||
nano::bootstrap_client::bootstrap_client (std::shared_ptr<nano::node> const & node_a, std::shared_ptr<nano::transport::tcp_channel> const & channel_a, std::shared_ptr<nano::transport::tcp_socket> const & socket_a) :
|
||||
node (node_a),
|
||||
channel (channel_a),
|
||||
socket (socket_a),
|
||||
receive_buffer (std::make_shared<std::vector<uint8_t>> ()),
|
||||
start_time_m (std::chrono::steady_clock::now ())
|
||||
{
|
||||
++node_a->bootstrap_initiator.connections->connections_count;
|
||||
receive_buffer->resize (256);
|
||||
channel->update_endpoints ();
|
||||
}
|
||||
|
||||
nano::bootstrap_client::~bootstrap_client ()
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
--node->bootstrap_initiator.connections->connections_count;
|
||||
}
|
||||
|
||||
double nano::bootstrap_client::sample_block_rate ()
|
||||
{
|
||||
auto elapsed = std::max (elapsed_seconds (), nano::bootstrap_limits::bootstrap_minimum_elapsed_seconds_blockrate);
|
||||
block_rate = static_cast<double> (block_count.load ()) / elapsed;
|
||||
return block_rate;
|
||||
}
|
||||
|
||||
void nano::bootstrap_client::set_start_time (std::chrono::steady_clock::time_point start_time_a)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ start_time_mutex };
|
||||
start_time_m = start_time_a;
|
||||
}
|
||||
|
||||
double nano::bootstrap_client::elapsed_seconds () const
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ start_time_mutex };
|
||||
return std::chrono::duration_cast<std::chrono::duration<double>> (std::chrono::steady_clock::now () - start_time_m).count ();
|
||||
}
|
||||
|
||||
void nano::bootstrap_client::stop (bool force)
|
||||
{
|
||||
pending_stop = true;
|
||||
if (force)
|
||||
{
|
||||
hard_stop = true;
|
||||
}
|
||||
}
|
||||
|
||||
nano::bootstrap_connections::bootstrap_connections (nano::node & node_a) :
|
||||
node (node_a)
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<nano::bootstrap_client> nano::bootstrap_connections::connection (std::shared_ptr<nano::bootstrap_attempt> const & attempt_a, bool use_front_connection)
|
||||
{
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
condition.wait (lock, [&stopped = stopped, &idle = idle, &new_connections_empty = new_connections_empty] { return stopped || !idle.empty () || new_connections_empty; });
|
||||
std::shared_ptr<nano::bootstrap_client> result;
|
||||
if (!stopped && !idle.empty ())
|
||||
{
|
||||
if (!use_front_connection)
|
||||
{
|
||||
result = idle.back ();
|
||||
idle.pop_back ();
|
||||
}
|
||||
else
|
||||
{
|
||||
result = idle.front ();
|
||||
idle.pop_front ();
|
||||
}
|
||||
}
|
||||
if (result == nullptr && connections_count == 0 && new_connections_empty && attempt_a != nullptr)
|
||||
{
|
||||
node.logger.debug (nano::log::type::bootstrap, "Bootstrap attempt stopped because there are no peers");
|
||||
|
||||
lock.unlock ();
|
||||
attempt_a->stop ();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void nano::bootstrap_connections::pool_connection (std::shared_ptr<nano::bootstrap_client> const & client_a, bool new_client, bool push_front)
|
||||
{
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
auto const & socket_l = client_a->socket;
|
||||
if (!stopped && !client_a->pending_stop && !node.network.excluded_peers.check (client_a->channel->get_remote_endpoint ()))
|
||||
{
|
||||
socket_l->set_timeout (node.network_params.network.idle_timeout);
|
||||
// Push into idle deque
|
||||
if (!push_front)
|
||||
{
|
||||
idle.push_back (client_a);
|
||||
}
|
||||
else
|
||||
{
|
||||
idle.push_front (client_a);
|
||||
}
|
||||
if (new_client)
|
||||
{
|
||||
clients.push_back (client_a);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
socket_l->close ();
|
||||
}
|
||||
lock.unlock ();
|
||||
condition.notify_all ();
|
||||
}
|
||||
|
||||
void nano::bootstrap_connections::add_connection (nano::endpoint const & endpoint_a)
|
||||
{
|
||||
connect_client (nano::tcp_endpoint (endpoint_a.address (), endpoint_a.port ()), true);
|
||||
}
|
||||
|
||||
std::shared_ptr<nano::bootstrap_client> nano::bootstrap_connections::find_connection (nano::tcp_endpoint const & endpoint_a)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
std::shared_ptr<nano::bootstrap_client> result;
|
||||
for (auto i (idle.begin ()), end (idle.end ()); i != end && !stopped; ++i)
|
||||
{
|
||||
if ((*i)->channel->get_remote_endpoint () == endpoint_a)
|
||||
{
|
||||
result = *i;
|
||||
idle.erase (i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void nano::bootstrap_connections::connect_client (nano::tcp_endpoint const & endpoint_a, bool push_front)
|
||||
{
|
||||
++connections_count;
|
||||
auto socket (std::make_shared<nano::transport::tcp_socket> (node));
|
||||
auto this_l (shared_from_this ());
|
||||
socket->async_connect (endpoint_a,
|
||||
[this_l, socket, endpoint_a, push_front] (boost::system::error_code const & ec) {
|
||||
if (!ec)
|
||||
{
|
||||
this_l->node.logger.debug (nano::log::type::bootstrap, "Connection established to: {}", nano::util::to_str (endpoint_a));
|
||||
|
||||
auto client (std::make_shared<nano::bootstrap_client> (this_l->node.shared (), std::make_shared<nano::transport::tcp_channel> (*this_l->node.shared (), socket), socket));
|
||||
this_l->pool_connection (client, true, push_front);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (ec.value ())
|
||||
{
|
||||
default:
|
||||
this_l->node.logger.debug (nano::log::type::bootstrap, "Error initiating bootstrap connection to: {} ({})", nano::util::to_str (endpoint_a), ec.message ());
|
||||
break;
|
||||
case boost::system::errc::connection_refused:
|
||||
case boost::system::errc::operation_canceled:
|
||||
case boost::system::errc::timed_out:
|
||||
case 995: // Windows The I/O operation has been aborted because of either a thread exit or an application request
|
||||
case 10061: // Windows No connection could be made because the target machine actively refused it
|
||||
break;
|
||||
}
|
||||
}
|
||||
--this_l->connections_count;
|
||||
});
|
||||
}
|
||||
|
||||
unsigned nano::bootstrap_connections::target_connections (std::size_t pulls_remaining, std::size_t attempts_count) const
|
||||
{
|
||||
auto const attempts_factor = nano::narrow_cast<unsigned> (node.config.bootstrap_connections * attempts_count);
|
||||
if (attempts_factor >= node.config.bootstrap_connections_max)
|
||||
{
|
||||
return std::max (1U, node.config.bootstrap_connections_max);
|
||||
}
|
||||
|
||||
// Only scale up to bootstrap_connections_max for large pulls.
|
||||
double step_scale = std::min (1.0, std::max (0.0, (double)pulls_remaining / nano::bootstrap_limits::bootstrap_connection_scale_target_blocks));
|
||||
double target = (double)attempts_factor + (double)(node.config.bootstrap_connections_max - attempts_factor) * step_scale;
|
||||
return std::max (1U, (unsigned)(target + 0.5f));
|
||||
}
|
||||
|
||||
struct block_rate_cmp
|
||||
{
|
||||
bool operator() (std::shared_ptr<nano::bootstrap_client> const & lhs, std::shared_ptr<nano::bootstrap_client> const & rhs) const
|
||||
{
|
||||
return lhs->block_rate > rhs->block_rate;
|
||||
}
|
||||
};
|
||||
|
||||
void nano::bootstrap_connections::populate_connections (bool repeat)
|
||||
{
|
||||
double rate_sum = 0.0;
|
||||
std::size_t num_pulls = 0;
|
||||
std::size_t attempts_count = node.bootstrap_initiator.attempts.size ();
|
||||
std::priority_queue<std::shared_ptr<nano::bootstrap_client>, std::vector<std::shared_ptr<nano::bootstrap_client>>, block_rate_cmp> sorted_connections;
|
||||
std::unordered_set<nano::tcp_endpoint> endpoints;
|
||||
{
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
num_pulls = pulls.size ();
|
||||
std::deque<std::weak_ptr<nano::bootstrap_client>> new_clients;
|
||||
for (auto & c : clients)
|
||||
{
|
||||
if (auto client = c.lock ())
|
||||
{
|
||||
new_clients.push_back (client);
|
||||
endpoints.insert (client->socket->remote_endpoint ());
|
||||
double elapsed_sec = client->elapsed_seconds ();
|
||||
auto blocks_per_sec = client->sample_block_rate ();
|
||||
rate_sum += blocks_per_sec;
|
||||
if (client->elapsed_seconds () > nano::bootstrap_limits::bootstrap_connection_warmup_time_sec && client->block_count > 0)
|
||||
{
|
||||
sorted_connections.push (client);
|
||||
}
|
||||
// Force-stop the slowest peers, since they can take the whole bootstrap hostage by dribbling out blocks on the last remaining pull.
|
||||
// This is ~1.5kilobits/sec.
|
||||
if (elapsed_sec > nano::bootstrap_limits::bootstrap_minimum_termination_time_sec && blocks_per_sec < nano::bootstrap_limits::bootstrap_minimum_blocks_per_sec)
|
||||
{
|
||||
node.logger.debug (nano::log::type::bootstrap, "Stopping slow peer {} (elapsed sec {} > {} and {} blocks per second < {})",
|
||||
client->channel->to_string (),
|
||||
elapsed_sec,
|
||||
nano::bootstrap_limits::bootstrap_minimum_termination_time_sec,
|
||||
blocks_per_sec,
|
||||
nano::bootstrap_limits::bootstrap_minimum_blocks_per_sec);
|
||||
|
||||
client->stop (true);
|
||||
new_clients.pop_back ();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Cleanup expired clients
|
||||
clients.swap (new_clients);
|
||||
}
|
||||
|
||||
auto target = target_connections (num_pulls, attempts_count);
|
||||
|
||||
// We only want to drop slow peers when more than 2/3 are active. 2/3 because 1/2 is too aggressive, and 100% rarely happens.
|
||||
// Probably needs more tuning.
|
||||
if (sorted_connections.size () >= (target * 2) / 3 && target >= 4)
|
||||
{
|
||||
// 4 -> 1, 8 -> 2, 16 -> 4, arbitrary, but seems to work well.
|
||||
auto drop = (int)roundf (sqrtf ((float)target - 2.0f));
|
||||
|
||||
node.logger.debug (nano::log::type::bootstrap, "Dropping {} bulk pull peers, target connections {}", drop, target);
|
||||
|
||||
for (int i = 0; i < drop; i++)
|
||||
{
|
||||
auto client = sorted_connections.top ();
|
||||
|
||||
node.logger.debug (nano::log::type::bootstrap, "Dropping peer with block rate {} and block count {} ({})",
|
||||
client->block_rate.load (),
|
||||
client->block_count.load (),
|
||||
client->channel->to_string ());
|
||||
|
||||
client->stop (false);
|
||||
sorted_connections.pop ();
|
||||
}
|
||||
}
|
||||
|
||||
node.logger.debug (nano::log::type::bootstrap, "Bulk pull connections: {}, rate: {} blocks/sec, bootstrap attempts {}, remaining pulls: {}",
|
||||
connections_count.load (),
|
||||
(int)rate_sum,
|
||||
attempts_count,
|
||||
num_pulls);
|
||||
|
||||
if (connections_count < target && (attempts_count != 0 || new_connections_empty) && !stopped)
|
||||
{
|
||||
auto delta = std::min ((target - connections_count) * 2, nano::bootstrap_limits::bootstrap_max_new_connections);
|
||||
// TODO - tune this better
|
||||
// Not many peers respond, need to try to make more connections than we need.
|
||||
for (auto i = 0u; i < delta; i++)
|
||||
{
|
||||
auto endpoint (node.network.bootstrap_peer ()); // Legacy bootstrap is compatible with older version of protocol
|
||||
if (endpoint != nano::tcp_endpoint (boost::asio::ip::address_v6::any (), 0) && (node.flags.allow_bootstrap_peers_duplicates || endpoints.find (endpoint) == endpoints.end ()) && !node.network.excluded_peers.check (endpoint))
|
||||
{
|
||||
connect_client (endpoint);
|
||||
endpoints.insert (endpoint);
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
new_connections_empty = false;
|
||||
}
|
||||
else if (connections_count == 0)
|
||||
{
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
new_connections_empty = true;
|
||||
}
|
||||
condition.notify_all ();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!stopped && repeat)
|
||||
{
|
||||
std::weak_ptr<nano::bootstrap_connections> this_w (shared_from_this ());
|
||||
node.workers.post_delayed (std::chrono::seconds (1), [this_w] () {
|
||||
if (auto this_l = this_w.lock ())
|
||||
{
|
||||
this_l->populate_connections ();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bootstrap_connections::start_populate_connections ()
|
||||
{
|
||||
if (!populate_connections_started.exchange (true))
|
||||
{
|
||||
populate_connections ();
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bootstrap_connections::add_pull (nano::pull_info const & pull_a)
|
||||
{
|
||||
nano::pull_info pull (pull_a);
|
||||
node.bootstrap_initiator.cache.update_pull (pull);
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
pulls.push_back (pull);
|
||||
}
|
||||
condition.notify_all ();
|
||||
}
|
||||
|
||||
void nano::bootstrap_connections::request_pull (nano::unique_lock<nano::mutex> & lock_a)
|
||||
{
|
||||
lock_a.unlock ();
|
||||
auto connection_l (connection ());
|
||||
lock_a.lock ();
|
||||
if (connection_l != nullptr && !pulls.empty ())
|
||||
{
|
||||
std::shared_ptr<nano::bootstrap_attempt> attempt_l;
|
||||
nano::pull_info pull;
|
||||
// Search pulls with existing attempts
|
||||
while (attempt_l == nullptr && !pulls.empty ())
|
||||
{
|
||||
pull = pulls.front ();
|
||||
pulls.pop_front ();
|
||||
attempt_l = node.bootstrap_initiator.attempts.find (pull.bootstrap_id);
|
||||
// Check if lazy pull is obsolete (head was processed or head is 0 for destinations requests)
|
||||
if (auto lazy = std::dynamic_pointer_cast<nano::bootstrap_attempt_lazy> (attempt_l))
|
||||
{
|
||||
if (!pull.head.is_zero () && lazy->lazy_processed_or_exists (pull.head))
|
||||
{
|
||||
attempt_l->pull_finished ();
|
||||
attempt_l = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (attempt_l != nullptr)
|
||||
{
|
||||
// The bulk_pull_client destructor attempt to requeue_pull which can cause a deadlock if this is the last reference
|
||||
// Dispatch request in an external thread in case it needs to be destroyed
|
||||
node.background ([connection_l, attempt_l, pull] () {
|
||||
auto client (std::make_shared<nano::bulk_pull_client> (connection_l, attempt_l, pull));
|
||||
client->request ();
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (connection_l != nullptr)
|
||||
{
|
||||
// Reuse connection if pulls deque become empty
|
||||
lock_a.unlock ();
|
||||
pool_connection (connection_l);
|
||||
lock_a.lock ();
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bootstrap_connections::requeue_pull (nano::pull_info const & pull_a, bool network_error)
|
||||
{
|
||||
auto pull (pull_a);
|
||||
if (!network_error)
|
||||
{
|
||||
++pull.attempts;
|
||||
}
|
||||
auto attempt_l (node.bootstrap_initiator.attempts.find (pull.bootstrap_id));
|
||||
if (attempt_l != nullptr)
|
||||
{
|
||||
auto lazy = std::dynamic_pointer_cast<nano::bootstrap_attempt_lazy> (attempt_l);
|
||||
++attempt_l->requeued_pulls;
|
||||
if (lazy)
|
||||
{
|
||||
pull.count = lazy->lazy_batch_size ();
|
||||
}
|
||||
if (attempt_l->mode == nano::bootstrap_mode::legacy && (pull.attempts < pull.retry_limit + (pull.processed / nano::bootstrap_limits::requeued_pulls_processed_blocks_factor)))
|
||||
{
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
pulls.push_front (pull);
|
||||
}
|
||||
attempt_l->pull_started ();
|
||||
condition.notify_all ();
|
||||
}
|
||||
else if (lazy && (pull.attempts <= pull.retry_limit + (pull.processed / node.network_params.bootstrap.lazy_max_pull_blocks)))
|
||||
{
|
||||
debug_assert (pull.account_or_head.as_block_hash () == pull.head);
|
||||
if (!lazy->lazy_processed_or_exists (pull.account_or_head.as_block_hash ()))
|
||||
{
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
pulls.push_back (pull);
|
||||
}
|
||||
attempt_l->pull_started ();
|
||||
condition.notify_all ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
node.stats.inc (nano::stat::type::bootstrap, nano::stat::detail::bulk_pull_failed_account, nano::stat::dir::in);
|
||||
node.logger.debug (nano::log::type::bootstrap, "Failed to pull account {} or head block {} down to {} after {} attempts and {} blocks processed",
|
||||
pull.account_or_head.to_account (),
|
||||
pull.account_or_head.to_string (),
|
||||
pull.end.to_string (),
|
||||
pull.attempts,
|
||||
pull.processed);
|
||||
|
||||
if (lazy && pull.processed > 0)
|
||||
{
|
||||
lazy->lazy_add (pull);
|
||||
}
|
||||
else if (attempt_l->mode == nano::bootstrap_mode::legacy)
|
||||
{
|
||||
node.bootstrap_initiator.cache.add (pull);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bootstrap_connections::clear_pulls (uint64_t bootstrap_id_a)
|
||||
{
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
|
||||
erase_if (pulls, [bootstrap_id_a] (auto const & pull) {
|
||||
return pull.bootstrap_id == bootstrap_id_a;
|
||||
});
|
||||
}
|
||||
condition.notify_all ();
|
||||
}
|
||||
|
||||
void nano::bootstrap_connections::run ()
|
||||
{
|
||||
start_populate_connections ();
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
while (!stopped)
|
||||
{
|
||||
if (!pulls.empty ())
|
||||
{
|
||||
request_pull (lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
condition.wait (lock);
|
||||
}
|
||||
}
|
||||
stopped = true;
|
||||
lock.unlock ();
|
||||
condition.notify_all ();
|
||||
}
|
||||
|
||||
void nano::bootstrap_connections::stop ()
|
||||
{
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
stopped = true;
|
||||
lock.unlock ();
|
||||
condition.notify_all ();
|
||||
lock.lock ();
|
||||
for (auto const & i : clients)
|
||||
{
|
||||
if (auto client = i.lock ())
|
||||
{
|
||||
client->socket->close ();
|
||||
}
|
||||
}
|
||||
clients.clear ();
|
||||
idle.clear ();
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/node/bootstrap/bootstrap_bulk_pull.hpp>
|
||||
#include <nano/node/common.hpp>
|
||||
#include <nano/node/transport/tcp_socket.hpp>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class node;
|
||||
namespace transport
|
||||
{
|
||||
class tcp_channel;
|
||||
}
|
||||
|
||||
class bootstrap_attempt;
|
||||
class bootstrap_connections;
|
||||
class frontier_req_client;
|
||||
class pull_info;
|
||||
|
||||
/**
|
||||
* Owns the client side of the bootstrap connection.
|
||||
*/
|
||||
class bootstrap_client final : public std::enable_shared_from_this<bootstrap_client>
|
||||
{
|
||||
public:
|
||||
bootstrap_client (std::shared_ptr<nano::node> const & node_a, std::shared_ptr<nano::transport::tcp_channel> const & channel_a, std::shared_ptr<nano::transport::tcp_socket> const & socket_a);
|
||||
~bootstrap_client ();
|
||||
void stop (bool force);
|
||||
double sample_block_rate ();
|
||||
double elapsed_seconds () const;
|
||||
void set_start_time (std::chrono::steady_clock::time_point start_time_a);
|
||||
std::weak_ptr<nano::node> node;
|
||||
std::shared_ptr<nano::transport::tcp_channel> channel;
|
||||
std::shared_ptr<nano::transport::tcp_socket> socket;
|
||||
std::shared_ptr<std::vector<uint8_t>> receive_buffer;
|
||||
std::atomic<uint64_t> block_count{ 0 };
|
||||
std::atomic<double> block_rate{ 0 };
|
||||
std::atomic<bool> pending_stop{ false };
|
||||
std::atomic<bool> hard_stop{ false };
|
||||
|
||||
private:
|
||||
mutable nano::mutex start_time_mutex;
|
||||
std::chrono::steady_clock::time_point start_time_m;
|
||||
};
|
||||
|
||||
/**
|
||||
* Container for bootstrap_client objects. Owned by bootstrap_initiator which pools open connections and makes them available
|
||||
* for use by different bootstrap sessions.
|
||||
*/
|
||||
class bootstrap_connections final : public std::enable_shared_from_this<bootstrap_connections>
|
||||
{
|
||||
public:
|
||||
explicit bootstrap_connections (nano::node & node_a);
|
||||
std::shared_ptr<nano::bootstrap_client> connection (std::shared_ptr<nano::bootstrap_attempt> const & attempt_a = nullptr, bool use_front_connection = false);
|
||||
void pool_connection (std::shared_ptr<nano::bootstrap_client> const & client_a, bool new_client = false, bool push_front = false);
|
||||
void add_connection (nano::endpoint const & endpoint_a);
|
||||
std::shared_ptr<nano::bootstrap_client> find_connection (nano::tcp_endpoint const & endpoint_a);
|
||||
void connect_client (nano::tcp_endpoint const & endpoint_a, bool push_front = false);
|
||||
unsigned target_connections (std::size_t pulls_remaining, std::size_t attempts_count) const;
|
||||
void populate_connections (bool repeat = true);
|
||||
void start_populate_connections ();
|
||||
void add_pull (nano::pull_info const & pull_a);
|
||||
void request_pull (nano::unique_lock<nano::mutex> & lock_a);
|
||||
void requeue_pull (nano::pull_info const & pull_a, bool network_error = false);
|
||||
void clear_pulls (uint64_t);
|
||||
void run ();
|
||||
void stop ();
|
||||
std::deque<std::weak_ptr<nano::bootstrap_client>> clients;
|
||||
std::atomic<unsigned> connections_count{ 0 };
|
||||
nano::node & node;
|
||||
std::deque<std::shared_ptr<nano::bootstrap_client>> idle;
|
||||
std::deque<nano::pull_info> pulls;
|
||||
std::atomic<bool> populate_connections_started{ false };
|
||||
std::atomic<bool> new_connections_empty{ false };
|
||||
std::atomic<bool> stopped{ false };
|
||||
nano::mutex mutex;
|
||||
nano::condition_variable condition;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,424 +0,0 @@
|
|||
#include <nano/node/bootstrap/bootstrap_attempt.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_frontier.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_legacy.hpp>
|
||||
#include <nano/node/node.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
#include <nano/secure/ledger_set_any.hpp>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
constexpr double nano::bootstrap_limits::bootstrap_connection_warmup_time_sec;
|
||||
constexpr double nano::bootstrap_limits::bootstrap_minimum_elapsed_seconds_blockrate;
|
||||
constexpr double nano::bootstrap_limits::bootstrap_minimum_frontier_blocks_per_sec;
|
||||
constexpr unsigned nano::bootstrap_limits::bulk_push_cost_limit;
|
||||
|
||||
constexpr std::size_t nano::frontier_req_client::size_frontier;
|
||||
|
||||
void nano::frontier_req_client::run (nano::account const & start_account_a, uint32_t const frontiers_age_a, uint32_t const count_a)
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
nano::frontier_req request{ node->network_params.network };
|
||||
request.start = (start_account_a.is_zero () || start_account_a.number () == std::numeric_limits<nano::uint256_t>::max ()) ? start_account_a.number () : start_account_a.number () + 1;
|
||||
request.age = frontiers_age_a;
|
||||
request.count = count_a;
|
||||
current = start_account_a;
|
||||
frontiers_age = frontiers_age_a;
|
||||
count_limit = count_a;
|
||||
next (); // Load accounts from disk
|
||||
auto this_l (shared_from_this ());
|
||||
connection->channel->send (
|
||||
request, [this_l] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
auto node = this_l->connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!ec)
|
||||
{
|
||||
this_l->receive_frontier ();
|
||||
}
|
||||
else
|
||||
{
|
||||
node->logger.debug (nano::log::type::frontier_req_client, "Error while sending bootstrap request: {}", ec.message ());
|
||||
}
|
||||
},
|
||||
nano::transport::buffer_drop_policy::no_limiter_drop);
|
||||
}
|
||||
|
||||
nano::frontier_req_client::frontier_req_client (std::shared_ptr<nano::bootstrap_client> const & connection_a, std::shared_ptr<nano::bootstrap_attempt_legacy> const & attempt_a) :
|
||||
connection (connection_a),
|
||||
attempt (attempt_a),
|
||||
count (0),
|
||||
bulk_push_cost (0)
|
||||
{
|
||||
}
|
||||
|
||||
void nano::frontier_req_client::receive_frontier ()
|
||||
{
|
||||
auto this_l (shared_from_this ());
|
||||
connection->socket->async_read (connection->receive_buffer, nano::frontier_req_client::size_frontier, [this_l] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
auto node = this_l->connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// An issue with asio is that sometimes, instead of reporting a bad file descriptor during disconnect,
|
||||
// we simply get a size of 0.
|
||||
if (size_a == nano::frontier_req_client::size_frontier)
|
||||
{
|
||||
node->bootstrap_workers.post ([this_l, ec, size_a] () {
|
||||
this_l->received_frontier (ec, size_a);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
node->logger.debug (nano::log::type::frontier_req_client, "Invalid size: expected {}, got {}", nano::frontier_req_client::size_frontier, size_a);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool nano::frontier_req_client::bulk_push_available ()
|
||||
{
|
||||
return bulk_push_cost < nano::bootstrap_limits::bulk_push_cost_limit && frontiers_age == std::numeric_limits<decltype (frontiers_age)>::max ();
|
||||
}
|
||||
|
||||
void nano::frontier_req_client::unsynced (nano::block_hash const & head, nano::block_hash const & end)
|
||||
{
|
||||
if (bulk_push_available ())
|
||||
{
|
||||
attempt->add_bulk_push_target (head, end);
|
||||
if (end.is_zero ())
|
||||
{
|
||||
bulk_push_cost += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
bulk_push_cost += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nano::frontier_req_client::received_frontier (boost::system::error_code const & ec, std::size_t size_a)
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!ec)
|
||||
{
|
||||
debug_assert (size_a == nano::frontier_req_client::size_frontier);
|
||||
nano::account account;
|
||||
nano::bufferstream account_stream (connection->receive_buffer->data (), sizeof (account));
|
||||
auto error1 (nano::try_read (account_stream, account));
|
||||
(void)error1;
|
||||
debug_assert (!error1);
|
||||
nano::block_hash latest;
|
||||
nano::bufferstream latest_stream (connection->receive_buffer->data () + sizeof (account), sizeof (latest));
|
||||
auto error2 (nano::try_read (latest_stream, latest));
|
||||
(void)error2;
|
||||
debug_assert (!error2);
|
||||
if (count == 0)
|
||||
{
|
||||
start_time = std::chrono::steady_clock::now ();
|
||||
}
|
||||
++count;
|
||||
std::chrono::duration<double> time_span = std::chrono::duration_cast<std::chrono::duration<double>> (std::chrono::steady_clock::now () - start_time);
|
||||
|
||||
double elapsed_sec = std::max (time_span.count (), nano::bootstrap_limits::bootstrap_minimum_elapsed_seconds_blockrate);
|
||||
double blocks_per_sec = static_cast<double> (count) / elapsed_sec;
|
||||
double age_factor = (frontiers_age == std::numeric_limits<decltype (frontiers_age)>::max ()) ? 1.0 : 1.5; // Allow slower frontiers receive for requests with age
|
||||
if (elapsed_sec > nano::bootstrap_limits::bootstrap_connection_warmup_time_sec && blocks_per_sec * age_factor < nano::bootstrap_limits::bootstrap_minimum_frontier_blocks_per_sec)
|
||||
{
|
||||
node->logger.debug (nano::log::type::frontier_req_client, "Aborting frontier req because it was too slow: {} frontiers per second, last {}", blocks_per_sec, account.to_account ());
|
||||
|
||||
promise.set_value (true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (attempt->should_log ())
|
||||
{
|
||||
node->logger.debug (nano::log::type::frontier_req_client, "Received {} frontiers from {}", count, connection->channel->to_string ());
|
||||
}
|
||||
|
||||
if (!account.is_zero () && count <= count_limit)
|
||||
{
|
||||
last_account = account;
|
||||
while (!current.is_zero () && current < account)
|
||||
{
|
||||
// We know about an account they don't.
|
||||
unsynced (frontier, 0);
|
||||
next ();
|
||||
}
|
||||
if (!current.is_zero ())
|
||||
{
|
||||
if (account == current)
|
||||
{
|
||||
if (latest == frontier)
|
||||
{
|
||||
// In sync
|
||||
}
|
||||
else
|
||||
{
|
||||
if (node->block_or_pruned_exists (latest))
|
||||
{
|
||||
// We know about a block they don't.
|
||||
unsynced (frontier, latest);
|
||||
}
|
||||
else
|
||||
{
|
||||
attempt->add_frontier (nano::pull_info (account, latest, frontier, attempt->incremental_id, 0, node->network_params.bootstrap.frontier_retry_limit));
|
||||
// Either we're behind or there's a fork we differ on
|
||||
// Either way, bulk pushing will probably not be effective
|
||||
bulk_push_cost += 5;
|
||||
}
|
||||
}
|
||||
next ();
|
||||
}
|
||||
else
|
||||
{
|
||||
debug_assert (account < current);
|
||||
attempt->add_frontier (nano::pull_info (account, latest, nano::block_hash (0), attempt->incremental_id, 0, node->network_params.bootstrap.frontier_retry_limit));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
attempt->add_frontier (nano::pull_info (account, latest, nano::block_hash (0), attempt->incremental_id, 0, node->network_params.bootstrap.frontier_retry_limit));
|
||||
}
|
||||
receive_frontier ();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (count <= count_limit)
|
||||
{
|
||||
while (!current.is_zero () && bulk_push_available ())
|
||||
{
|
||||
// We know about an account they don't.
|
||||
unsynced (frontier, 0);
|
||||
next ();
|
||||
}
|
||||
// Prevent new frontier_req requests
|
||||
attempt->set_start_account (std::numeric_limits<nano::uint256_t>::max ());
|
||||
|
||||
node->logger.debug (nano::log::type::frontier_req_client, "Bulk push cost: {}", bulk_push_cost);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set last processed account as new start target
|
||||
attempt->set_start_account (last_account);
|
||||
}
|
||||
node->bootstrap_initiator.connections->pool_connection (connection);
|
||||
try
|
||||
{
|
||||
promise.set_value (false);
|
||||
}
|
||||
catch (std::future_error &)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
node->logger.debug (nano::log::type::frontier_req_client, "Error while receiving frontier: {}", ec.message ());
|
||||
}
|
||||
}
|
||||
|
||||
void nano::frontier_req_client::next ()
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Filling accounts deque to prevent often read transactions
|
||||
if (accounts.empty ())
|
||||
{
|
||||
std::size_t max_size (128);
|
||||
auto transaction (node->store.tx_begin_read ());
|
||||
for (auto i (node->store.account.begin (transaction, current.number () + 1)), n (node->store.account.end (transaction)); i != n && accounts.size () != max_size; ++i)
|
||||
{
|
||||
nano::account_info const & info (i->second);
|
||||
nano::account const & account (i->first);
|
||||
accounts.emplace_back (account, info.head);
|
||||
}
|
||||
|
||||
/* If loop breaks before max_size, then accounts_end () is reached. Add empty record */
|
||||
if (accounts.size () != max_size)
|
||||
{
|
||||
accounts.emplace_back (nano::account{}, nano::block_hash (0));
|
||||
}
|
||||
}
|
||||
// Retrieving accounts from deque
|
||||
auto const & account_pair (accounts.front ());
|
||||
current = account_pair.first;
|
||||
frontier = account_pair.second;
|
||||
accounts.pop_front ();
|
||||
}
|
||||
|
||||
nano::frontier_req_server::frontier_req_server (std::shared_ptr<nano::transport::tcp_server> const & connection_a, std::unique_ptr<nano::frontier_req> request_a) :
|
||||
connection (connection_a),
|
||||
current (request_a->start.number () - 1),
|
||||
frontier (0),
|
||||
request (std::move (request_a)),
|
||||
count (0)
|
||||
{
|
||||
next ();
|
||||
}
|
||||
|
||||
void nano::frontier_req_server::send_next ()
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!current.is_zero () && count < request->count)
|
||||
{
|
||||
node->logger.trace (nano::log::type::frontier_req_server, nano::log::detail::sending_frontier,
|
||||
nano::log::arg{ "account", current.to_account () }, // TODO: Convert to lazy eval
|
||||
nano::log::arg{ "frontier", frontier },
|
||||
nano::log::arg{ "socket", connection->socket });
|
||||
|
||||
std::vector<uint8_t> send_buffer;
|
||||
{
|
||||
nano::vectorstream stream (send_buffer);
|
||||
write (stream, current.bytes);
|
||||
write (stream, frontier.bytes);
|
||||
debug_assert (!current.is_zero ());
|
||||
debug_assert (!frontier.is_zero ());
|
||||
}
|
||||
|
||||
auto this_l (shared_from_this ());
|
||||
next ();
|
||||
connection->socket->async_write (nano::shared_const_buffer (std::move (send_buffer)), [this_l] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
this_l->sent_action (ec, size_a);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
send_finished ();
|
||||
}
|
||||
}
|
||||
|
||||
void nano::frontier_req_server::send_finished ()
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::vector<uint8_t> send_buffer;
|
||||
{
|
||||
nano::vectorstream stream (send_buffer);
|
||||
nano::uint256_union zero (0);
|
||||
write (stream, zero.bytes);
|
||||
write (stream, zero.bytes);
|
||||
}
|
||||
|
||||
node->logger.debug (nano::log::type::frontier_req_server, "Frontier sending finished");
|
||||
|
||||
auto this_l (shared_from_this ());
|
||||
connection->socket->async_write (nano::shared_const_buffer (std::move (send_buffer)), [this_l] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
this_l->no_block_sent (ec, size_a);
|
||||
});
|
||||
}
|
||||
|
||||
void nano::frontier_req_server::no_block_sent (boost::system::error_code const & ec, std::size_t size_a)
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!ec)
|
||||
{
|
||||
connection->start ();
|
||||
}
|
||||
else
|
||||
{
|
||||
node->logger.debug (nano::log::type::frontier_req_server, "Error sending frontier finish: {}", ec.message ());
|
||||
}
|
||||
}
|
||||
|
||||
void nano::frontier_req_server::sent_action (boost::system::error_code const & ec, std::size_t size_a)
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!ec)
|
||||
{
|
||||
count++;
|
||||
|
||||
node->bootstrap_workers.post ([this_l = shared_from_this ()] () {
|
||||
this_l->send_next ();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
node->logger.debug (nano::log::type::frontier_req_server, "Error sending frontier pair: {}", ec.message ());
|
||||
}
|
||||
}
|
||||
|
||||
void nano::frontier_req_server::next ()
|
||||
{
|
||||
auto node = connection->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Filling accounts deque to prevent often read transactions
|
||||
if (accounts.empty ())
|
||||
{
|
||||
auto now (nano::seconds_since_epoch ());
|
||||
bool disable_age_filter (request->age == std::numeric_limits<decltype (request->age)>::max ());
|
||||
std::size_t max_size (128);
|
||||
auto transaction (node->store.tx_begin_read ());
|
||||
if (!send_confirmed ())
|
||||
{
|
||||
for (auto i (node->store.account.begin (transaction, current.number () + 1)), n (node->store.account.end (transaction)); i != n && accounts.size () != max_size; ++i)
|
||||
{
|
||||
nano::account_info const & info (i->second);
|
||||
if (disable_age_filter || (now - info.modified) <= request->age)
|
||||
{
|
||||
nano::account const & account (i->first);
|
||||
accounts.emplace_back (account, info.head);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto i (node->store.confirmation_height.begin (transaction, current.number () + 1)), n (node->store.confirmation_height.end (transaction)); i != n && accounts.size () != max_size; ++i)
|
||||
{
|
||||
nano::confirmation_height_info const & info (i->second);
|
||||
nano::block_hash const & confirmed_frontier (info.frontier);
|
||||
if (!confirmed_frontier.is_zero ())
|
||||
{
|
||||
nano::account const & account (i->first);
|
||||
accounts.emplace_back (account, confirmed_frontier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If loop breaks before max_size, then accounts_end () is reached. Add empty record to finish frontier_req_server */
|
||||
if (accounts.size () != max_size)
|
||||
{
|
||||
accounts.emplace_back (nano::account{}, nano::block_hash (0));
|
||||
}
|
||||
}
|
||||
// Retrieving accounts from deque
|
||||
auto const & account_pair (accounts.front ());
|
||||
current = account_pair.first;
|
||||
frontier = account_pair.second;
|
||||
accounts.pop_front ();
|
||||
}
|
||||
|
||||
bool nano::frontier_req_server::send_confirmed ()
|
||||
{
|
||||
return request->header.frontier_req_is_only_confirmed_present ();
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/lib/numbers.hpp>
|
||||
|
||||
#include <deque>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class bootstrap_attempt_legacy;
|
||||
class bootstrap_client;
|
||||
namespace transport
|
||||
{
|
||||
class tcp_server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Client side of a frontier request. Created to send and listen for frontier sequences from the server.
|
||||
*/
|
||||
class frontier_req_client final : public std::enable_shared_from_this<nano::frontier_req_client>
|
||||
{
|
||||
public:
|
||||
explicit frontier_req_client (std::shared_ptr<nano::bootstrap_client> const &, std::shared_ptr<nano::bootstrap_attempt_legacy> const &);
|
||||
void run (nano::account const & start_account_a, uint32_t const frontiers_age_a, uint32_t const count_a);
|
||||
void receive_frontier ();
|
||||
void received_frontier (boost::system::error_code const &, std::size_t);
|
||||
bool bulk_push_available ();
|
||||
void unsynced (nano::block_hash const &, nano::block_hash const &);
|
||||
void next ();
|
||||
std::shared_ptr<nano::bootstrap_client> connection;
|
||||
std::shared_ptr<nano::bootstrap_attempt_legacy> attempt;
|
||||
nano::account current;
|
||||
nano::block_hash frontier;
|
||||
unsigned count;
|
||||
nano::account last_account{ std::numeric_limits<nano::uint256_t>::max () }; // Using last possible account stop further frontier requests
|
||||
std::chrono::steady_clock::time_point start_time;
|
||||
std::promise<bool> promise;
|
||||
/** A very rough estimate of the cost of `bulk_push`ing missing blocks */
|
||||
uint64_t bulk_push_cost;
|
||||
std::deque<std::pair<nano::account, nano::block_hash>> accounts;
|
||||
uint32_t frontiers_age{ std::numeric_limits<uint32_t>::max () };
|
||||
uint32_t count_limit{ std::numeric_limits<uint32_t>::max () };
|
||||
static std::size_t constexpr size_frontier = sizeof (nano::account) + sizeof (nano::block_hash);
|
||||
};
|
||||
|
||||
class frontier_req;
|
||||
|
||||
/**
|
||||
* Server side of a frontier request. Created when a tcp_server receives a frontier_req message and exited when end-of-list is reached.
|
||||
*/
|
||||
class frontier_req_server final : public std::enable_shared_from_this<nano::frontier_req_server>
|
||||
{
|
||||
public:
|
||||
frontier_req_server (std::shared_ptr<nano::transport::tcp_server> const &, std::unique_ptr<nano::frontier_req>);
|
||||
void send_next ();
|
||||
void sent_action (boost::system::error_code const &, std::size_t);
|
||||
void send_finished ();
|
||||
void no_block_sent (boost::system::error_code const &, std::size_t);
|
||||
void next ();
|
||||
bool send_confirmed ();
|
||||
std::shared_ptr<nano::transport::tcp_server> connection;
|
||||
nano::account current;
|
||||
nano::block_hash frontier;
|
||||
std::unique_ptr<nano::frontier_req> request;
|
||||
std::size_t count;
|
||||
std::deque<std::pair<nano::account, nano::block_hash>> accounts;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,634 +0,0 @@
|
|||
#include <nano/lib/blocks.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_lazy.hpp>
|
||||
#include <nano/node/common.hpp>
|
||||
#include <nano/node/node.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
#include <nano/secure/ledger_set_any.hpp>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
constexpr std::chrono::seconds nano::bootstrap_limits::lazy_flush_delay_sec;
|
||||
constexpr uint64_t nano::bootstrap_limits::lazy_batch_pull_count_resize_blocks_limit;
|
||||
constexpr double nano::bootstrap_limits::lazy_batch_pull_count_resize_ratio;
|
||||
constexpr std::size_t nano::bootstrap_limits::lazy_blocks_restart_limit;
|
||||
|
||||
nano::bootstrap_attempt_lazy::bootstrap_attempt_lazy (std::shared_ptr<nano::node> const & node_a, uint64_t incremental_id_a, std::string const & id_a) :
|
||||
nano::bootstrap_attempt (node_a, nano::bootstrap_mode::lazy, incremental_id_a, id_a)
|
||||
{
|
||||
node_a->bootstrap_initiator.notify_listeners (true);
|
||||
}
|
||||
|
||||
nano::bootstrap_attempt_lazy::~bootstrap_attempt_lazy ()
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
debug_assert (lazy_blocks.size () == lazy_blocks_count);
|
||||
node->bootstrap_initiator.notify_listeners (false);
|
||||
}
|
||||
|
||||
bool nano::bootstrap_attempt_lazy::lazy_start (nano::hash_or_account const & hash_or_account_a)
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
bool inserted (false);
|
||||
// Add start blocks, limit 1024 (4k with disabled legacy bootstrap)
|
||||
std::size_t max_keys (node->flags.disable_legacy_bootstrap ? 4 * 1024 : 1024);
|
||||
if (lazy_keys.size () < max_keys && lazy_keys.find (hash_or_account_a.as_block_hash ()) == lazy_keys.end () && !lazy_blocks_processed (hash_or_account_a.as_block_hash ()))
|
||||
{
|
||||
lazy_keys.insert (hash_or_account_a.as_block_hash ());
|
||||
lazy_pulls.emplace_back (hash_or_account_a, node->network_params.bootstrap.lazy_retry_limit);
|
||||
lock.unlock ();
|
||||
condition.notify_all ();
|
||||
inserted = true;
|
||||
}
|
||||
return inserted;
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_lazy::lazy_add (nano::hash_or_account const & hash_or_account_a, unsigned retry_limit)
|
||||
{
|
||||
// Add only unknown blocks
|
||||
debug_assert (!mutex.try_lock ());
|
||||
if (!lazy_blocks_processed (hash_or_account_a.as_block_hash ()))
|
||||
{
|
||||
lazy_pulls.emplace_back (hash_or_account_a, retry_limit);
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_lazy::lazy_add (nano::pull_info const & pull_a)
|
||||
{
|
||||
debug_assert (pull_a.account_or_head.as_block_hash () == pull_a.head);
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
lazy_add (pull_a.account_or_head, pull_a.retry_limit);
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_lazy::lazy_requeue (nano::block_hash const & hash_a, nano::block_hash const & previous_a)
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
// Add only known blocks
|
||||
if (lazy_blocks_processed (hash_a))
|
||||
{
|
||||
lazy_blocks_erase (hash_a);
|
||||
lock.unlock ();
|
||||
node->bootstrap_initiator.connections->requeue_pull (nano::pull_info (hash_a, hash_a, previous_a, incremental_id, static_cast<nano::pull_info::count_t> (1), node->network_params.bootstrap.lazy_destinations_retry_limit));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t nano::bootstrap_attempt_lazy::lazy_batch_size ()
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
auto result (node->network_params.bootstrap.lazy_max_pull_blocks);
|
||||
if (total_blocks > nano::bootstrap_limits::lazy_batch_pull_count_resize_blocks_limit && lazy_blocks_count != 0)
|
||||
{
|
||||
auto lazy_blocks_ratio (static_cast<double> (total_blocks / lazy_blocks_count));
|
||||
if (lazy_blocks_ratio > nano::bootstrap_limits::lazy_batch_pull_count_resize_ratio)
|
||||
{
|
||||
// Increasing blocks ratio weight as more important (^3). Small batch count should lower blocks ratio below target
|
||||
double lazy_blocks_factor (std::pow (lazy_blocks_ratio / nano::bootstrap_limits::lazy_batch_pull_count_resize_ratio, 3.0));
|
||||
// Decreasing total block count weight as less important (sqrt)
|
||||
double total_blocks_factor (std::sqrt (total_blocks / nano::bootstrap_limits::lazy_batch_pull_count_resize_blocks_limit));
|
||||
uint32_t batch_count_min (node->network_params.bootstrap.lazy_max_pull_blocks / static_cast<uint32_t> (lazy_blocks_factor * total_blocks_factor));
|
||||
result = std::max (node->network_params.bootstrap.lazy_min_pull_blocks, batch_count_min);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_lazy::lazy_pull_flush (nano::unique_lock<nano::mutex> & lock_a)
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
static std::size_t const max_pulls (static_cast<std::size_t> (nano::bootstrap_limits::bootstrap_connection_scale_target_blocks) * 3);
|
||||
if (pulling < max_pulls)
|
||||
{
|
||||
debug_assert (node->network_params.bootstrap.lazy_max_pull_blocks <= std::numeric_limits<nano::pull_info::count_t>::max ());
|
||||
nano::pull_info::count_t batch_count (lazy_batch_size ());
|
||||
uint64_t read_count (0);
|
||||
std::size_t count (0);
|
||||
auto transaction = node->ledger.tx_begin_read ();
|
||||
while (!lazy_pulls.empty () && count < max_pulls)
|
||||
{
|
||||
auto pull_start (lazy_pulls.front ());
|
||||
lazy_pulls.pop_front ();
|
||||
// Recheck if block was already processed
|
||||
if (!lazy_blocks_processed (pull_start.first.as_block_hash ()) && !node->ledger.any.block_exists_or_pruned (transaction, pull_start.first.as_block_hash ()))
|
||||
{
|
||||
lock_a.unlock ();
|
||||
node->bootstrap_initiator.connections->add_pull (nano::pull_info (pull_start.first, pull_start.first.as_block_hash (), nano::block_hash (0), incremental_id, batch_count, pull_start.second));
|
||||
++pulling;
|
||||
++count;
|
||||
lock_a.lock ();
|
||||
}
|
||||
// We don't want to open read transactions for too long
|
||||
++read_count;
|
||||
if (read_count % batch_read_size == 0)
|
||||
{
|
||||
lock_a.unlock ();
|
||||
transaction.refresh ();
|
||||
lock_a.lock ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool nano::bootstrap_attempt_lazy::lazy_finished ()
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
debug_assert (!mutex.try_lock ());
|
||||
if (stopped)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool result (true);
|
||||
uint64_t read_count (0);
|
||||
auto transaction = node->ledger.tx_begin_read ();
|
||||
for (auto it (lazy_keys.begin ()), end (lazy_keys.end ()); it != end && !stopped;)
|
||||
{
|
||||
if (node->ledger.any.block_exists_or_pruned (transaction, *it))
|
||||
{
|
||||
it = lazy_keys.erase (it);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = false;
|
||||
break;
|
||||
// No need to increment `it` as we break above.
|
||||
}
|
||||
// We don't want to open read transactions for too long
|
||||
++read_count;
|
||||
if (read_count % batch_read_size == 0)
|
||||
{
|
||||
transaction.refresh ();
|
||||
}
|
||||
}
|
||||
// Finish lazy bootstrap without lazy pulls (in combination with still_pulling ())
|
||||
if (!result && lazy_pulls.empty () && lazy_state_backlog.empty ())
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool nano::bootstrap_attempt_lazy::lazy_has_expired () const
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool result (false);
|
||||
// Max 30 minutes run with enabled legacy bootstrap
|
||||
static std::chrono::minutes const max_lazy_time (node->flags.disable_legacy_bootstrap ? 7 * 24 * 60 : 30);
|
||||
if (std::chrono::steady_clock::now () - lazy_start_time >= max_lazy_time)
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
else if (!node->flags.disable_legacy_bootstrap && lazy_blocks_count > nano::bootstrap_limits::lazy_blocks_restart_limit)
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_lazy::run ()
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
debug_assert (started);
|
||||
debug_assert (!node->flags.disable_lazy_bootstrap);
|
||||
node->bootstrap_initiator.connections->populate_connections (false);
|
||||
lazy_start_time = std::chrono::steady_clock::now ();
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
while ((still_pulling () || !lazy_finished ()) && !lazy_has_expired ())
|
||||
{
|
||||
unsigned iterations (0);
|
||||
while (still_pulling () && !lazy_has_expired ())
|
||||
{
|
||||
condition.wait (lock, [this, &stopped = stopped, &pulling = pulling, &lazy_pulls = lazy_pulls] { return stopped || pulling == 0 || (pulling < nano::bootstrap_limits::bootstrap_connection_scale_target_blocks && !lazy_pulls.empty ()) || lazy_has_expired (); });
|
||||
++iterations;
|
||||
// Flushing lazy pulls
|
||||
lazy_pull_flush (lock);
|
||||
// Start backlog cleanup
|
||||
if (iterations % 100 == 0)
|
||||
{
|
||||
lazy_backlog_cleanup ();
|
||||
}
|
||||
}
|
||||
// Flushing lazy pulls
|
||||
lazy_pull_flush (lock);
|
||||
// Check if some blocks required for backlog were processed. Start destinations check
|
||||
if (pulling == 0)
|
||||
{
|
||||
lazy_backlog_cleanup ();
|
||||
lazy_pull_flush (lock);
|
||||
}
|
||||
}
|
||||
if (!stopped)
|
||||
{
|
||||
node->logger.debug (nano::log::type::bootstrap_lazy, "Completed lazy pulls");
|
||||
}
|
||||
if (lazy_has_expired ())
|
||||
{
|
||||
node->logger.debug (nano::log::type::bootstrap_lazy, "Lazy bootstrap attempt ID {} expired", id);
|
||||
}
|
||||
lock.unlock ();
|
||||
stop ();
|
||||
condition.notify_all ();
|
||||
}
|
||||
|
||||
bool nano::bootstrap_attempt_lazy::process_block (std::shared_ptr<nano::block> const & block_a, nano::account const & known_account_a, uint64_t pull_blocks_processed, nano::bulk_pull::count_t max_blocks, bool block_expected, unsigned retry_limit)
|
||||
{
|
||||
bool stop_pull (false);
|
||||
if (block_expected)
|
||||
{
|
||||
stop_pull = process_block_lazy (block_a, known_account_a, pull_blocks_processed, max_blocks, retry_limit);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Drop connection with unexpected block for lazy bootstrap
|
||||
stop_pull = true;
|
||||
}
|
||||
return stop_pull;
|
||||
}
|
||||
|
||||
bool nano::bootstrap_attempt_lazy::process_block_lazy (std::shared_ptr<nano::block> const & block_a, nano::account const & known_account_a, uint64_t pull_blocks_processed, nano::bulk_pull::count_t max_blocks, unsigned retry_limit)
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool stop_pull (false);
|
||||
auto hash (block_a->hash ());
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
// Processing new blocks
|
||||
if (!lazy_blocks_processed (hash))
|
||||
{
|
||||
// Search for new dependencies
|
||||
if (block_a->source_field () && !node->block_or_pruned_exists (block_a->source_field ().value ()) && block_a->source_field ().value () != node->network_params.ledger.genesis->account ().as_union ())
|
||||
{
|
||||
lazy_add (block_a->source_field ().value (), retry_limit);
|
||||
}
|
||||
else if (block_a->type () == nano::block_type::state)
|
||||
{
|
||||
lazy_block_state (block_a, retry_limit);
|
||||
}
|
||||
lazy_blocks_insert (hash);
|
||||
// Adding lazy balances for first processed block in pull
|
||||
if (pull_blocks_processed == 1 && (block_a->type () == nano::block_type::state || block_a->type () == nano::block_type::send))
|
||||
{
|
||||
lazy_balances.emplace (hash, block_a->balance_field ().value ().number ());
|
||||
}
|
||||
// Clearing lazy balances for previous block
|
||||
if (!block_a->previous ().is_zero () && lazy_balances.find (block_a->previous ()) != lazy_balances.end ())
|
||||
{
|
||||
lazy_balances.erase (block_a->previous ());
|
||||
}
|
||||
lazy_block_state_backlog_check (block_a, hash);
|
||||
lock.unlock ();
|
||||
node->block_processor.add (block_a, nano::block_source::bootstrap_legacy);
|
||||
}
|
||||
// Force drop lazy bootstrap connection for long bulk_pull
|
||||
if (pull_blocks_processed > max_blocks)
|
||||
{
|
||||
stop_pull = true;
|
||||
}
|
||||
return stop_pull;
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_lazy::lazy_block_state (std::shared_ptr<nano::block> const & block_a, unsigned retry_limit)
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<nano::state_block> block_l (std::static_pointer_cast<nano::state_block> (block_a));
|
||||
if (block_l != nullptr)
|
||||
{
|
||||
auto transaction = node->ledger.tx_begin_read ();
|
||||
nano::uint128_t balance (block_l->hashables.balance.number ());
|
||||
auto const & link (block_l->hashables.link);
|
||||
// If link is not epoch link or 0. And if block from link is unknown
|
||||
if (!link.is_zero () && !node->ledger.is_epoch_link (link) && !lazy_blocks_processed (link.as_block_hash ()) && !node->ledger.any.block_exists_or_pruned (transaction, link.as_block_hash ()))
|
||||
{
|
||||
auto const & previous (block_l->hashables.previous);
|
||||
// If state block previous is 0 then source block required
|
||||
if (previous.is_zero ())
|
||||
{
|
||||
lazy_add (link, retry_limit);
|
||||
}
|
||||
// In other cases previous block balance required to find out subtype of state block
|
||||
else if (node->ledger.any.block_exists_or_pruned (transaction, previous))
|
||||
{
|
||||
auto previous_balance = node->ledger.any.block_balance (transaction, previous);
|
||||
if (previous_balance)
|
||||
{
|
||||
if (previous_balance.value ().number () <= balance)
|
||||
{
|
||||
lazy_add (link, retry_limit);
|
||||
}
|
||||
}
|
||||
// Else ignore pruned blocks
|
||||
}
|
||||
// Search balance of already processed previous blocks
|
||||
else if (lazy_blocks_processed (previous))
|
||||
{
|
||||
auto previous_balance (lazy_balances.find (previous));
|
||||
if (previous_balance != lazy_balances.end ())
|
||||
{
|
||||
if (previous_balance->second <= balance)
|
||||
{
|
||||
lazy_add (link, retry_limit);
|
||||
}
|
||||
lazy_balances.erase (previous_balance);
|
||||
}
|
||||
}
|
||||
// Insert in backlog state blocks if previous wasn't already processed
|
||||
else
|
||||
{
|
||||
lazy_state_backlog.emplace (previous, nano::lazy_state_backlog_item{ link, balance, retry_limit });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_lazy::lazy_block_state_backlog_check (std::shared_ptr<nano::block> const & block_a, nano::block_hash const & hash_a)
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Search unknown state blocks balances
|
||||
auto find_state (lazy_state_backlog.find (hash_a));
|
||||
if (find_state != lazy_state_backlog.end ())
|
||||
{
|
||||
auto next_block (find_state->second);
|
||||
// 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_field ().value ().number () <= next_block.balance) // balance
|
||||
{
|
||||
lazy_add (next_block.link, next_block.retry_limit); // link
|
||||
}
|
||||
}
|
||||
// Assumption for other legacy block types
|
||||
else if (lazy_undefined_links.find (next_block.link.as_block_hash ()) == lazy_undefined_links.end ())
|
||||
{
|
||||
lazy_add (next_block.link, node->network_params.bootstrap.lazy_retry_limit); // Head is not confirmed. It can be account or hash or non-existing
|
||||
lazy_undefined_links.insert (next_block.link.as_block_hash ());
|
||||
}
|
||||
lazy_state_backlog.erase (find_state);
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_lazy::lazy_backlog_cleanup ()
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
uint64_t read_count (0);
|
||||
auto transaction = node->ledger.tx_begin_read ();
|
||||
for (auto it (lazy_state_backlog.begin ()), end (lazy_state_backlog.end ()); it != end && !stopped;)
|
||||
{
|
||||
if (node->ledger.any.block_exists_or_pruned (transaction, it->first))
|
||||
{
|
||||
auto next_block (it->second);
|
||||
auto balance = node->ledger.any.block_balance (transaction, it->first);
|
||||
if (balance)
|
||||
{
|
||||
if (balance.value ().number () <= next_block.balance) // balance
|
||||
{
|
||||
lazy_add (next_block.link, next_block.retry_limit); // link
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lazy_add (next_block.link, node->network_params.bootstrap.lazy_retry_limit); // Not confirmed
|
||||
}
|
||||
it = lazy_state_backlog.erase (it);
|
||||
}
|
||||
else
|
||||
{
|
||||
lazy_add (it->first, it->second.retry_limit);
|
||||
++it;
|
||||
}
|
||||
// We don't want to open read transactions for too long
|
||||
++read_count;
|
||||
if (read_count % batch_read_size == 0)
|
||||
{
|
||||
transaction.refresh ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_lazy::lazy_blocks_insert (nano::block_hash const & hash_a)
|
||||
{
|
||||
debug_assert (!mutex.try_lock ());
|
||||
auto inserted (lazy_blocks.insert (std::hash<::nano::block_hash> () (hash_a)));
|
||||
if (inserted.second)
|
||||
{
|
||||
++lazy_blocks_count;
|
||||
debug_assert (lazy_blocks_count > 0);
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_lazy::lazy_blocks_erase (nano::block_hash const & hash_a)
|
||||
{
|
||||
debug_assert (!mutex.try_lock ());
|
||||
auto erased (lazy_blocks.erase (std::hash<::nano::block_hash> () (hash_a)));
|
||||
if (erased)
|
||||
{
|
||||
--lazy_blocks_count;
|
||||
debug_assert (lazy_blocks_count != std::numeric_limits<std::size_t>::max ());
|
||||
}
|
||||
}
|
||||
|
||||
bool nano::bootstrap_attempt_lazy::lazy_blocks_processed (nano::block_hash const & hash_a)
|
||||
{
|
||||
return lazy_blocks.find (std::hash<::nano::block_hash> () (hash_a)) != lazy_blocks.end ();
|
||||
}
|
||||
|
||||
bool nano::bootstrap_attempt_lazy::lazy_processed_or_exists (nano::block_hash const & hash_a)
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool result (false);
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
if (lazy_blocks_processed (hash_a))
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
lock.unlock ();
|
||||
if (node->block_or_pruned_exists (hash_a))
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_lazy::get_information (boost::property_tree::ptree & tree_a)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
tree_a.put ("lazy_blocks", std::to_string (lazy_blocks.size ()));
|
||||
tree_a.put ("lazy_state_backlog", std::to_string (lazy_state_backlog.size ()));
|
||||
tree_a.put ("lazy_balances", std::to_string (lazy_balances.size ()));
|
||||
tree_a.put ("lazy_undefined_links", std::to_string (lazy_undefined_links.size ()));
|
||||
tree_a.put ("lazy_pulls", std::to_string (lazy_pulls.size ()));
|
||||
tree_a.put ("lazy_keys", std::to_string (lazy_keys.size ()));
|
||||
if (!lazy_keys.empty ())
|
||||
{
|
||||
tree_a.put ("lazy_key_1", (*(lazy_keys.begin ())).to_string ());
|
||||
}
|
||||
}
|
||||
|
||||
nano::bootstrap_attempt_wallet::bootstrap_attempt_wallet (std::shared_ptr<nano::node> const & node_a, uint64_t incremental_id_a, std::string id_a) :
|
||||
nano::bootstrap_attempt (node_a, nano::bootstrap_mode::wallet_lazy, incremental_id_a, id_a)
|
||||
{
|
||||
node_a->bootstrap_initiator.notify_listeners (true);
|
||||
}
|
||||
|
||||
nano::bootstrap_attempt_wallet::~bootstrap_attempt_wallet ()
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
node->bootstrap_initiator.notify_listeners (false);
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_wallet::request_pending (nano::unique_lock<nano::mutex> & lock_a)
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
lock_a.unlock ();
|
||||
auto connection_l (node->bootstrap_initiator.connections->connection (shared_from_this ()));
|
||||
lock_a.lock ();
|
||||
if (connection_l && !stopped)
|
||||
{
|
||||
auto account (wallet_accounts.front ());
|
||||
wallet_accounts.pop_front ();
|
||||
++pulling;
|
||||
auto this_l = std::dynamic_pointer_cast<nano::bootstrap_attempt_wallet> (shared_from_this ());
|
||||
// The bulk_pull_account_client destructor attempt to requeue_pull which can cause a deadlock if this is the last reference
|
||||
// Dispatch request in an external thread in case it needs to be destroyed
|
||||
node->background ([connection_l, this_l, account] () {
|
||||
auto client (std::make_shared<nano::bulk_pull_account_client> (connection_l, this_l, account));
|
||||
client->request ();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_wallet::requeue_pending (nano::account const & account_a)
|
||||
{
|
||||
auto account (account_a);
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
wallet_accounts.push_front (account);
|
||||
}
|
||||
condition.notify_all ();
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_wallet::wallet_start (std::deque<nano::account> & accounts_a)
|
||||
{
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
wallet_accounts.swap (accounts_a);
|
||||
}
|
||||
condition.notify_all ();
|
||||
}
|
||||
|
||||
bool nano::bootstrap_attempt_wallet::wallet_finished ()
|
||||
{
|
||||
debug_assert (!mutex.try_lock ());
|
||||
auto running (!stopped);
|
||||
auto more_accounts (!wallet_accounts.empty ());
|
||||
auto still_pulling (pulling > 0);
|
||||
return running && (more_accounts || still_pulling);
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_wallet::run ()
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
debug_assert (started);
|
||||
debug_assert (!node->flags.disable_wallet_bootstrap);
|
||||
node->bootstrap_initiator.connections->populate_connections (false);
|
||||
auto start_time (std::chrono::steady_clock::now ());
|
||||
auto max_time (std::chrono::minutes (10));
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
while (wallet_finished () && std::chrono::steady_clock::now () - start_time < max_time)
|
||||
{
|
||||
if (!wallet_accounts.empty ())
|
||||
{
|
||||
request_pending (lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
condition.wait_for (lock, std::chrono::seconds (1));
|
||||
}
|
||||
}
|
||||
if (!stopped)
|
||||
{
|
||||
node->logger.info (nano::log::type::bootstrap_lazy, "Completed wallet lazy pulls");
|
||||
}
|
||||
lock.unlock ();
|
||||
stop ();
|
||||
condition.notify_all ();
|
||||
}
|
||||
|
||||
std::size_t nano::bootstrap_attempt_wallet::wallet_size ()
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
return wallet_accounts.size ();
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_wallet::get_information (boost::property_tree::ptree & tree_a)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
tree_a.put ("wallet_accounts", std::to_string (wallet_accounts.size ()));
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/node/bootstrap/bootstrap_attempt.hpp>
|
||||
|
||||
#include <boost/multi_index_container.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace mi = boost::multi_index;
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class node;
|
||||
class lazy_state_backlog_item final
|
||||
{
|
||||
public:
|
||||
nano::link link{ 0 };
|
||||
nano::uint128_t balance{ 0 };
|
||||
unsigned retry_limit{ 0 };
|
||||
};
|
||||
|
||||
/**
|
||||
* Lazy bootstrap session. Started with a block hash, this will "trace down" the blocks obtained to find a connection to the ledger.
|
||||
* This attempts to quickly bootstrap a section of the ledger given a hash that's known to be confirmed.
|
||||
*/
|
||||
class bootstrap_attempt_lazy final : public bootstrap_attempt
|
||||
{
|
||||
public:
|
||||
explicit bootstrap_attempt_lazy (std::shared_ptr<nano::node> const & node_a, uint64_t incremental_id_a, std::string const & id_a = "");
|
||||
~bootstrap_attempt_lazy ();
|
||||
bool process_block (std::shared_ptr<nano::block> const &, nano::account const &, uint64_t, nano::bulk_pull::count_t, bool, unsigned) override;
|
||||
void run () override;
|
||||
bool lazy_start (nano::hash_or_account const &);
|
||||
void lazy_add (nano::hash_or_account const &, unsigned);
|
||||
void lazy_add (nano::pull_info const &);
|
||||
void lazy_requeue (nano::block_hash const &, nano::block_hash const &);
|
||||
bool lazy_finished ();
|
||||
bool lazy_has_expired () const;
|
||||
uint32_t lazy_batch_size ();
|
||||
void lazy_pull_flush (nano::unique_lock<nano::mutex> & lock_a);
|
||||
bool process_block_lazy (std::shared_ptr<nano::block> const &, nano::account const &, uint64_t, nano::bulk_pull::count_t, unsigned);
|
||||
void lazy_block_state (std::shared_ptr<nano::block> const &, unsigned);
|
||||
void lazy_block_state_backlog_check (std::shared_ptr<nano::block> const &, nano::block_hash const &);
|
||||
void lazy_backlog_cleanup ();
|
||||
void lazy_blocks_insert (nano::block_hash const &);
|
||||
void lazy_blocks_erase (nano::block_hash const &);
|
||||
bool lazy_blocks_processed (nano::block_hash const &);
|
||||
bool lazy_processed_or_exists (nano::block_hash const &);
|
||||
void get_information (boost::property_tree::ptree &) override;
|
||||
std::unordered_set<std::size_t> lazy_blocks;
|
||||
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<std::pair<nano::hash_or_account, unsigned>> lazy_pulls;
|
||||
std::chrono::steady_clock::time_point lazy_start_time;
|
||||
std::atomic<std::size_t> lazy_blocks_count{ 0 };
|
||||
std::size_t peer_count{ 0 };
|
||||
/** The maximum number of records to be read in while iterating over long lazy containers */
|
||||
static uint64_t constexpr batch_read_size = 256;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wallet bootstrap session. This session will trace down accounts within local wallets to try and bootstrap those blocks first.
|
||||
*/
|
||||
class bootstrap_attempt_wallet final : public bootstrap_attempt
|
||||
{
|
||||
public:
|
||||
explicit bootstrap_attempt_wallet (std::shared_ptr<nano::node> const & node_a, uint64_t incremental_id_a, std::string id_a = "");
|
||||
~bootstrap_attempt_wallet ();
|
||||
void request_pending (nano::unique_lock<nano::mutex> &);
|
||||
void requeue_pending (nano::account const &);
|
||||
void run () override;
|
||||
void wallet_start (std::deque<nano::account> &);
|
||||
bool wallet_finished ();
|
||||
std::size_t wallet_size ();
|
||||
void get_information (boost::property_tree::ptree &) override;
|
||||
std::deque<nano::account> wallet_accounts;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,262 +0,0 @@
|
|||
#include <nano/node/bootstrap/bootstrap_bulk_push.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_frontier.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_legacy.hpp>
|
||||
#include <nano/node/node.hpp>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
nano::bootstrap_attempt_legacy::bootstrap_attempt_legacy (std::shared_ptr<nano::node> const & node_a, uint64_t const incremental_id_a, std::string const & id_a, uint32_t const frontiers_age_a, nano::account const & start_account_a) :
|
||||
nano::bootstrap_attempt (node_a, nano::bootstrap_mode::legacy, incremental_id_a, id_a),
|
||||
frontiers_age (frontiers_age_a),
|
||||
start_account (start_account_a)
|
||||
{
|
||||
node_a->bootstrap_initiator.notify_listeners (true);
|
||||
}
|
||||
|
||||
bool nano::bootstrap_attempt_legacy::consume_future (std::future<bool> & future_a)
|
||||
{
|
||||
bool result;
|
||||
try
|
||||
{
|
||||
result = future_a.get ();
|
||||
}
|
||||
catch (std::future_error &)
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_legacy::stop ()
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
stopped = true;
|
||||
lock.unlock ();
|
||||
condition.notify_all ();
|
||||
lock.lock ();
|
||||
if (auto i = frontiers.lock ())
|
||||
{
|
||||
try
|
||||
{
|
||||
i->promise.set_value (true);
|
||||
}
|
||||
catch (std::future_error &)
|
||||
{
|
||||
}
|
||||
}
|
||||
if (auto i = push.lock ())
|
||||
{
|
||||
try
|
||||
{
|
||||
i->promise.set_value (true);
|
||||
}
|
||||
catch (std::future_error &)
|
||||
{
|
||||
}
|
||||
}
|
||||
lock.unlock ();
|
||||
node->bootstrap_initiator.connections->clear_pulls (incremental_id);
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_legacy::request_push (nano::unique_lock<nano::mutex> & lock_a)
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
bool error (false);
|
||||
lock_a.unlock ();
|
||||
auto connection_l (node->bootstrap_initiator.connections->find_connection (endpoint_frontier_request));
|
||||
lock_a.lock ();
|
||||
if (connection_l)
|
||||
{
|
||||
std::future<bool> future;
|
||||
{
|
||||
auto this_l = std::dynamic_pointer_cast<nano::bootstrap_attempt_legacy> (shared_from_this ());
|
||||
auto client = std::make_shared<nano::bulk_push_client> (connection_l, this_l);
|
||||
client->start ();
|
||||
push = client;
|
||||
future = client->promise.get_future ();
|
||||
}
|
||||
lock_a.unlock ();
|
||||
error = consume_future (future); // This is out of scope of `client' so when the last reference via boost::asio::io_context is lost and the client is destroyed, the future throws an exception.
|
||||
lock_a.lock ();
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_legacy::add_frontier (nano::pull_info const & pull_a)
|
||||
{
|
||||
// Prevent incorrect or malicious pulls with frontier 0 insertion
|
||||
if (!pull_a.head.is_zero ())
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
frontier_pulls.push_back (pull_a);
|
||||
}
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_legacy::add_bulk_push_target (nano::block_hash const & head, nano::block_hash const & end)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
bulk_push_targets.emplace_back (head, end);
|
||||
}
|
||||
|
||||
bool nano::bootstrap_attempt_legacy::request_bulk_push_target (std::pair<nano::block_hash, nano::block_hash> & current_target_a)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
auto empty (bulk_push_targets.empty ());
|
||||
if (!empty)
|
||||
{
|
||||
current_target_a = bulk_push_targets.back ();
|
||||
bulk_push_targets.pop_back ();
|
||||
}
|
||||
return empty;
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_legacy::set_start_account (nano::account const & start_account_a)
|
||||
{
|
||||
// Add last account from frontier request
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
start_account = start_account_a;
|
||||
}
|
||||
|
||||
bool nano::bootstrap_attempt_legacy::request_frontier (nano::unique_lock<nano::mutex> & lock_a, bool first_attempt)
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
auto result (true);
|
||||
lock_a.unlock ();
|
||||
auto connection_l (node->bootstrap_initiator.connections->connection (shared_from_this (), first_attempt));
|
||||
lock_a.lock ();
|
||||
if (connection_l && !stopped)
|
||||
{
|
||||
endpoint_frontier_request = connection_l->channel->get_remote_endpoint ();
|
||||
std::future<bool> future;
|
||||
{
|
||||
auto this_l = std::dynamic_pointer_cast<nano::bootstrap_attempt_legacy> (shared_from_this ());
|
||||
auto client = std::make_shared<nano::frontier_req_client> (connection_l, this_l);
|
||||
client->run (start_account, frontiers_age, node->config.bootstrap_frontier_request_count);
|
||||
frontiers = client;
|
||||
future = client->promise.get_future ();
|
||||
}
|
||||
lock_a.unlock ();
|
||||
result = consume_future (future); // This is out of scope of `client' so when the last reference via boost::asio::io_context is lost and the client is destroyed, the future throws an exception.
|
||||
lock_a.lock ();
|
||||
if (result)
|
||||
{
|
||||
frontier_pulls.clear ();
|
||||
}
|
||||
else
|
||||
{
|
||||
account_count = nano::narrow_cast<unsigned int> (frontier_pulls.size ());
|
||||
// Shuffle pulls
|
||||
release_assert (std::numeric_limits<CryptoPP::word32>::max () > frontier_pulls.size ());
|
||||
if (!frontier_pulls.empty ())
|
||||
{
|
||||
for (auto i = static_cast<CryptoPP::word32> (frontier_pulls.size () - 1); i > 0; --i)
|
||||
{
|
||||
auto k = nano::random_pool::generate_word32 (0, i);
|
||||
std::swap (frontier_pulls[i], frontier_pulls[k]);
|
||||
}
|
||||
}
|
||||
// Add to regular pulls
|
||||
while (!frontier_pulls.empty ())
|
||||
{
|
||||
auto pull (frontier_pulls.front ());
|
||||
lock_a.unlock ();
|
||||
node->bootstrap_initiator.connections->add_pull (pull);
|
||||
lock_a.lock ();
|
||||
++pulling;
|
||||
frontier_pulls.pop_front ();
|
||||
}
|
||||
}
|
||||
if (!result)
|
||||
{
|
||||
node->logger.debug (nano::log::type::bootstrap_legacy, "Completed frontier request, {} out of sync accounts according to {}", account_count.load (), connection_l->channel->to_string ());
|
||||
}
|
||||
else
|
||||
{
|
||||
node->stats.inc (nano::stat::type::error, nano::stat::detail::frontier_req, nano::stat::dir::out);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_legacy::run_start (nano::unique_lock<nano::mutex> & lock_a)
|
||||
{
|
||||
frontiers_received = false;
|
||||
auto frontier_failure (true);
|
||||
uint64_t frontier_attempts (0);
|
||||
while (!stopped && frontier_failure)
|
||||
{
|
||||
++frontier_attempts;
|
||||
frontier_failure = request_frontier (lock_a, frontier_attempts == 1);
|
||||
}
|
||||
frontiers_received = true;
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_legacy::run ()
|
||||
{
|
||||
auto node = this->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
debug_assert (started);
|
||||
debug_assert (!node->flags.disable_legacy_bootstrap);
|
||||
node->bootstrap_initiator.connections->populate_connections (false);
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
run_start (lock);
|
||||
while (still_pulling ())
|
||||
{
|
||||
while (still_pulling ())
|
||||
{
|
||||
// clang-format off
|
||||
condition.wait (lock, [&stopped = stopped, &pulling = pulling] { return stopped || pulling == 0; });
|
||||
}
|
||||
|
||||
// TODO: This check / wait is a heuristic and should be improved.
|
||||
auto wait_start = std::chrono::steady_clock::now ();
|
||||
while (!stopped && node->block_processor.size (nano::block_source::bootstrap_legacy) != 0 && ((std::chrono::steady_clock::now () - wait_start) < std::chrono::seconds{ 10 }))
|
||||
{
|
||||
condition.wait_for (lock, std::chrono::milliseconds{ 100 }, [this, node] { return stopped || node->block_processor.size (nano::block_source::bootstrap_legacy) == 0; });
|
||||
}
|
||||
|
||||
if (start_account.number () != std::numeric_limits<nano::uint256_t>::max ())
|
||||
{
|
||||
node->logger.debug(nano::log::type::bootstrap_legacy, "Requesting new frontiers after: {}", start_account.to_account ());
|
||||
|
||||
// Requesting new frontiers
|
||||
run_start (lock);
|
||||
}
|
||||
}
|
||||
if (!stopped)
|
||||
{
|
||||
node->logger.debug(nano::log::type::bootstrap_legacy, "Completed legacy pulls");
|
||||
|
||||
if (!node->flags.disable_bootstrap_bulk_push_client)
|
||||
{
|
||||
request_push (lock);
|
||||
}
|
||||
}
|
||||
lock.unlock ();
|
||||
stop ();
|
||||
condition.notify_all ();
|
||||
}
|
||||
|
||||
void nano::bootstrap_attempt_legacy::get_information (boost::property_tree::ptree & tree_a)
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||
tree_a.put ("frontier_pulls", std::to_string (frontier_pulls.size ()));
|
||||
tree_a.put ("frontiers_received", static_cast<bool> (frontiers_received));
|
||||
tree_a.put ("frontiers_age", std::to_string (frontiers_age));
|
||||
tree_a.put ("last_account", start_account.to_account ());
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/node/bootstrap/bootstrap_attempt.hpp>
|
||||
|
||||
#include <boost/property_tree/ptree_fwd.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class node;
|
||||
|
||||
/**
|
||||
* Legacy bootstrap session. This is made up of 3 phases: frontier requests, bootstrap pulls, bootstrap pushes.
|
||||
*/
|
||||
class bootstrap_attempt_legacy : public bootstrap_attempt
|
||||
{
|
||||
public:
|
||||
explicit bootstrap_attempt_legacy (std::shared_ptr<nano::node> const & node_a, uint64_t const incremental_id_a, std::string const & id_a, uint32_t const frontiers_age_a, nano::account const & start_account_a);
|
||||
void run () override;
|
||||
bool consume_future (std::future<bool> &);
|
||||
void stop () override;
|
||||
bool request_frontier (nano::unique_lock<nano::mutex> &, bool = false);
|
||||
void request_push (nano::unique_lock<nano::mutex> &);
|
||||
void add_frontier (nano::pull_info const &);
|
||||
void add_bulk_push_target (nano::block_hash const &, nano::block_hash const &);
|
||||
bool request_bulk_push_target (std::pair<nano::block_hash, nano::block_hash> &);
|
||||
void set_start_account (nano::account const &);
|
||||
void run_start (nano::unique_lock<nano::mutex> &);
|
||||
void get_information (boost::property_tree::ptree &) override;
|
||||
nano::tcp_endpoint endpoint_frontier_request;
|
||||
std::weak_ptr<nano::frontier_req_client> frontiers;
|
||||
std::weak_ptr<nano::bulk_push_client> push;
|
||||
std::deque<nano::pull_info> frontier_pulls;
|
||||
std::vector<std::pair<nano::block_hash, nano::block_hash>> bulk_push_targets;
|
||||
nano::account start_account{};
|
||||
std::atomic<unsigned> account_count{ 0 };
|
||||
uint32_t frontiers_age;
|
||||
};
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@
|
|||
#include <nano/lib/stats_sinks.hpp>
|
||||
#include <nano/lib/timer.hpp>
|
||||
#include <nano/node/active_elections.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_lazy.hpp>
|
||||
#include <nano/node/bootstrap_ascending/service.hpp>
|
||||
#include <nano/node/common.hpp>
|
||||
#include <nano/node/confirming_set.hpp>
|
||||
|
|
@ -250,7 +249,6 @@ nano::account_info nano::json_handler::account_info_impl (secure::transaction co
|
|||
if (!info)
|
||||
{
|
||||
ec = nano::error_common::account_not_found;
|
||||
node.bootstrap_initiator.bootstrap_lazy (account_a, false, account_a.to_account ());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1804,16 +1802,7 @@ void nano::json_handler::bootstrap ()
|
|||
uint16_t port;
|
||||
if (!nano::parse_port (port_text, port))
|
||||
{
|
||||
if (!node.flags.disable_legacy_bootstrap)
|
||||
{
|
||||
std::string bootstrap_id (request.get<std::string> ("id", ""));
|
||||
node.bootstrap_initiator.bootstrap (nano::endpoint (address, port), true, bootstrap_id);
|
||||
response_l.put ("success", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
ec = nano::error_rpc::disabled_bootstrap_legacy;
|
||||
}
|
||||
ec = nano::error_rpc::disabled_bootstrap_legacy;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1830,22 +1819,7 @@ void nano::json_handler::bootstrap ()
|
|||
void nano::json_handler::bootstrap_any ()
|
||||
{
|
||||
bool const force = request.get<bool> ("force", false);
|
||||
if (!node.flags.disable_legacy_bootstrap)
|
||||
{
|
||||
nano::account start_account{};
|
||||
boost::optional<std::string> account_text (request.get_optional<std::string> ("account"));
|
||||
if (account_text.is_initialized ())
|
||||
{
|
||||
start_account = account_impl (account_text.get ());
|
||||
}
|
||||
std::string bootstrap_id (request.get<std::string> ("id", ""));
|
||||
node.bootstrap_initiator.bootstrap (force, bootstrap_id, std::numeric_limits<uint32_t>::max (), start_account);
|
||||
response_l.put ("success", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
ec = nano::error_rpc::disabled_bootstrap_legacy;
|
||||
}
|
||||
ec = nano::error_rpc::disabled_bootstrap_legacy;
|
||||
response_errors ();
|
||||
}
|
||||
|
||||
|
|
@ -1855,19 +1829,7 @@ void nano::json_handler::bootstrap_lazy ()
|
|||
bool const force = request.get<bool> ("force", false);
|
||||
if (!ec)
|
||||
{
|
||||
if (!node.flags.disable_lazy_bootstrap)
|
||||
{
|
||||
auto existed (node.bootstrap_initiator.current_lazy_attempt () != nullptr);
|
||||
std::string bootstrap_id (request.get<std::string> ("id", ""));
|
||||
auto key_inserted (node.bootstrap_initiator.bootstrap_lazy (hash, force, bootstrap_id));
|
||||
bool started = !existed && key_inserted;
|
||||
response_l.put ("started", started ? "1" : "0");
|
||||
response_l.put ("key_inserted", key_inserted ? "1" : "0");
|
||||
}
|
||||
else
|
||||
{
|
||||
ec = nano::error_rpc::disabled_bootstrap_lazy;
|
||||
}
|
||||
ec = nano::error_rpc::disabled_bootstrap_lazy;
|
||||
}
|
||||
response_errors ();
|
||||
}
|
||||
|
|
@ -1877,39 +1839,7 @@ void nano::json_handler::bootstrap_lazy ()
|
|||
*/
|
||||
void nano::json_handler::bootstrap_status ()
|
||||
{
|
||||
auto attempts_count (node.bootstrap_initiator.attempts.size ());
|
||||
response_l.put ("bootstrap_threads", std::to_string (node.config.bootstrap_initiator_threads));
|
||||
response_l.put ("running_attempts_count", std::to_string (attempts_count));
|
||||
response_l.put ("total_attempts_count", std::to_string (node.bootstrap_initiator.attempts.incremental));
|
||||
boost::property_tree::ptree connections;
|
||||
{
|
||||
nano::lock_guard<nano::mutex> connections_lock (node.bootstrap_initiator.connections->mutex);
|
||||
connections.put ("clients", std::to_string (node.bootstrap_initiator.connections->clients.size ()));
|
||||
connections.put ("connections", std::to_string (node.bootstrap_initiator.connections->connections_count));
|
||||
connections.put ("idle", std::to_string (node.bootstrap_initiator.connections->idle.size ()));
|
||||
connections.put ("target_connections", std::to_string (node.bootstrap_initiator.connections->target_connections (node.bootstrap_initiator.connections->pulls.size (), attempts_count)));
|
||||
connections.put ("pulls", std::to_string (node.bootstrap_initiator.connections->pulls.size ()));
|
||||
}
|
||||
response_l.add_child ("connections", connections);
|
||||
boost::property_tree::ptree attempts;
|
||||
{
|
||||
nano::lock_guard<nano::mutex> attempts_lock (node.bootstrap_initiator.attempts.bootstrap_attempts_mutex);
|
||||
for (auto i : node.bootstrap_initiator.attempts.attempts)
|
||||
{
|
||||
boost::property_tree::ptree entry;
|
||||
auto & attempt (i.second);
|
||||
entry.put ("id", attempt->id);
|
||||
entry.put ("mode", attempt->mode_text ());
|
||||
entry.put ("started", static_cast<bool> (attempt->started));
|
||||
entry.put ("pulling", std::to_string (attempt->pulling));
|
||||
entry.put ("total_blocks", std::to_string (attempt->total_blocks));
|
||||
entry.put ("requeued_pulls", std::to_string (attempt->requeued_pulls));
|
||||
attempt->get_information (entry);
|
||||
entry.put ("duration", std::chrono::duration_cast<std::chrono::seconds> (std::chrono::steady_clock::now () - attempt->attempt_start).count ());
|
||||
attempts.push_back (std::make_pair ("", entry));
|
||||
}
|
||||
}
|
||||
response_l.add_child ("attempts", attempts);
|
||||
// TODO: Bootstrap status for ascending bootstrap
|
||||
response_errors ();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -108,7 +108,6 @@ nano::node::node (std::shared_ptr<boost::asio::io_context> io_ctx_a, std::filesy
|
|||
network (*this, config.peering_port.has_value () ? *config.peering_port : 0),
|
||||
telemetry_impl{ std::make_unique<nano::telemetry> (flags, *this, network, observers, network_params, stats) },
|
||||
telemetry{ *telemetry_impl },
|
||||
bootstrap_initiator (*this),
|
||||
bootstrap_server{ config.bootstrap_server, store, ledger, network_params.network, stats },
|
||||
// BEWARE: `bootstrap` takes `network.port` instead of `config.peering_port` because when the user doesn't specify
|
||||
// a peering port and wants the OS to pick one, the picking happens when `network` gets initialized
|
||||
|
|
@ -564,10 +563,6 @@ void nano::node::start ()
|
|||
network.start ();
|
||||
message_processor.start ();
|
||||
|
||||
if (!flags.disable_legacy_bootstrap && !flags.disable_ongoing_bootstrap)
|
||||
{
|
||||
ongoing_bootstrap ();
|
||||
}
|
||||
if (flags.enable_pruning)
|
||||
{
|
||||
auto this_l (shared ());
|
||||
|
|
@ -608,14 +603,6 @@ void nano::node::start ()
|
|||
{
|
||||
search_receivable_all ();
|
||||
}
|
||||
if (!flags.disable_wallet_bootstrap)
|
||||
{
|
||||
// Delay to start wallet lazy bootstrap
|
||||
auto this_l (shared ());
|
||||
workers.post_delayed (std::chrono::minutes (1), [this_l] () {
|
||||
this_l->bootstrap_wallet ();
|
||||
});
|
||||
}
|
||||
// Start port mapping if external address is not defined and TCP ports are enabled
|
||||
if (config.external_address == boost::asio::ip::address_v6::any ().to_string () && tcp_enabled)
|
||||
{
|
||||
|
|
@ -684,7 +671,6 @@ void nano::node::stop ()
|
|||
telemetry.stop ();
|
||||
websocket.stop ();
|
||||
bootstrap_server.stop ();
|
||||
bootstrap_initiator.stop ();
|
||||
port_mapping.stop ();
|
||||
wallets.stop ();
|
||||
stats.stop ();
|
||||
|
|
@ -777,69 +763,6 @@ void nano::node::long_inactivity_cleanup ()
|
|||
}
|
||||
}
|
||||
|
||||
void nano::node::ongoing_bootstrap ()
|
||||
{
|
||||
auto next_wakeup = network_params.network.bootstrap_interval;
|
||||
if (warmed_up < 3)
|
||||
{
|
||||
// Re-attempt bootstrapping more aggressively on startup
|
||||
next_wakeup = std::chrono::seconds (5);
|
||||
if (!bootstrap_initiator.in_progress () && !network.empty ())
|
||||
{
|
||||
++warmed_up;
|
||||
}
|
||||
}
|
||||
if (network_params.network.is_dev_network () && flags.bootstrap_interval != 0)
|
||||
{
|
||||
// For test purposes allow faster automatic bootstraps
|
||||
next_wakeup = std::chrono::seconds (flags.bootstrap_interval);
|
||||
++warmed_up;
|
||||
}
|
||||
// Differential bootstrap with max age (75% of all legacy attempts)
|
||||
uint32_t frontiers_age (std::numeric_limits<uint32_t>::max ());
|
||||
auto bootstrap_weight_reached (ledger.block_count () >= ledger.bootstrap_weight_max_blocks);
|
||||
auto previous_bootstrap_count (stats.count (nano::stat::type::bootstrap, nano::stat::detail::initiate, nano::stat::dir::out) + stats.count (nano::stat::type::bootstrap, nano::stat::detail::initiate_legacy_age, nano::stat::dir::out));
|
||||
/*
|
||||
- Maximum value for 25% of attempts or if block count is below preconfigured value (initial bootstrap not finished)
|
||||
- Node shutdown time minus 1 hour for start attempts (warm up)
|
||||
- Default age value otherwise (1 day for live network, 1 hour for beta)
|
||||
*/
|
||||
if (bootstrap_weight_reached)
|
||||
{
|
||||
if (warmed_up < 3)
|
||||
{
|
||||
// Find last online weight sample (last active time for node)
|
||||
uint64_t last_sample_time (0);
|
||||
{
|
||||
auto transaction = store.tx_begin_read ();
|
||||
auto last_record = store.online_weight.rbegin (transaction);
|
||||
if (last_record != store.online_weight.end (transaction))
|
||||
{
|
||||
last_sample_time = last_record->first;
|
||||
}
|
||||
}
|
||||
uint64_t time_since_last_sample = std::chrono::duration_cast<std::chrono::seconds> (std::chrono::system_clock::now ().time_since_epoch ()).count () - static_cast<uint64_t> (last_sample_time / std::pow (10, 9)); // Nanoseconds to seconds
|
||||
if (time_since_last_sample + 60 * 60 < std::numeric_limits<uint32_t>::max ())
|
||||
{
|
||||
frontiers_age = std::max<uint32_t> (static_cast<uint32_t> (time_since_last_sample + 60 * 60), network_params.bootstrap.default_frontiers_age_seconds);
|
||||
}
|
||||
}
|
||||
else if (previous_bootstrap_count % 4 != 0)
|
||||
{
|
||||
frontiers_age = network_params.bootstrap.default_frontiers_age_seconds;
|
||||
}
|
||||
}
|
||||
// Bootstrap and schedule for next attempt
|
||||
bootstrap_initiator.bootstrap (false, boost::str (boost::format ("auto_bootstrap_%1%") % previous_bootstrap_count), frontiers_age);
|
||||
std::weak_ptr<nano::node> node_w (shared_from_this ());
|
||||
workers.post_delayed (next_wakeup, [node_w] () {
|
||||
if (auto node_l = node_w.lock ())
|
||||
{
|
||||
node_l->ongoing_bootstrap ();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void nano::node::backup_wallet ()
|
||||
{
|
||||
auto transaction (wallets.tx_begin_read ());
|
||||
|
|
@ -870,29 +793,6 @@ void nano::node::search_receivable_all ()
|
|||
});
|
||||
}
|
||||
|
||||
void nano::node::bootstrap_wallet ()
|
||||
{
|
||||
std::deque<nano::account> accounts;
|
||||
{
|
||||
nano::lock_guard<nano::mutex> lock{ wallets.mutex };
|
||||
auto const transaction (wallets.tx_begin_read ());
|
||||
for (auto i (wallets.items.begin ()), n (wallets.items.end ()); i != n && accounts.size () < 128; ++i)
|
||||
{
|
||||
auto & wallet (*i->second);
|
||||
nano::lock_guard<std::recursive_mutex> wallet_lock{ wallet.store.mutex };
|
||||
for (auto j (wallet.store.begin (transaction)), m (wallet.store.end (transaction)); j != m && accounts.size () < 128; ++j)
|
||||
{
|
||||
nano::account account (j->first);
|
||||
accounts.push_back (account);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!accounts.empty ())
|
||||
{
|
||||
bootstrap_initiator.bootstrap_wallet (accounts);
|
||||
}
|
||||
}
|
||||
|
||||
bool nano::node::collect_ledger_pruning_targets (std::deque<nano::block_hash> & pruning_targets_a, nano::account & last_account_a, uint64_t const batch_read_size_a, uint64_t const max_depth_a, uint64_t const cutoff_time_a)
|
||||
{
|
||||
uint64_t read_operations (0);
|
||||
|
|
@ -1285,7 +1185,6 @@ nano::container_info nano::node::container_info () const
|
|||
info.add ("work", work.container_info ());
|
||||
info.add ("ledger", ledger.container_info ());
|
||||
info.add ("active", active.container_info ());
|
||||
info.add ("bootstrap_initiator", bootstrap_initiator.container_info ());
|
||||
info.add ("tcp_listener", tcp_listener.container_info ());
|
||||
info.add ("network", network.container_info ());
|
||||
info.add ("telemetry", telemetry.container_info ());
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@
|
|||
#include <nano/lib/stats.hpp>
|
||||
#include <nano/lib/work.hpp>
|
||||
#include <nano/node/blockprocessor.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_attempt.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_server.hpp>
|
||||
#include <nano/node/distributed_work_factory.hpp>
|
||||
#include <nano/node/epoch_upgrader.hpp>
|
||||
|
|
@ -104,10 +102,8 @@ public:
|
|||
std::pair<nano::uint128_t, nano::uint128_t> balance_pending (nano::account const &, bool only_confirmed);
|
||||
nano::uint128_t weight (nano::account const &);
|
||||
nano::uint128_t minimum_principal_weight ();
|
||||
void ongoing_bootstrap ();
|
||||
void backup_wallet ();
|
||||
void search_receivable_all ();
|
||||
void bootstrap_wallet ();
|
||||
bool collect_ledger_pruning_targets (std::deque<nano::block_hash> &, nano::account &, uint64_t const, uint64_t const, uint64_t const);
|
||||
void ledger_pruning (uint64_t const, bool);
|
||||
void ongoing_ledger_pruning ();
|
||||
|
|
@ -180,7 +176,6 @@ public:
|
|||
nano::network network;
|
||||
std::unique_ptr<nano::telemetry> telemetry_impl;
|
||||
nano::telemetry & telemetry;
|
||||
nano::bootstrap_initiator bootstrap_initiator;
|
||||
nano::bootstrap_server bootstrap_server;
|
||||
std::unique_ptr<nano::transport::tcp_listener> tcp_listener_impl;
|
||||
nano::transport::tcp_listener & tcp_listener;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
#include <nano/node/bootstrap/bootstrap_bulk_push.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_frontier.hpp>
|
||||
#include <nano/node/messages.hpp>
|
||||
#include <nano/node/node.hpp>
|
||||
#include <nano/node/transport/message_deserializer.hpp>
|
||||
|
|
@ -516,79 +514,26 @@ nano::transport::tcp_server::bootstrap_message_visitor::bootstrap_message_visito
|
|||
|
||||
void nano::transport::tcp_server::bootstrap_message_visitor::bulk_pull (const nano::bulk_pull & message)
|
||||
{
|
||||
auto node = server->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (node->flags.disable_bootstrap_bulk_pull_server)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
node->bootstrap_workers.post ([server = server, message = message] () {
|
||||
// TODO: Add completion callback to bulk pull server
|
||||
// TODO: There should be no need to re-copy message as unique pointer, refactor those bulk/frontier pull/push servers
|
||||
auto bulk_pull_server = std::make_shared<nano::bulk_pull_server> (server, std::make_unique<nano::bulk_pull> (message));
|
||||
bulk_pull_server->send_next ();
|
||||
});
|
||||
|
||||
processed = true;
|
||||
// Ignored since V28
|
||||
// TODO: Abort connection?
|
||||
}
|
||||
|
||||
void nano::transport::tcp_server::bootstrap_message_visitor::bulk_pull_account (const nano::bulk_pull_account & message)
|
||||
{
|
||||
auto node = server->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (node->flags.disable_bootstrap_bulk_pull_server)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
node->bootstrap_workers.post ([server = server, message = message] () {
|
||||
// TODO: Add completion callback to bulk pull server
|
||||
// TODO: There should be no need to re-copy message as unique pointer, refactor those bulk/frontier pull/push servers
|
||||
auto bulk_pull_account_server = std::make_shared<nano::bulk_pull_account_server> (server, std::make_unique<nano::bulk_pull_account> (message));
|
||||
bulk_pull_account_server->send_frontier ();
|
||||
});
|
||||
|
||||
processed = true;
|
||||
// Ignored since V28
|
||||
// TODO: Abort connection?
|
||||
}
|
||||
|
||||
void nano::transport::tcp_server::bootstrap_message_visitor::bulk_push (const nano::bulk_push &)
|
||||
{
|
||||
auto node = server->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
node->bootstrap_workers.post ([server = server] () {
|
||||
// TODO: Add completion callback to bulk pull server
|
||||
auto bulk_push_server = std::make_shared<nano::bulk_push_server> (server);
|
||||
bulk_push_server->throttled_receive ();
|
||||
});
|
||||
|
||||
processed = true;
|
||||
// Ignored since V28
|
||||
// TODO: Abort connection?
|
||||
}
|
||||
|
||||
void nano::transport::tcp_server::bootstrap_message_visitor::frontier_req (const nano::frontier_req & message)
|
||||
{
|
||||
auto node = server->node.lock ();
|
||||
if (!node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
node->bootstrap_workers.post ([server = server, message = message] () {
|
||||
// TODO: There should be no need to re-copy message as unique pointer, refactor those bulk/frontier pull/push servers
|
||||
auto response = std::make_shared<nano::frontier_req_server> (server, std::make_unique<nano::frontier_req> (message));
|
||||
response->send_next ();
|
||||
});
|
||||
|
||||
processed = true;
|
||||
// Ignored since V28
|
||||
// TODO: Abort connection?
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -399,11 +399,6 @@ nano_qt::import::import (nano_qt::wallet & wallet_a) :
|
|||
{
|
||||
this->wallet.account = this->wallet.wallet_m->change_seed (transaction, seed_l);
|
||||
successful = true;
|
||||
// Pending check for accounts to restore if bootstrap is in progress
|
||||
if (this->wallet.node.bootstrap_initiator.in_progress ())
|
||||
{
|
||||
this->wallet.needs_deterministic_restore = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1379,31 +1374,6 @@ void nano_qt::wallet::start ()
|
|||
}));
|
||||
}
|
||||
});
|
||||
node.bootstrap_initiator.add_observer ([this_w] (bool active_a) {
|
||||
if (auto this_l = this_w.lock ())
|
||||
{
|
||||
this_l->application.postEvent (&this_l->processor, new eventloop_event ([this_w, active_a] () {
|
||||
if (auto this_l = this_w.lock ())
|
||||
{
|
||||
if (active_a)
|
||||
{
|
||||
this_l->active_status.insert (nano_qt::status_types::synchronizing);
|
||||
}
|
||||
else
|
||||
{
|
||||
this_l->active_status.erase (nano_qt::status_types::synchronizing);
|
||||
// Check for accounts to restore
|
||||
if (this_l->needs_deterministic_restore)
|
||||
{
|
||||
this_l->needs_deterministic_restore = false;
|
||||
auto transaction (this_l->wallet_m->wallets.tx_begin_write ());
|
||||
this_l->wallet_m->deterministic_restore (transaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
});
|
||||
node.work.work_observers.add ([this_w] (bool working) {
|
||||
if (auto this_l = this_w.lock ())
|
||||
{
|
||||
|
|
@ -1785,7 +1755,6 @@ nano_qt::advanced_actions::advanced_actions (nano_qt::wallet & wallet_a) :
|
|||
bootstrap_label (new QLabel ("IPV6:port \"::ffff:192.168.0.1:7075\"")),
|
||||
peer_count_label (new QLabel ("")),
|
||||
bootstrap_line (new QLineEdit),
|
||||
peers_bootstrap (new QPushButton ("Initiate Bootstrap")),
|
||||
peers_refresh (new QPushButton ("Refresh")),
|
||||
peers_back (new QPushButton ("Back")),
|
||||
wallet (wallet_a)
|
||||
|
|
@ -1827,7 +1796,6 @@ nano_qt::advanced_actions::advanced_actions (nano_qt::wallet & wallet_a) :
|
|||
peer_summary_layout->addWidget (peer_count_label);
|
||||
peers_layout->addLayout (peer_summary_layout);
|
||||
peers_layout->addWidget (bootstrap_line);
|
||||
peers_layout->addWidget (peers_bootstrap);
|
||||
peers_layout->addWidget (peers_refresh);
|
||||
peers_layout->addWidget (peers_back);
|
||||
peers_layout->setContentsMargins (0, 0, 0, 0);
|
||||
|
|
@ -1887,20 +1855,6 @@ nano_qt::advanced_actions::advanced_actions (nano_qt::wallet & wallet_a) :
|
|||
QObject::connect (peers_back, &QPushButton::released, [this] () {
|
||||
this->wallet.pop_main_stack ();
|
||||
});
|
||||
QObject::connect (peers_bootstrap, &QPushButton::released, [this] () {
|
||||
nano::endpoint endpoint;
|
||||
auto error (nano::parse_endpoint (bootstrap_line->text ().toStdString (), endpoint));
|
||||
if (!error)
|
||||
{
|
||||
show_line_ok (*bootstrap_line);
|
||||
bootstrap_line->clear ();
|
||||
this->wallet.node.bootstrap_initiator.bootstrap (endpoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
show_line_error (*bootstrap_line);
|
||||
}
|
||||
});
|
||||
QObject::connect (peers_refresh, &QPushButton::released, [this] () {
|
||||
refresh_peers ();
|
||||
});
|
||||
|
|
@ -1914,7 +1868,6 @@ nano_qt::advanced_actions::advanced_actions (nano_qt::wallet & wallet_a) :
|
|||
std::thread ([this] { this->wallet.wallet_m->search_receivable (this->wallet.wallet_m->wallets.tx_begin_read ()); }).detach ();
|
||||
});
|
||||
QObject::connect (bootstrap, &QPushButton::released, [this] () {
|
||||
std::thread ([this] { this->wallet.node.bootstrap_initiator.bootstrap (); }).detach ();
|
||||
});
|
||||
QObject::connect (create_block, &QPushButton::released, [this] () {
|
||||
this->wallet.push_main_stack (this->wallet.block_creation.window);
|
||||
|
|
|
|||
|
|
@ -85,7 +85,6 @@ public:
|
|||
QLabel * bootstrap_label;
|
||||
QLabel * peer_count_label;
|
||||
QLineEdit * bootstrap_line;
|
||||
QPushButton * peers_bootstrap;
|
||||
QPushButton * peers_refresh;
|
||||
QPushButton * peers_back;
|
||||
|
||||
|
|
|
|||
|
|
@ -905,38 +905,6 @@ TEST (wallet, import_locked)
|
|||
system.wallet (0)->store.seed (seed3, transaction);
|
||||
ASSERT_EQ (seed1, seed3);
|
||||
}
|
||||
// DISABLED: this always fails
|
||||
TEST (wallet, DISABLED_synchronizing)
|
||||
{
|
||||
nano_qt::eventloop_processor processor;
|
||||
nano::test::system system0 (1);
|
||||
nano::test::system system1 (1);
|
||||
auto key1 (system0.wallet (0)->deterministic_insert ());
|
||||
auto wallet (std::make_shared<nano_qt::wallet> (*test_application, processor, *system0.nodes[0], system0.wallet (0), key1));
|
||||
wallet->start ();
|
||||
{
|
||||
auto transaction = system1.nodes[0]->ledger.tx_begin_write ();
|
||||
auto latest (system1.nodes[0]->ledger.any.account_head (transaction, nano::dev::genesis_key.pub));
|
||||
auto send = std::make_shared<nano::send_block> (latest, key1, 0, nano::dev::genesis_key.prv, nano::dev::genesis_key.pub, *system1.work.generate (latest));
|
||||
system1.nodes[0]->ledger.process (transaction, send);
|
||||
}
|
||||
ASSERT_EQ (0, wallet->active_status.active.count (nano_qt::status_types::synchronizing));
|
||||
system0.nodes[0]->bootstrap_initiator.bootstrap (system1.nodes[0]->network.endpoint ());
|
||||
system1.deadline_set (10s);
|
||||
while (wallet->active_status.active.count (nano_qt::status_types::synchronizing) == 0)
|
||||
{
|
||||
ASSERT_NO_ERROR (system0.poll ());
|
||||
ASSERT_NO_ERROR (system1.poll ());
|
||||
test_application->processEvents ();
|
||||
}
|
||||
system1.deadline_set (25s);
|
||||
while (wallet->active_status.active.count (nano_qt::status_types::synchronizing) == 1)
|
||||
{
|
||||
ASSERT_NO_ERROR (system0.poll ());
|
||||
ASSERT_NO_ERROR (system1.poll ());
|
||||
test_application->processEvents ();
|
||||
}
|
||||
}
|
||||
|
||||
TEST (wallet, epoch_2_validation)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6471,59 +6471,6 @@ TEST (rpc, epoch_upgrade_multithreaded)
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: This test is testing legacy bootstrap, the current behavior is different
|
||||
TEST (rpc, DISABLED_account_lazy_start)
|
||||
{
|
||||
nano::test::system system{};
|
||||
nano::node_flags node_flags{};
|
||||
node_flags.disable_legacy_bootstrap = true;
|
||||
auto node1 = system.add_node (node_flags);
|
||||
nano::keypair key{};
|
||||
nano::block_builder builder;
|
||||
// Generating test chain
|
||||
auto send1 = builder
|
||||
.state ()
|
||||
.account (nano::dev::genesis_key.pub)
|
||||
.previous (nano::dev::genesis->hash ())
|
||||
.representative (nano::dev::genesis_key.pub)
|
||||
.balance (nano::dev::constants.genesis_amount - nano::Knano_ratio)
|
||||
.link (key.pub)
|
||||
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
|
||||
.work (*system.work.generate (nano::dev::genesis->hash ()))
|
||||
.build ();
|
||||
ASSERT_EQ (nano::block_status::progress, node1->process (send1));
|
||||
auto open = builder
|
||||
.open ()
|
||||
.source (send1->hash ())
|
||||
.representative (key.pub)
|
||||
.account (key.pub)
|
||||
.sign (key.prv, key.pub)
|
||||
.work (*system.work.generate (key.pub))
|
||||
.build ();
|
||||
ASSERT_EQ (nano::block_status::progress, node1->process (open));
|
||||
|
||||
// Start lazy bootstrap with account
|
||||
nano::node_config node_config = system.default_config ();
|
||||
node_config.ipc_config.transport_tcp.enabled = true;
|
||||
node_config.ipc_config.transport_tcp.port = system.get_available_port ();
|
||||
auto node2 = system.add_node (node_config, node_flags);
|
||||
nano::test::establish_tcp (system, *node2, node1->network.endpoint ());
|
||||
auto const rpc_ctx = add_rpc (system, node2);
|
||||
boost::property_tree::ptree request;
|
||||
request.put ("action", "account_info");
|
||||
request.put ("account", key.pub.to_account ());
|
||||
auto response = wait_response (system, rpc_ctx, request);
|
||||
boost::optional<std::string> account_error{ response.get_optional<std::string> ("error") };
|
||||
ASSERT_TRUE (account_error.is_initialized ());
|
||||
|
||||
// Check processed blocks
|
||||
ASSERT_TIMELY (10s, !node2->bootstrap_initiator.in_progress ());
|
||||
|
||||
// needs timed assert because the writing (put) operation is done by a different
|
||||
// thread, it might not get done before DB get operation.
|
||||
ASSERT_TIMELY (15s, nano::test::block_or_pruned_all_exists (*node2, { send1, open }));
|
||||
}
|
||||
|
||||
TEST (rpc, receive)
|
||||
{
|
||||
nano::test::system system;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue