Remove bootstrap restarting code (#3149)

* This removes bootstrap restarting code as it is ineffective and can cause unnecessary restarting of the bootstrap process.

* bulk.genesis_pruning test improvements

* Fix TSAN warning in test active_transactions.confirm_frontier

* Remove now unused variables and options

Co-authored-by: Sergey Kroshnin <sergiysw@gmail.com>
This commit is contained in:
clemahieu 2021-03-19 15:18:01 +01:00 committed by GitHub
commit 9112636c22
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 15 additions and 399 deletions

View file

@ -88,17 +88,21 @@ TEST (active_transactions, confirm_frontier)
}
nano::genesis genesis;
auto send = nano::send_block_builder ()
nano::state_block_builder builder;
auto send = builder
.account (nano::dev_genesis_key.pub)
.previous (genesis.hash ())
.destination (nano::public_key ())
.representative (nano::dev_genesis_key.pub)
.balance (nano::genesis_amount - 100)
.link (nano::public_key ())
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
.work (*system.work.generate (genesis.hash ()))
.build_shared ();
auto send_copy = builder.make_block ().from (*send).build_shared ();
ASSERT_EQ (nano::process_result::progress, node1.process (*send).code);
node1.confirmation_height_processor.add (send);
ASSERT_TIMELY (5s, node1.ledger.block_confirmed (node1.store.tx_begin_read (), send->hash ()));
ASSERT_EQ (nano::process_result::progress, node2.process (*send).code);
ASSERT_EQ (nano::process_result::progress, node2.process (*send_copy).code);
ASSERT_TIMELY (5s, !node2.active.empty ());
// Save election to check request count afterwards
auto election2 = node2.active.election (send->qualified_root ());

View file

