Safe ascending bootstrap

This commit is contained in:
Piotr Wójcik 2024-10-02 22:15:37 +02:00
commit ce0956a8a4
5 changed files with 44 additions and 12 deletions

View file

@ -64,6 +64,7 @@ enum class type
bootstrap_ascending_verify_frontiers,
bootstrap_ascending_process,
bootstrap_ascending_request,
bootstrap_ascending_request_blocks,
bootstrap_ascending_reply,
bootstrap_ascending_next,
bootstrap_ascending_frontiers,
@ -454,7 +455,6 @@ enum class detail
process_frontiers,
dropped_frontiers,
// bootstrap_ascending_accounts
prioritize,
prioritize_failed,
block,
@ -491,6 +491,9 @@ enum class detail
request_blocks,
request_account_info,
safe,
base,
// active
started_hinted,
started_optimistic,

View file

@ -45,6 +45,7 @@ nano::error nano::bootstrap_ascending_config::deserialize (nano::tomlconfig & to
toml.get_duration ("throttle_wait", throttle_wait);
toml.get ("block_processor_threshold", block_processor_threshold);
toml.get ("max_requests", max_requests);
toml.get ("optimistic_request_percentage", optimistic_request_percentage);
if (toml.has_key ("account_sets"))
{
@ -71,6 +72,7 @@ nano::error nano::bootstrap_ascending_config::serialize (nano::tomlconfig & toml
toml.put ("throttle_wait", throttle_wait.count (), "Length of time to wait between requests when throttled.\ntype:milliseconds");
toml.put ("block_processor_threshold", block_processor_threshold, "Ascending bootstrap will wait while block processor has more than this many blocks queued.\ntype:uint64");
toml.put ("max_requests", max_requests, "Maximum total number of in flight requests.\ntype:uint64");
toml.put ("optimistic_request_percentage", optimistic_request_percentage, "Percentage of requests that will be optimistic. Optimistic requests start from the (possibly unconfirmed) account frontier and are vulnerable to bootstrap poisoning. Safe requests start from the confirmed frontier and given enough time will eventually resolve forks.\ntype:uint64");
nano::tomlconfig account_sets_l;
account_sets.serialize (account_sets_l);

View file

@ -60,6 +60,7 @@ public:
std::chrono::milliseconds throttle_wait{ 100 };
std::size_t block_processor_threshold{ 1000 };
std::size_t max_requests{ 1024 };
unsigned optimistic_request_percentage{ 75 };
account_sets_config account_sets;
frontier_scan_config frontier_scan;

View file

@ -13,6 +13,7 @@
#include <nano/secure/ledger_set_any.hpp>
#include <nano/store/account.hpp>
#include <nano/store/component.hpp>
#include <nano/store/confirmation_height.hpp>
using namespace std::chrono_literals;
@ -263,6 +264,7 @@ void nano::bootstrap_ascending::service::inspect (secure::transaction const & tx
break;
case nano::block_status::gap_source:
{
// Prevent malicious live traffic from filling up the blocked set
if (source == nano::block_source::bootstrap)
{
const auto account = block.previous ().is_zero () ? block.account_field ().value () : ledger.any.block_account (tx, block.previous ()).value_or (0);
@ -491,18 +493,40 @@ bool nano::bootstrap_ascending::service::request (nano::account account, size_t
tag.account = account;
tag.count = count;
// Check if the account picked has blocks, if it does, start the pull from the highest block
auto info = ledger.store.account.get (ledger.store.tx_begin_read (), account);
if (info)
{
tag.type = query_type::blocks_by_hash;
tag.start = info->head;
tag.hash = info->head;
}
else
{
tag.type = query_type::blocks_by_account;
tag.start = account;
auto transaction = ledger.store.tx_begin_read ();
// Check if the account picked has blocks, if it does, start the pull from the highest block
if (auto info = ledger.store.account.get (transaction, account))
{
tag.type = query_type::blocks_by_hash;
// Probabilistically choose between requesting blocks from account frontier or confirmed frontier
// Optimistic requests start from the (possibly unconfirmed) account frontier and are vulnerable to bootstrap poisoning
// Safe requests start from the confirmed frontier and given enough time will eventually resolve forks
bool optimistic_reuest = rng.random (100) < config.optimistic_request_percentage;
if (!optimistic_reuest)
{
if (auto conf_info = ledger.store.confirmation_height.get (transaction, account))
{
stats.inc (nano::stat::type::bootstrap_ascending_request_blocks, nano::stat::detail::safe);
tag.start = conf_info->frontier;
tag.hash = conf_info->height;
}
}
if (tag.start.is_zero ())
{
stats.inc (nano::stat::type::bootstrap_ascending_request_blocks, nano::stat::detail::optimistic);
tag.start = info->head;
tag.hash = info->head;
}
}
else
{
stats.inc (nano::stat::type::bootstrap_ascending_request_blocks, nano::stat::detail::base);
tag.type = query_type::blocks_by_account;
tag.start = account;
}
}
return send (channel, tag);

View file

@ -4,6 +4,7 @@
#include <nano/lib/locks.hpp>
#include <nano/lib/numbers.hpp>
#include <nano/lib/observer_set.hpp>
#include <nano/lib/random.hpp>
#include <nano/lib/rate_limiting.hpp>
#include <nano/lib/thread_pool.hpp>
#include <nano/lib/timer.hpp>
@ -211,6 +212,7 @@ namespace bootstrap_ascending
std::thread timeout_thread;
nano::thread_pool workers;
nano::random_generator_mt rng;
};
nano::stat::detail to_stat_detail (service::query_type);