From 5c40bbd2067afa123715af201c921e3b65331ebb Mon Sep 17 00:00:00 2001 From: evoskuil Date: Sat, 28 Mar 2026 00:26:35 -0400 Subject: [PATCH 1/2] Add allow_overlapped node setting. --- include/bitcoin/node/settings.hpp | 1 + src/settings.cpp | 1 + test/settings.cpp | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/bitcoin/node/settings.hpp b/include/bitcoin/node/settings.hpp index f1064840..679ef1c3 100644 --- a/include/bitcoin/node/settings.hpp +++ b/include/bitcoin/node/settings.hpp @@ -38,6 +38,7 @@ class BCN_API settings bool headers_first; bool thread_priority; bool memory_priority; + bool allow_overlapped; bool defer_validation; bool defer_confirmation; float allowed_deviation; diff --git a/src/settings.cpp b/src/settings.cpp index 12a8c5bc..d9673e77 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -33,6 +33,7 @@ settings::settings() NOEXCEPT headers_first{ true }, memory_priority{ true }, thread_priority{ true }, + allow_overlapped{ true }, defer_validation{ false }, defer_confirmation{ false }, minimum_fee_rate{ 0.0 }, diff --git a/test/settings.cpp b/test/settings.cpp index 519041e6..24aaf624 100644 --- a/test/settings.cpp +++ b/test/settings.cpp @@ -30,9 +30,11 @@ BOOST_AUTO_TEST_CASE(settings__node__default_context__expected) using namespace network; const node::settings node{}; - BOOST_REQUIRE_EQUAL(node.thread_priority, true); BOOST_REQUIRE_EQUAL(node.delay_inbound, true); BOOST_REQUIRE_EQUAL(node.headers_first, true); + BOOST_REQUIRE_EQUAL(node.memory_priority, true); + BOOST_REQUIRE_EQUAL(node.thread_priority, true); + BOOST_REQUIRE_EQUAL(node.allow_overlapped, true); BOOST_REQUIRE_EQUAL(node.defer_validation, false); BOOST_REQUIRE_EQUAL(node.defer_confirmation, false); BOOST_REQUIRE_EQUAL(node.minimum_fee_rate, 0.0); From 30b089225cf3d5e08e6e5ade1f36e3f4e71bdf94 Mon Sep 17 00:00:00 2001 From: evoskuil Date: Sat, 28 Mar 2026 00:30:52 -0400 Subject: [PATCH 2/2] Update block-out to allow overlapped block requests. --- .../node/protocols/protocol_block_out_106.hpp | 19 ++++--- src/protocols/protocol_block_out_106.cpp | 50 ++++++++++++++----- 2 files changed, 49 insertions(+), 20 deletions(-) diff --git a/include/bitcoin/node/protocols/protocol_block_out_106.hpp b/include/bitcoin/node/protocols/protocol_block_out_106.hpp index eca678ae..96470554 100644 --- a/include/bitcoin/node/protocols/protocol_block_out_106.hpp +++ b/include/bitcoin/node/protocols/protocol_block_out_106.hpp @@ -19,6 +19,7 @@ #ifndef LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_BLOCK_OUT_106_HPP #define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_BLOCK_OUT_106_HPP +#include #include #include @@ -36,6 +37,7 @@ class BCN_API protocol_block_out_106 const network::channel::ptr& channel) NOEXCEPT : node::protocol_peer(session, channel), node_witness_(session->network_settings().witness_node()), + allow_overlapped_(session->node_settings().allow_overlapped), network::tracker(session->log) { } @@ -49,9 +51,6 @@ class BCN_API protocol_block_out_106 protected: using get_data = network::messages::peer::get_data; using get_blocks = network::messages::peer::get_blocks; - using inventory = network::messages::peer::inventory; - using inventory_items = network::messages::peer::inventory_items; - using inventory_items_ptr = std::shared_ptr; /// Block announcements are superseded by send_headers. virtual bool superseded() const NOEXCEPT; @@ -67,18 +66,22 @@ class BCN_API protocol_block_out_106 const get_blocks::cptr& message) NOEXCEPT; virtual bool handle_receive_get_data(const code& ec, const get_data::cptr& message) NOEXCEPT; - virtual void send_block(const code& ec, - const inventory_items_ptr& items) NOEXCEPT; + virtual void send_block(const code& ec) NOEXCEPT; private: + using inventory = network::messages::peer::inventory; + using inventory_item = network::messages::peer::inventory_item; + using inventory_items = network::messages::peer::inventory_items; + inventory create_inventory(const get_blocks& locator) const NOEXCEPT; + void merge_inventory(const inventory_items& items) NOEXCEPT; -private: - // This is thread safe. + // These are thread safe. const bool node_witness_; + const bool allow_overlapped_; // This is protected by strand. - bool busy_{}; + std::deque backlog_{}; }; } // namespace node diff --git a/src/protocols/protocol_block_out_106.cpp b/src/protocols/protocol_block_out_106.cpp index 960cc55b..d3664461 100644 --- a/src/protocols/protocol_block_out_106.cpp +++ b/src/protocols/protocol_block_out_106.cpp @@ -153,35 +153,53 @@ bool protocol_block_out_106::handle_receive_get_data(const code& ec, return false; } - constexpr auto only = get_data::selector::blocks; - const auto blocks = emplace_shared(message->select(only)); - if (blocks->empty()) + const auto size = message->count(get_data::selector::blocks); + if (is_zero(size)) return true; - if (busy_) + const auto total = ceilinged_add(backlog_.size(), size); + if (total > messages::peer::max_inventory) + { + LOGR("Blocks requested (" << total << ") exceeds inv limit [" + << opposite() << "]."); + stop(network::error::protocol_violation); + return false; + } + + // Satoshi sends overlapping get_data requests, but assumes that the + // recipient is blocking *all traffic* until the previous is completed. + // So to prevent frequent drops of satoshi peers, and not let one protocol + // block all others, we must accumulate the requests into a backlog. If the + // backlog exceeds the *individual* message limit we drop the peer. + const auto idle = backlog_.empty(); + if (!allow_overlapped_ && !idle) { LOGR("Overlapping block requests [" << opposite() << "]."); stop(network::error::protocol_violation); return false; } - busy_ = true; - send_block(error::success, blocks); + // Append the new inventory the request queue. + merge_inventory(message->items); + + // Bump the idle async send loop if no pending send. + if (idle) + send_block(error::success); + return true; } // Outbound (block). // ---------------------------------------------------------------------------- -void protocol_block_out_106::send_block(const code& ec, - const inventory_items_ptr& items) NOEXCEPT +void protocol_block_out_106::send_block(const code& ec) NOEXCEPT { BC_ASSERT(stranded()); if (stopped(ec)) return; - if (items->empty()) return; - const auto item = pop(*items); + if (backlog_.empty()) return; + const auto& item = backlog_.front(); const auto witness = item.is_witness_type(); if (!node_witness_ && witness) { @@ -203,14 +221,22 @@ void protocol_block_out_106::send_block(const code& ec, return; } + backlog_.pop_front(); span(events::block_usecs, start); - if (items->empty()) busy_ = false; - SEND(messages::peer::block{ ptr }, send_block, _1, items); + SEND(messages::peer::block{ ptr }, send_block, _1); } // utilities // ---------------------------------------------------------------------------- +void protocol_block_out_106::merge_inventory( + const inventory_items& items) NOEXCEPT +{ + for (const auto& item: items) + if (item.is_block()) + backlog_.push_back(item); +} + protocol_block_out_106::inventory protocol_block_out_106::create_inventory( const get_blocks& locator) const NOEXCEPT {