@ -354,157 +354,6 @@ TEST (bootstrap_processor, DISABLED_pull_requeue_network_error)
ASSERT_EQ (0, node1->stats.count (nano::stat::type::bootstrap, nano::stat::detail::bulk_pull_failed_account, nano::stat::dir::in)); // Requeue is not increasing failed attempts
}
TEST (bootstrap_processor, frontiers_unconfirmed)
{
nano::system system;
nano::node_config node_config (nano::get_available_port (), system.logging);
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
node_config.tcp_io_timeout = std::chrono::seconds (2);
nano::node_flags node_flags;
node_flags.disable_bootstrap_bulk_pull_server = true;
node_flags.disable_bootstrap_bulk_push_client = true;
node_flags.disable_legacy_bootstrap = true;
node_flags.disable_lazy_bootstrap = true;
node_flags.disable_wallet_bootstrap = true;
node_flags.disable_rep_crawler = true;
auto node1 = system.add_node (node_config, node_flags);
nano::genesis genesis;
nano::keypair key1, key2;
// Generating invalid chain
auto send1 (std::make_shared<nano::state_block> (nano::dev_genesis_key.pub, genesis.hash (), nano::dev_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key1.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (genesis.hash ())));
ASSERT_EQ (nano::process_result::progress, node1->process (*send1).code);
auto send2 (std::make_shared<nano::state_block> (nano::dev_genesis_key.pub, send1->hash (), nano::dev_genesis_key.pub, nano::genesis_amount - 2 * nano::Gxrb_ratio, key2.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (send1->hash ())));
ASSERT_EQ (nano::process_result::progress, node1->process (*send2).code);
auto open1 (std::make_shared<nano::state_block> (key1.pub, 0, key1.pub, nano::Gxrb_ratio, send1->hash (), key1.prv, key1.pub, *system.work.generate (key1.pub)));
ASSERT_EQ (nano::process_result::progress, node1->process (*open1).code);
auto open2 (std::make_shared<nano::state_block> (key2.pub, 0, key2.pub, nano::Gxrb_ratio, send2->hash (), key2.prv, key2.pub, *system.work.generate (key2.pub)));
ASSERT_EQ (nano::process_result::progress, node1->process (*open2).code);
node_config.peering_port = nano::get_available_port ();
node_flags.disable_bootstrap_bulk_pull_server = false;
node_flags.disable_rep_crawler = false;
auto node2 = system.add_node (node_config, node_flags);
// Generating valid chain
auto send3 (std::make_shared<nano::state_block> (nano::dev_genesis_key.pub, genesis.hash (), nano::dev_genesis_key.pub, nano::genesis_amount - nano::xrb_ratio, key1.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (genesis.hash ())));
ASSERT_EQ (nano::process_result::progress, node2->process (*send3).code);
auto open3 (std::make_shared<nano::state_block> (key1.pub, 0, key1.pub, nano::xrb_ratio, send3->hash (), key1.prv, key1.pub, *system.work.generate (key1.pub)));
ASSERT_EQ (nano::process_result::progress, node2->process (*open3).code);
system.wallet (1)->insert_adhoc (nano::dev_genesis_key.prv);
// Ensure node2 can generate votes
node2->block_confirm (send3);
ASSERT_TIMELY (10s, node2->ledger.cache.cemented_count == 3 && node2->confirmation_height_processor.current ().is_zero ());
// Test node to restart bootstrap
node_config.peering_port = nano::get_available_port ();
node_flags.disable_legacy_bootstrap = false;
auto node3 = system.add_node (node_config, node_flags);
ASSERT_TIMELY (5s, node3->rep_crawler.representative_count () != 0);
//Add single excluded peers record (2 records are required to drop peer)
node3->network.excluded_peers.add (nano::transport::map_endpoint_to_tcp (node1->network.endpoint ()), 0);
ASSERT_FALSE (node3->network.excluded_peers.check (nano::transport::map_endpoint_to_tcp (node1->network.endpoint ())));
node3->bootstrap_initiator.bootstrap (node1->network.endpoint (), false);
ASSERT_TIMELY (15s, !node3->bootstrap_initiator.in_progress ());
ASSERT_FALSE (node3->ledger.block_exists (send1->hash ()));
ASSERT_FALSE (node3->ledger.block_exists (open1->hash ()));
ASSERT_EQ (1, node3->stats.count (nano::stat::type::bootstrap, nano::stat::detail::frontier_confirmation_failed, nano::stat::dir::in)); // failed request from node1
ASSERT_FALSE (node3->network.excluded_peers.check (nano::transport::map_endpoint_to_tcp (node1->network.endpoint ()))); // Banning from bootstrap is disabled
}
TEST (bootstrap_processor, frontiers_confirmed)
{
nano::system system;
nano::node_config node_config (nano::get_available_port (), system.logging);
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
node_config.tcp_io_timeout = std::chrono::seconds (2);
nano::node_flags node_flags;
node_flags.disable_bootstrap_bulk_pull_server = true;
node_flags.disable_bootstrap_bulk_push_client = true;
node_flags.disable_legacy_bootstrap = true;
node_flags.disable_lazy_bootstrap = true;
node_flags.disable_wallet_bootstrap = true;
node_flags.disable_rep_crawler = true;
auto node1 = system.add_node (node_config, node_flags);
nano::genesis genesis;
nano::keypair key1, key2;
// Generating valid chain
auto send1 (std::make_shared<nano::state_block> (nano::dev_genesis_key.pub, genesis.hash (), nano::dev_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key1.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (genesis.hash ())));
ASSERT_EQ (nano::process_result::progress, node1->process (*send1).code);
auto send2 (std::make_shared<nano::state_block> (nano::dev_genesis_key.pub, send1->hash (), nano::dev_genesis_key.pub, nano::genesis_amount - 2 * nano::Gxrb_ratio, key2.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (send1->hash ())));
ASSERT_EQ (nano::process_result::progress, node1->process (*send2).code);
auto open1 (std::make_shared<nano::state_block> (key1.pub, 0, key1.pub, nano::Gxrb_ratio, send1->hash (), key1.prv, key1.pub, *system.work.generate (key1.pub)));
ASSERT_EQ (nano::process_result::progress, node1->process (*open1).code);
auto open2 (std::make_shared<nano::state_block> (key2.pub, 0, key2.pub, nano::Gxrb_ratio, send2->hash (), key2.prv, key2.pub, *system.work.generate (key2.pub)));
ASSERT_EQ (nano::process_result::progress, node1->process (*open2).code);
system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv);
// Confirm all blocks so node1 is free to generate votes
node1->block_confirm (send1);
ASSERT_TIMELY (10s, node1->ledger.cache.cemented_count == 5 && node1->confirmation_height_processor.current ().is_zero ());
// Test node to bootstrap
node_config.peering_port = nano::get_available_port ();
node_flags.disable_legacy_bootstrap = false;
node_flags.disable_rep_crawler = false;
auto node2 = system.add_node (node_config, node_flags);
ASSERT_TIMELY (5s, node2->rep_crawler.representative_count () != 0);
node2->bootstrap_initiator.bootstrap (node1->network.endpoint (), false);
ASSERT_TIMELY (10s, node2->bootstrap_initiator.current_attempt () == nullptr || node2->bootstrap_initiator.current_attempt ()->frontiers_confirmed);
ASSERT_EQ (1, node2->stats.count (nano::stat::type::bootstrap, nano::stat::detail::frontier_confirmation_successful, nano::stat::dir::in)); // Successful request from node1
ASSERT_EQ (0, node2->stats.count (nano::stat::type::bootstrap, nano::stat::detail::frontier_confirmation_failed, nano::stat::dir::in));
}
TEST (bootstrap_processor, frontiers_unconfirmed_threshold)
{
nano::system system;
nano::node_config node_config (nano::get_available_port (), system.logging);
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
node_config.tcp_io_timeout = std::chrono::seconds (2);
node_config.bootstrap_fraction_numerator = 4;
nano::node_flags node_flags;
node_flags.disable_bootstrap_bulk_pull_server = true;
node_flags.disable_bootstrap_bulk_push_client = true;
node_flags.disable_legacy_bootstrap = true;
node_flags.disable_lazy_bootstrap = true;
node_flags.disable_wallet_bootstrap = true;
node_flags.disable_rep_crawler = true;
auto node1 = system.add_node (node_config, node_flags);
nano::genesis genesis;
nano::keypair key1, key2;
// Generating invalid chain
auto threshold (node1->gap_cache.bootstrap_threshold () + 1);
ASSERT_LT (threshold, node1->online_reps.delta ());
auto send1 (std::make_shared<nano::state_block> (nano::dev_genesis_key.pub, genesis.hash (), nano::dev_genesis_key.pub, nano::genesis_amount - threshold, key1.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (genesis.hash ())));
ASSERT_EQ (nano::process_result::progress, node1->process (*send1).code);
auto send2 (std::make_shared<nano::state_block> (nano::dev_genesis_key.pub, send1->hash (), nano::dev_genesis_key.pub, nano::genesis_amount - threshold - nano::Gxrb_ratio, key2.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (send1->hash ())));
ASSERT_EQ (nano::process_result::progress, node1->process (*send2).code);
auto open1 (std::make_shared<nano::state_block> (key1.pub, 0, key1.pub, threshold, send1->hash (), key1.prv, key1.pub, *system.work.generate (key1.pub)));
ASSERT_EQ (nano::process_result::progress, node1->process (*open1).code);
auto open2 (std::make_shared<nano::state_block> (key2.pub, 0, key2.pub, nano::Gxrb_ratio, send2->hash (), key2.prv, key2.pub, *system.work.generate (key2.pub)));
ASSERT_EQ (nano::process_result::progress, node1->process (*open2).code);
system.wallet (0)->insert_adhoc (key1.prv); // Small representative
// Test node with large representative
node_config.peering_port = nano::get_available_port ();
auto node2 = system.add_node (node_config, node_flags);
system.wallet (1)->insert_adhoc (nano::dev_genesis_key.prv);
// Test node to bootstrap
node_config.peering_port = nano::get_available_port ();
node_flags.disable_legacy_bootstrap = false;
node_flags.disable_rep_crawler = false;
auto node3 = system.add_node (node_config, node_flags);
ASSERT_EQ (nano::process_result::progress, node3->process (*send1).code);
ASSERT_EQ (nano::process_result::progress, node3->process (*open1).code); // Change known representative weight
ASSERT_TIMELY (5s, node3->rep_crawler.representative_count () == 2);
node3->bootstrap_initiator.bootstrap (node1->network.endpoint (), false);
ASSERT_TIMELY (15s, node3->stats.count (nano::stat::type::bootstrap, nano::stat::detail::frontier_confirmation_failed, nano::stat::dir::in) == 1);
ASSERT_FALSE (node3->ledger.block_exists (send2->hash ()));
ASSERT_FALSE (node3->ledger.block_exists (open2->hash ()));
ASSERT_EQ (1, node3->stats.count (nano::stat::type::bootstrap, nano::stat::detail::frontier_confirmation_failed, nano::stat::dir::in)); // failed confirmation
ASSERT_EQ (0, node3->stats.count (nano::stat::type::bootstrap, nano::stat::detail::frontier_confirmation_successful, nano::stat::dir::in));
}
TEST (bootstrap_processor, push_diamond)
{
nano::system system;
@ -1494,8 +1343,7 @@ TEST (bulk, genesis_pruning)
// Bootstrap with missing blocks for node2
node2->bootstrap_initiator.bootstrap (node1->network.endpoint (), false);
node2->network.merge_peer (node1->network.endpoint ());
// 2 bootstraps including test bootstrap & restart after frontier confirmation failure
ASSERT_TIMELY (25s, node2->stats.count (nano::stat::type::bootstrap, nano::stat::detail::initiate, nano::stat::dir::out) >= 2 && !node2->bootstrap_initiator.in_progress ());
ASSERT_TIMELY (25s, node2->stats.count (nano::stat::type::bootstrap, nano::stat::detail::initiate, nano::stat::dir::out) >= 1 && !node2->bootstrap_initiator.in_progress ());
// node2 still missing blocks
ASSERT_EQ (1, node2->ledger.cache.block_count);
{
@ -1509,7 +1357,6 @@ TEST (bulk, genesis_pruning)
ASSERT_EQ (3, node2->ledger.cache.block_count);
// New bootstrap
ASSERT_TIMELY (5s, node2->bootstrap_initiator.connections->connections_count == 0);
node2->network.excluded_peers.remove (nano::transport::map_endpoint_to_tcp (node1->network.endpoint ()));
node2->bootstrap_initiator.bootstrap (node1->network.endpoint (), false);
ASSERT_TIMELY (10s, node2->latest (nano::dev_genesis_key.pub) == node1->latest (nano::dev_genesis_key.pub));
ASSERT_EQ (node2->latest (nano::dev_genesis_key.pub), node1->latest (nano::dev_genesis_key.pub));

View file

@ -49,7 +49,7 @@ void nano::bootstrap_initiator::bootstrap (bool force, std::string id_a, uint32_
}
}
void nano::bootstrap_initiator::bootstrap (nano::endpoint const & endpoint_a, bool add_to_peers, bool frontiers_confirmed, std::string id_a)
void nano::bootstrap_initiator::bootstrap (nano::endpoint const & endpoint_a, bool add_to_peers, std::string id_a)
{
if (add_to_peers)
{
@ -70,15 +70,10 @@ void nano::bootstrap_initiator::bootstrap (nano::endpoint const & endpoint_a, bo
auto legacy_attempt (std::make_shared<nano::bootstrap_attempt_legacy> (node.shared (), attempts.incremental++, id_a, std::numeric_limits<uint32_t>::max ()));
attempts_list.push_back (legacy_attempt);
attempts.add (legacy_attempt);
if (frontiers_confirmed)
{
node.network.excluded_peers.remove (nano::transport::map_endpoint_to_tcp (endpoint_a));
}
if (!node.network.excluded_peers.check (nano::transport::map_endpoint_to_tcp (endpoint_a)))
{
connections->add_connection (endpoint_a);
}
legacy_attempt->frontiers_confirmed = frontiers_confirmed;
}
condition.notify_all ();
}

View file

@ -81,7 +81,7 @@ class bootstrap_initiator final
public:
explicit bootstrap_initiator (nano::node &);
~bootstrap_initiator ();
void bootstrap (nano::endpoint const &, bool add_to_peers = true, bool frontiers_confirmed = false, std::string id_a = "");
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 ());
void bootstrap_lazy (nano::hash_or_account const &, bool force = false, bool confirmed = true, std::string id_a = "");
void bootstrap_wallet (std::deque<nano::account> &);
@ -127,9 +127,6 @@ public:
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 size_t bootstrap_max_confirm_frontiers = 70;
static constexpr double required_frontier_confirmation_ratio = 0.8;
static constexpr unsigned frontier_confirmation_blocks_limit = 128 * 1024;
static constexpr unsigned requeued_pulls_limit = 256;
static constexpr unsigned requeued_pulls_limit_dev = 1;
static constexpr unsigned requeued_pulls_processed_blocks_factor = 4096;

View file

@ -12,9 +12,6 @@
#include <algorithm>
constexpr size_t nano::bootstrap_limits::bootstrap_max_confirm_frontiers;
constexpr double nano::bootstrap_limits::required_frontier_confirmation_ratio;
constexpr unsigned nano::bootstrap_limits::frontier_confirmation_blocks_limit;
constexpr unsigned nano::bootstrap_limits::requeued_pulls_limit;
constexpr unsigned nano::bootstrap_limits::requeued_pulls_limit_dev;
@ -116,11 +113,6 @@ std::string nano::bootstrap_attempt::mode_text ()
return mode_text;
}
void nano::bootstrap_attempt::restart_condition ()
{
debug_assert (mode == nano::bootstrap_mode::legacy);
}
void nano::bootstrap_attempt::add_frontier (nano::pull_info const &)
{
debug_assert (mode == nano::bootstrap_mode::legacy);
@ -137,11 +129,6 @@ bool nano::bootstrap_attempt::request_bulk_push_target (std::pair<nano::block_ha
return true;
}
void nano::bootstrap_attempt::add_recent_pull (nano::block_hash const &)
{
debug_assert (mode == nano::bootstrap_mode::legacy);
}
bool nano::bootstrap_attempt::process_block (std::shared_ptr<nano::block> const & block_a, nano::account const & known_account_a, uint64_t pull_blocks, nano::bulk_pull::count_t max_blocks, bool block_expected, unsigned retry_limit)
{
nano::unchecked_info info (block_a, known_account_a, 0, nano::signature_verification::unknown);

View file

@ -23,11 +23,9 @@ public:
void pull_finished ();
bool should_log ();
std::string mode_text ();
virtual void restart_condition ();
virtual void add_frontier (nano::pull_info const &);
virtual void add_bulk_push_target (nano::block_hash const &, nano::block_hash const &);
virtual bool request_bulk_push_target (std::pair<nano::block_hash, nano::block_hash> &);
virtual void add_recent_pull (nano::block_hash const &);
virtual void lazy_start (nano::hash_or_account const &, bool confirmed = true);
virtual void lazy_add (nano::pull_info const &);
virtual void lazy_requeue (nano::block_hash const &, nano::block_hash const &, bool);
@ -51,7 +49,6 @@ public:
std::string id;
std::chrono::steady_clock::time_point attempt_start{ std::chrono::steady_clock::now () };
std::atomic<bool> frontiers_received{ false };
std::atomic<bool> frontiers_confirmed{ false };
nano::bootstrap_mode mode;
nano::mutex mutex;
nano::condition_variable condition;

View file

@ -356,10 +356,6 @@ void nano::bootstrap_connections::request_pull (nano::unique_lock<nano::mutex> &
}
if (attempt_l != nullptr)
{
if (attempt_l->mode == nano::bootstrap_mode::legacy)
{
attempt_l->add_recent_pull (pull.head);
}
// 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]() {
@ -388,11 +384,7 @@ void nano::bootstrap_connections::requeue_pull (nano::pull_info const & pull_a,
if (attempt_l != nullptr)
{
++attempt_l->requeued_pulls;
if (attempt_l->mode == nano::bootstrap_mode::legacy)
{
attempt_l->restart_condition ();
}
else if (attempt_l->mode == nano::bootstrap_mode::lazy)
if (attempt_l->mode == nano::bootstrap_mode::lazy)
{
pull.count = attempt_l->lazy_batch_size ();
}

View file

@ -115,196 +115,6 @@ bool nano::bootstrap_attempt_legacy::request_bulk_push_target (std::pair<nano::b
return empty;
}
void nano::bootstrap_attempt_legacy::add_recent_pull (nano::block_hash const & head_a)
{
nano::lock_guard<nano::mutex> lock (mutex);
recent_pulls_head.push_back (head_a);
if (recent_pulls_head.size () > nano::bootstrap_limits::bootstrap_max_confirm_frontiers)
{
recent_pulls_head.pop_front ();
}
}
void nano::bootstrap_attempt_legacy::restart_condition ()
{
/* Conditions to start frontiers confirmation:
- not completed frontiers confirmation
- more than 256 pull retries usually indicating issues with requested pulls
- or 128k processed blocks indicating large bootstrap */
if (!frontiers_confirmation_pending && !frontiers_confirmed && (requeued_pulls > (!node->network_params.network.is_dev_network () ? nano::bootstrap_limits::requeued_pulls_limit : nano::bootstrap_limits::requeued_pulls_limit_dev) || total_blocks > nano::bootstrap_limits::frontier_confirmation_blocks_limit))
{
frontiers_confirmation_pending = true;
}
}
void nano::bootstrap_attempt_legacy::attempt_restart_check (nano::unique_lock<nano::mutex> & lock_a)
{
if (frontiers_confirmation_pending)
{
auto confirmed (confirm_frontiers (lock_a));
debug_assert (lock_a.owns_lock ());
if (!confirmed)
{
node->stats.inc (nano::stat::type::bootstrap, nano::stat::detail::frontier_confirmation_failed, nano::stat::dir::in);
node->logger.try_log (boost::str (boost::format ("Frontier confirmation failed for peer %1% after %2% seconds bootstrap attempt") % endpoint_frontier_request % std::chrono::duration_cast<std::chrono::seconds> (std::chrono::steady_clock::now () - attempt_start).count ()));
lock_a.unlock ();
stop ();
lock_a.lock ();
// Start new bootstrap connection
auto node_l (node->shared ());
auto this_l (shared_from_this ());
auto duration (std::chrono::duration_cast<std::chrono::seconds> (std::chrono::steady_clock::now () - attempt_start).count ());
auto frontiers_age_l (frontiers_age != std::numeric_limits<uint32_t>::max () ? frontiers_age + duration : frontiers_age);
node->background ([node_l, this_l, frontiers_age_l]() {
node_l->bootstrap_initiator.remove_attempt (this_l);
auto id_l (this_l->id);
// Delay after removing current attempt
node_l->workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::milliseconds (50), [node_l, id_l, frontiers_age_l]() {
node_l->bootstrap_initiator.bootstrap (true, id_l, frontiers_age_l);
});
});
}
else
{
node->stats.inc (nano::stat::type::bootstrap, nano::stat::detail::frontier_confirmation_successful, nano::stat::dir::in);
}
frontiers_confirmed = confirmed;
frontiers_confirmation_pending = false;
}
}
bool nano::bootstrap_attempt_legacy::confirm_frontiers (nano::unique_lock<nano::mutex> & lock_a)
{
bool confirmed (false);
debug_assert (!frontiers_confirmed);
condition.wait (lock_a, [& stopped = stopped] { return !stopped; });
auto this_l (shared_from_this ());
std::vector<nano::block_hash> frontiers;
lock_a.unlock ();
nano::unique_lock<nano::mutex> pulls_lock (node->bootstrap_initiator.connections->mutex);
for (auto i (node->bootstrap_initiator.connections->pulls.begin ()), end (node->bootstrap_initiator.connections->pulls.end ()); i != end && frontiers.size () != nano::bootstrap_limits::bootstrap_max_confirm_frontiers; ++i)
{
if (!i->head.is_zero () && i->bootstrap_id == incremental_id && std::find (frontiers.begin (), frontiers.end (), i->head) == frontiers.end ())
{
frontiers.push_back (i->head);
}
}
pulls_lock.unlock ();
lock_a.lock ();
for (auto i (recent_pulls_head.begin ()), end (recent_pulls_head.end ()); i != end && frontiers.size () != nano::bootstrap_limits::bootstrap_max_confirm_frontiers; ++i)
{
if (!i->is_zero () && std::find (frontiers.begin (), frontiers.end (), *i) == frontiers.end ())
{
frontiers.push_back (*i);
}
}
lock_a.unlock ();
auto frontiers_count (frontiers.size ());
if (frontiers_count > 0)
{
const size_t reps_limit = 20;
auto representatives (node->rep_crawler.representatives ());
auto reps_weight (node->rep_crawler.total_weight ());
auto representatives_copy (representatives);
nano::uint128_t total_weight (0);
// Select random peers from bottom 50% of principal representatives
if (representatives.size () > 1)
{
std::reverse (representatives.begin (), representatives.end ());
representatives.resize (representatives.size () / 2);
for (auto i = static_cast<CryptoPP::word32> (representatives.size () - 1); i > 0; --i)
{
auto k = nano::random_pool::generate_word32 (0, i);
std::swap (representatives[i], representatives[k]);
}
if (representatives.size () > reps_limit)
{
representatives.resize (reps_limit);
}
}
for (auto const & rep : representatives)
{
total_weight += rep.weight.number ();
}
// Select peers with total 25% of reps stake from top 50% of principal representatives
representatives_copy.resize (representatives_copy.size () / 2);
while (total_weight < reps_weight / 4) // 25%
{
auto k = nano::random_pool::generate_word32 (0, static_cast<CryptoPP::word32> (representatives_copy.size () - 1));
auto rep (representatives_copy[k]);
if (std::find (representatives.begin (), representatives.end (), rep) == representatives.end ())
{
representatives.push_back (rep);
total_weight += rep.weight.number ();
}
}
// Start requests
for (auto i (0), max_requests (20); i <= max_requests && !confirmed && !stopped; ++i)
{
std::unordered_map<std::shared_ptr<nano::transport::channel>, std::deque<std::pair<nano::block_hash, nano::root>>> batched_confirm_req_bundle;
std::deque<std::pair<nano::block_hash, nano::root>> request;
// Find confirmed frontiers (tally > 12.5% of reps stake, 60% of requestsed reps responded
for (auto ii (frontiers.begin ()); ii != frontiers.end ();)
{
if (node->ledger.block_or_pruned_exists (*ii))
{
ii = frontiers.erase (ii);
}
else
{
auto existing (node->active.find_inactive_votes_cache (*ii));
nano::uint128_t tally;
for (auto & [voter, timestamp] : existing.voters)
{
tally += node->ledger.weight (voter);
}
if (existing.status.confirmed || (tally > reps_weight / 8 && existing.voters.size () >= representatives.size () * 0.6)) // 12.5% of weight, 60% of reps
{
ii = frontiers.erase (ii);
}
else
{
for (auto const & rep : representatives)
{
if (std::find_if (existing.voters.begin (), existing.voters.end (), [&rep](auto const & item_a) { return item_a.first == rep.account; }) == existing.voters.end ())
{
release_assert (!ii->is_zero ());
auto rep_request (batched_confirm_req_bundle.find (rep.channel));
if (rep_request == batched_confirm_req_bundle.end ())
{
std::deque<std::pair<nano::block_hash, nano::root>> insert_root_hash = { std::make_pair (*ii, *ii) };
batched_confirm_req_bundle.emplace (rep.channel, insert_root_hash);
}
else
{
rep_request->second.emplace_back (*ii, *ii);
}
}
}
++ii;
}
}
}
auto confirmed_count (frontiers_count - frontiers.size ());
if (confirmed_count >= frontiers_count * nano::bootstrap_limits::required_frontier_confirmation_ratio) // 80% of frontiers confirmed
{
confirmed = true;
}
else if (i < max_requests)
{
node->network.broadcast_confirm_req_batched_many (batched_confirm_req_bundle);
std::this_thread::sleep_for (std::chrono::milliseconds (!node->network_params.network.is_dev_network () ? 500 : 25));
}
}
if (!confirmed)
{
node->logger.always_log (boost::str (boost::format ("Failed to confirm frontiers for bootstrap attempt. %1% of %2% frontiers were not confirmed") % frontiers.size () % frontiers_count));
}
}
lock_a.lock ();
return confirmed;
}
bool nano::bootstrap_attempt_legacy::request_frontier (nano::unique_lock<nano::mutex> & lock_a, bool first_attempt)
{
auto result (true);
@ -371,10 +181,8 @@ bool nano::bootstrap_attempt_legacy::request_frontier (nano::unique_lock<nano::m
void nano::bootstrap_attempt_legacy::run_start (nano::unique_lock<nano::mutex> & lock_a)
{
frontiers_received = false;
frontiers_confirmed = false;
total_blocks = 0;
requeued_pulls = 0;
recent_pulls_head.clear ();
auto frontier_failure (true);
uint64_t frontier_attempts (0);
while (!stopped && frontier_failure)
@ -397,9 +205,7 @@ void nano::bootstrap_attempt_legacy::run ()
while (still_pulling ())
{
// clang-format off
condition.wait (lock, [&stopped = stopped, &pulling = pulling, &frontiers_confirmation_pending = frontiers_confirmation_pending] { return stopped || pulling == 0 || frontiers_confirmation_pending; });
// clang-format on
attempt_restart_check (lock);
condition.wait (lock, [&stopped = stopped, &pulling = pulling] { return stopped || pulling == 0; });
}
// Flushing may resolve forks which can add more pulls
node->logger.try_log ("Flushing unchecked blocks");
@ -430,6 +236,4 @@ void nano::bootstrap_attempt_legacy::get_information (boost::property_tree::ptre
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_confirmed", static_cast<bool> (frontiers_confirmed));
tree_a.put ("frontiers_confirmation_pending", static_cast<bool> (frontiers_confirmation_pending));
}
}

View file

@ -25,20 +25,14 @@ public:
void add_frontier (nano::pull_info const &) override;
void add_bulk_push_target (nano::block_hash const &, nano::block_hash const &) override;
bool request_bulk_push_target (std::pair<nano::block_hash, nano::block_hash> &) override;
void add_recent_pull (nano::block_hash const &) override;
void run_start (nano::unique_lock<nano::mutex> &);
void restart_condition () override;
void attempt_restart_check (nano::unique_lock<nano::mutex> &);
bool confirm_frontiers (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::deque<nano::block_hash> recent_pulls_head;
std::vector<std::pair<nano::block_hash, nano::block_hash>> bulk_push_targets;
std::atomic<unsigned> account_count{ 0 };
std::atomic<bool> frontiers_confirmation_pending{ false };
uint32_t frontiers_age;
};
}
}

View file

@ -1688,7 +1688,6 @@ void nano::json_handler::bootstrap ()
{
std::string address_text = request.get<std::string> ("address");
std::string port_text = request.get<std::string> ("port");
const bool bypass_frontier_confirmation = request.get<bool> ("bypass_frontier_confirmation", false);
boost::system::error_code address_ec;
auto address (boost::asio::ip::make_address_v6 (address_text, address_ec));
if (!address_ec)
@ -1699,7 +1698,7 @@ void nano::json_handler::bootstrap ()
if (!node.flags.disable_legacy_bootstrap)
{
std::string bootstrap_id (request.get<std::string> ("id", ""));
node.bootstrap_initiator.bootstrap (nano::endpoint (address, port), true, bypass_frontier_confirmation, bootstrap_id);
node.bootstrap_initiator.bootstrap (nano::endpoint (address, port), true, bootstrap_id);
response_l.put ("success", "");
}
else