From bd8a1b51aea3785f616ded62d86bc869f5b02da2 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Mon, 5 Oct 2026 00:42:32 -0400 Subject: [PATCH 1/3] Drop peers whose header responses do not connect. --- src/protocols/protocol_header_in_31800.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/protocols/protocol_header_in_31800.cpp b/src/protocols/protocol_header_in_31800.cpp index d58bab12..cd7e3c2c 100644 --- a/src/protocols/protocol_header_in_31800.cpp +++ b/src/protocols/protocol_header_in_31800.cpp @@ -129,13 +129,26 @@ void protocol_header_in_31800::synchronize(const headers& message, return; } - // A message not extending the branch restarts from its stored parent. - // An unstored parent (announcement) is requested from the candidate. + // A message that does not extend the branch restarts from the parent of + // its first header. A single header with an unknown parent is an + // announcement from a peer that is ahead, so headers are requested from + // the top candidate. Multiple headers with an unknown parent are a + // response from a peer that does not connect to our locator, so the + // peer is dropped. const auto& first = ptrs.front()->previous_block_hash(); if ((!state_ || first != state_->hash()) && !restart(first)) { + const code ec{ error::orphan_header }; + if (!is_one(ptrs.size())) + { + LOGR("Headers (" << ptrs.size() << ") from [" << opposite() + << "] " << ec.message()); + stop(ec); + return; + } + LOGP("Header [" << encode_hash(ptrs.front()->get_hash()) << "] from [" - << opposite() << "] " << code{ error::orphan_header }.message()); + << opposite() << "] " << ec.message()); SEND(create_get_headers(), handle_send, _1); return; } From 125fa437943170ddf684d40dd8d6e5ca144c664a Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Mon, 5 Oct 2026 00:53:47 -0400 Subject: [PATCH 2/3] Set block valid after bypassed indexing. --- src/chasers/chaser_validate_parallel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chasers/chaser_validate_parallel.cpp b/src/chasers/chaser_validate_parallel.cpp index a2314cc4..110978f4 100644 --- a/src/chasers/chaser_validate_parallel.cpp +++ b/src/chasers/chaser_validate_parallel.cpp @@ -216,7 +216,7 @@ code chaser_validate::validate(bool& batched, bool& capturing, bool bypass, // Defer block state change when batched. // Valid must be set after set_prevouts, set_filter_body, and set_silent. - if (!batched && !bypass && !query.set_block_valid(link)) + if (!batched && !query.set_block_valid(link)) return error::validate10; return error::success; From bbd266e7aad6e17e29d4cefd15ee0829a7fe1148 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Mon, 5 Oct 2026 02:06:25 -0400 Subject: [PATCH 3/3] Gate silent indexing on taproot activation. --- src/chasers/chaser_validate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chasers/chaser_validate.cpp b/src/chasers/chaser_validate.cpp index 53755d99..34c1aaa4 100644 --- a/src/chasers/chaser_validate.cpp +++ b/src/chasers/chaser_validate.cpp @@ -209,7 +209,7 @@ void chaser_validate::do_bumped(height_t height) NOEXCEPT const auto bypass = is_under_checkpoint(height) || query.is_milestone(link); - const auto silent = height >= query.silent_start_height(); + const auto silent = query.is_silent(link, height); switch (ec.value()) {