From 8bd7f851f425610594b93badfddc71d9ba1aa6a6 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sat, 10 Oct 2026 00:35:34 -0400 Subject: [PATCH] Hold the dispatch gate across compact block chaser calls. --- .../protocols/protocol_header_in_70014.hpp | 12 +++--- src/protocols/protocol_header_in_70014.cpp | 33 +++++++-------- test/functional/p2p_compact.cpp | 41 +++++++++++++++++++ 3 files changed, 64 insertions(+), 22 deletions(-) diff --git a/include/bitcoin/node/protocols/protocol_header_in_70014.hpp b/include/bitcoin/node/protocols/protocol_header_in_70014.hpp index 87349cfa..16c20282 100644 --- a/include/bitcoin/node/protocols/protocol_header_in_70014.hpp +++ b/include/bitcoin/node/protocols/protocol_header_in_70014.hpp @@ -76,15 +76,15 @@ class BCN_API protocol_header_in_70014 virtual bool handle_receive_compact_transactions(const code& ec, const compact_transactions::cptr& message) NOEXCEPT; virtual void handle_organize_compact(const code& ec, size_t height, - const compact_block::cptr& message) NOEXCEPT; + const compact_block::cptr& message, const gate_t::ptr& gate) NOEXCEPT; virtual void do_organize_compact(const code& ec, - const compact_block::cptr& message) NOEXCEPT; + const compact_block::cptr& message, const gate_t::ptr& gate) NOEXCEPT; virtual void handle_submit_compact(const code& ec, size_t index, const system::hash_digest& hash, size_t height, size_t pooled, - size_t count) NOEXCEPT; + size_t count, const gate_t::ptr& gate) NOEXCEPT; virtual void do_submit_compact(const code& ec, const system::hash_digest& hash, size_t height, size_t pooled, - size_t count) NOEXCEPT; + size_t count, const gate_t::ptr& gate) NOEXCEPT; private: bool scan(fill& block) NOEXCEPT; @@ -95,11 +95,11 @@ class BCN_API protocol_header_in_70014 void upgrade() NOEXCEPT; void request() NOEXCEPT; - void identify() NOEXCEPT; + void identify(const gate_t::ptr& gate) NOEXCEPT; bool resolve(const database::header_link& link) NOEXCEPT; bool matched(const database::header_link& link) const NOEXCEPT; void collect(const compact_block& message, - const database::header_link& link) NOEXCEPT; + const database::header_link& link, const gate_t::ptr& gate) NOEXCEPT; // These are protected by strand. bool compact_{}; diff --git a/src/protocols/protocol_header_in_70014.cpp b/src/protocols/protocol_header_in_70014.cpp index de29c552..7cf33d55 100644 --- a/src/protocols/protocol_header_in_70014.cpp +++ b/src/protocols/protocol_header_in_70014.cpp @@ -124,27 +124,28 @@ bool protocol_header_in_70014::handle_receive_compact_block(const code& ec, if (link.is_terminal()) { organize_compact(header, - BIND(handle_organize_compact, _1, _2, message)); + BIND(handle_organize_compact, _1, _2, message, gate())); return true; } - collect(*message, link); + collect(*message, link, gate()); return true; } // not stranded void protocol_header_in_70014::handle_organize_compact(const code& ec, - size_t, const compact_block::cptr& message) NOEXCEPT + size_t, const compact_block::cptr& message, + const gate_t::ptr& gate) NOEXCEPT { // Chaser may be stopped before protocol. if (stopped() || ec == network::error::service_stopped) return; - POST(do_organize_compact, ec, message); + POST(do_organize_compact, ec, message, gate); } void protocol_header_in_70014::do_organize_compact(const code& ec, - const compact_block::cptr& message) NOEXCEPT + const compact_block::cptr& message, const gate_t::ptr& gate) NOEXCEPT { BC_ASSERT(stranded()); @@ -171,14 +172,14 @@ void protocol_header_in_70014::do_organize_compact(const code& ec, // The header of a weak branch is not stored. const auto link = archive().to_header(hash); if (!link.is_terminal()) - collect(*message, link); + collect(*message, link, gate); } // Collect (short ids). // ---------------------------------------------------------------------------- void protocol_header_in_70014::collect(const compact_block& message, - const database::header_link& link) NOEXCEPT + const database::header_link& link, const gate_t::ptr& gate) NOEXCEPT { BC_ASSERT(stranded()); const auto& query = archive(); @@ -220,7 +221,7 @@ void protocol_header_in_70014::collect(const compact_block& message, fill_.emplace(std::move(block)); if (fill_->missing.empty()) - identify(); + identify(gate); else request(); } @@ -376,14 +377,14 @@ bool protocol_header_in_70014::handle_receive_compact_transactions( } block.missing.clear(); - identify(); + identify(gate()); return true; } // Identify and submit. // ---------------------------------------------------------------------------- -void protocol_header_in_70014::identify() NOEXCEPT +void protocol_header_in_70014::identify(const gate_t::ptr& gate) NOEXCEPT { BC_ASSERT(stranded()); @@ -428,7 +429,7 @@ void protocol_header_in_70014::identify() NOEXCEPT const auto count = block.txs.size(); submit_compact(unpooled, block.links, block.link, BIND(handle_submit_compact, _1, _2, block.hash, block.height, - count - block.unpooled.size(), count)); + count - block.unpooled.size(), count, gate)); fill_.reset(); } @@ -486,19 +487,19 @@ bool protocol_header_in_70014::is_malleated64(const fill& block) NOEXCEPT // not stranded void protocol_header_in_70014::handle_submit_compact(const code& ec, size_t, - const hash_digest& hash, size_t height, size_t pooled, - size_t count) NOEXCEPT + const hash_digest& hash, size_t height, size_t pooled, size_t count, + const gate_t::ptr& gate) NOEXCEPT { // Chaser may be stopped before protocol. if (stopped() || ec == network::error::service_stopped) return; - POST(do_submit_compact, ec, hash, height, pooled, count); + POST(do_submit_compact, ec, hash, height, pooled, count, gate); } void protocol_header_in_70014::do_submit_compact(const code& ec, - const hash_digest& hash, size_t height, size_t pooled, - size_t count) NOEXCEPT + const hash_digest& hash, size_t height, size_t pooled, size_t count, + const gate_t::ptr&) NOEXCEPT { BC_ASSERT(stranded()); diff --git a/test/functional/p2p_compact.cpp b/test/functional/p2p_compact.cpp index 5bb8edeb..7019389e 100644 --- a/test/functional/p2p_compact.cpp +++ b/test/functional/p2p_compact.cpp @@ -176,6 +176,47 @@ BOOST_AUTO_TEST_CASE(functional_p2p_compact__compact_block__short_id__requested_ BOOST_REQUIRE(associated(block.hash())); } +BOOST_AUTO_TEST_CASE(functional_p2p_compact__compact_block__replayed_while_organizing__requested_once) +{ + BOOST_REQUIRE(handshake(full_node)); + + const auto& block = block1(); + send(shortened(block), node_version->value); + send(shortened(block), node_version->value); + send(ping{ 42 }, node_version->value); + + receive(get_compact_transactions::command); + BOOST_REQUIRE(!received(get_compact_transactions::command, pong::command)); +} + +BOOST_AUTO_TEST_CASE(functional_p2p_compact__compact_block__replayed_while_submitting__not_requested) +{ + BOOST_REQUIRE(handshake(full_node)); + + const auto& block = block1(); + send(shortened(block), node_version->value); + receive(get_compact_transactions::command); + + send(compact_transactions{ block.hash(), { block.transactions_ptr()->front() } }, node_version->value); + send(shortened(block), node_version->value); + send(ping{ 42 }, node_version->value); + + BOOST_REQUIRE(!received(get_compact_transactions::command, pong::command)); + BOOST_REQUIRE(associated(block.hash())); +} + +// An unstored parent is resolved by header synchronization. +BOOST_AUTO_TEST_CASE(functional_p2p_compact__compact_block__orphan__not_stopped) +{ + BOOST_REQUIRE(handshake(full_node)); + + const auto& block = block2(); + send(prefilled(block, block.transactions_ptr()->front()), node_version->value); + send(ping{ 42 }, node_version->value); + + BOOST_REQUIRE(!receive(pong::command).empty()); +} + // The pool holds none of the block, so it is downloaded (not requested). BOOST_FIXTURE_TEST_CASE(functional_p2p_compact__compact_block__short_id_unpooled__not_requested, p2p_compact_pooled_setup_fixture) {