From ce0956a8a4f654b8eaa42fcc56ceb308da1ce324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Wed, 2 Oct 2024 22:15:37 +0200 Subject: [PATCH] Safe ascending bootstrap --- nano/lib/stats_enums.hpp | 5 ++- nano/node/bootstrap/bootstrap_config.cpp | 2 + nano/node/bootstrap/bootstrap_config.hpp | 1 + nano/node/bootstrap_ascending/service.cpp | 46 +++++++++++++++++------ nano/node/bootstrap_ascending/service.hpp | 2 + 5 files changed, 44 insertions(+), 12 deletions(-) diff --git a/nano/lib/stats_enums.hpp b/nano/lib/stats_enums.hpp index 9c764de6a..89bf2ddf5 100644 --- a/nano/lib/stats_enums.hpp +++ b/nano/lib/stats_enums.hpp @@ -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, diff --git a/nano/node/bootstrap/bootstrap_config.cpp b/nano/node/bootstrap/bootstrap_config.cpp index 2264776e0..705c1220a 100644 --- a/nano/node/bootstrap/bootstrap_config.cpp +++ b/nano/node/bootstrap/bootstrap_config.cpp @@ -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); diff --git a/nano/node/bootstrap/bootstrap_config.hpp b/nano/node/bootstrap/bootstrap_config.hpp index b1881825d..1d951e696 100644 --- a/nano/node/bootstrap/bootstrap_config.hpp +++ b/nano/node/bootstrap/bootstrap_config.hpp @@ -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; diff --git a/nano/node/bootstrap_ascending/service.cpp b/nano/node/bootstrap_ascending/service.cpp index fb16bc227..b3bfc41db 100644 --- a/nano/node/bootstrap_ascending/service.cpp +++ b/nano/node/bootstrap_ascending/service.cpp @@ -13,6 +13,7 @@ #include #include #include +#include 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); diff --git a/nano/node/bootstrap_ascending/service.hpp b/nano/node/bootstrap_ascending/service.hpp index e4f78d88c..2379b9594 100644 --- a/nano/node/bootstrap_ascending/service.hpp +++ b/nano/node/bootstrap_ascending/service.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -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);