From ae0ec293c2357f7d5e998398148c7b0deede1c6b Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Mon, 21 Sep 2026 10:10:32 -0400 Subject: [PATCH 1/5] Retain the read gate across out-of-line protocol work. --- .../node/protocols/protocol_block_out_106.hpp | 4 ++-- .../node/protocols/protocol_block_out_70001.hpp | 2 +- .../node/protocols/protocol_filter_out_70015.hpp | 3 ++- include/bitcoin/node/protocols/protocol_peer.hpp | 1 + .../node/protocols/protocol_transaction_in_106.hpp | 6 ++++-- .../protocols/protocol_transaction_in_70013.hpp | 3 ++- .../node/protocols/protocol_transaction_out_106.hpp | 6 ++++-- .../protocols/protocol_transaction_out_70001.hpp | 4 ++-- src/protocols/protocol_block_out_106.cpp | 13 +++++++------ src/protocols/protocol_block_out_70001.cpp | 5 +++-- src/protocols/protocol_filter_out_70015.cpp | 6 +++--- src/protocols/protocol_transaction_in_106.cpp | 10 ++++++---- src/protocols/protocol_transaction_in_70013.cpp | 5 +++-- src/protocols/protocol_transaction_out_106.cpp | 11 ++++++----- src/protocols/protocol_transaction_out_70001.cpp | 5 +++-- 15 files changed, 49 insertions(+), 35 deletions(-) diff --git a/include/bitcoin/node/protocols/protocol_block_out_106.hpp b/include/bitcoin/node/protocols/protocol_block_out_106.hpp index 7e3c8f96..995305a8 100644 --- a/include/bitcoin/node/protocols/protocol_block_out_106.hpp +++ b/include/bitcoin/node/protocols/protocol_block_out_106.hpp @@ -71,13 +71,13 @@ 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) NOEXCEPT; + virtual void send_block(const code& ec, const gate_t::ptr& gate) NOEXCEPT; /// The item cannot be served, stops the channel and returns false. virtual bool handle_unservable(const inventory_item& item) NOEXCEPT; /// Send any unservable items accumulated above, false if none. - virtual bool report_unservable() NOEXCEPT; + virtual bool report_unservable(const gate_t::ptr& gate) NOEXCEPT; private: using inventory = network::messages::peer::inventory; diff --git a/include/bitcoin/node/protocols/protocol_block_out_70001.hpp b/include/bitcoin/node/protocols/protocol_block_out_70001.hpp index 09104759..a4bfe2cf 100644 --- a/include/bitcoin/node/protocols/protocol_block_out_70001.hpp +++ b/include/bitcoin/node/protocols/protocol_block_out_70001.hpp @@ -45,7 +45,7 @@ class BCN_API protocol_block_out_70001 bool handle_unservable(const inventory_item& item) NOEXCEPT override; /// Replies not_found with the accumulated items, false if none. - bool report_unservable() NOEXCEPT override; + bool report_unservable(const gate_t::ptr& gate) NOEXCEPT override; private: // This is thread safe. diff --git a/include/bitcoin/node/protocols/protocol_filter_out_70015.hpp b/include/bitcoin/node/protocols/protocol_filter_out_70015.hpp index f15d16e7..53b8b102 100644 --- a/include/bitcoin/node/protocols/protocol_filter_out_70015.hpp +++ b/include/bitcoin/node/protocols/protocol_filter_out_70015.hpp @@ -52,7 +52,8 @@ class BCN_API protocol_filter_out_70015 private: using ancestry_ptr = std::shared_ptr; - void send_filter(const code& ec, const ancestry_ptr& ancestry) NOEXCEPT; + void send_filter(const code& ec, const ancestry_ptr& ancestry, + const gate_t::ptr& gate) NOEXCEPT; }; } // namespace node diff --git a/include/bitcoin/node/protocols/protocol_peer.hpp b/include/bitcoin/node/protocols/protocol_peer.hpp index 743323e2..1f3504fc 100644 --- a/include/bitcoin/node/protocols/protocol_peer.hpp +++ b/include/bitcoin/node/protocols/protocol_peer.hpp @@ -37,6 +37,7 @@ class BCN_API protocol_peer public: // Replace base class channel_t (network::channel_peer). using channel_t = node::channel_peer; + using gate_t = network::channel_peer::gate_t; protected: /// Constructors. diff --git a/include/bitcoin/node/protocols/protocol_transaction_in_106.hpp b/include/bitcoin/node/protocols/protocol_transaction_in_106.hpp index 606866c1..5d667fc7 100644 --- a/include/bitcoin/node/protocols/protocol_transaction_in_106.hpp +++ b/include/bitcoin/node/protocols/protocol_transaction_in_106.hpp @@ -56,8 +56,10 @@ class BCN_API protocol_transaction_in_106 /// Accept incoming transaction message. virtual bool handle_receive_transaction(const code& ec, const network::messages::peer::transaction::cptr& message) NOEXCEPT; - virtual void handle_submit(const code& ec, size_t index) NOEXCEPT; - virtual void do_handle_submit(const code& ec) NOEXCEPT; + virtual void handle_submit(const code& ec, size_t index, + const gate_t::ptr& gate) NOEXCEPT; + virtual void do_handle_submit(const code& ec, + const gate_t::ptr& gate) NOEXCEPT; private: /// Squash duplicates and provide constant time retrieval. diff --git a/include/bitcoin/node/protocols/protocol_transaction_in_70013.hpp b/include/bitcoin/node/protocols/protocol_transaction_in_70013.hpp index cb37d00e..0db53f90 100644 --- a/include/bitcoin/node/protocols/protocol_transaction_in_70013.hpp +++ b/include/bitcoin/node/protocols/protocol_transaction_in_70013.hpp @@ -41,7 +41,8 @@ class BCN_API protocol_transaction_in_70013 protected: /// The peer is advised of the fee rate and of suspension (see fee_filter). - void do_handle_submit(const code& ec) NOEXCEPT override; + void do_handle_submit(const code& ec, + const gate_t::ptr& gate) NOEXCEPT override; }; } // namespace node diff --git a/include/bitcoin/node/protocols/protocol_transaction_out_106.hpp b/include/bitcoin/node/protocols/protocol_transaction_out_106.hpp index b515b23b..fe4a01ef 100644 --- a/include/bitcoin/node/protocols/protocol_transaction_out_106.hpp +++ b/include/bitcoin/node/protocols/protocol_transaction_out_106.hpp @@ -59,7 +59,8 @@ class BCN_API protocol_transaction_out_106 virtual bool handle_receive_get_data(const code& ec, const network::messages::peer::get_data::cptr& message) NOEXCEPT; virtual void send_transaction(const code& ec, size_t index, - const network::messages::peer::get_data::cptr& message) NOEXCEPT; + const network::messages::peer::get_data::cptr& message, + const gate_t::ptr& gate) NOEXCEPT; /// The item cannot be served, false terminates the send loop. virtual bool handle_unservable( @@ -67,7 +68,8 @@ class BCN_API protocol_transaction_out_106 /// Replies not_found with the accumulated items, false if none. virtual bool report_unservable(size_t index, - const network::messages::peer::get_data::cptr& message) NOEXCEPT; + const network::messages::peer::get_data::cptr& message, + const gate_t::ptr& gate) NOEXCEPT; virtual bool announce(const system::hash_digest& hash) NOEXCEPT; diff --git a/include/bitcoin/node/protocols/protocol_transaction_out_70001.hpp b/include/bitcoin/node/protocols/protocol_transaction_out_70001.hpp index 021af7b9..b02510c8 100644 --- a/include/bitcoin/node/protocols/protocol_transaction_out_70001.hpp +++ b/include/bitcoin/node/protocols/protocol_transaction_out_70001.hpp @@ -47,8 +47,8 @@ class BCN_API protocol_transaction_out_70001 /// Replies not_found with the accumulated items, false if none. bool report_unservable(size_t index, - const network::messages::peer::get_data::cptr& message) NOEXCEPT - override; + const network::messages::peer::get_data::cptr& message, + const gate_t::ptr& gate) NOEXCEPT override; private: // This is thread safe. diff --git a/src/protocols/protocol_block_out_106.cpp b/src/protocols/protocol_block_out_106.cpp index ccd1d63a..aa0e68b5 100644 --- a/src/protocols/protocol_block_out_106.cpp +++ b/src/protocols/protocol_block_out_106.cpp @@ -190,7 +190,7 @@ bool protocol_block_out_106::handle_receive_get_data(const code& ec, // Bump the idle async send loop if no pending send. if (idle) - send_block(error::success); + send_block(error::success, gate()); return true; } @@ -198,7 +198,8 @@ bool protocol_block_out_106::handle_receive_get_data(const code& ec, // Outbound (block). // ---------------------------------------------------------------------------- -void protocol_block_out_106::send_block(const code& ec) NOEXCEPT +void protocol_block_out_106::send_block(const code& ec, + const gate_t::ptr& gate) NOEXCEPT { BC_ASSERT(stranded()); if (stopped(ec)) @@ -230,7 +231,7 @@ void protocol_block_out_106::send_block(const code& ec) NOEXCEPT } // The report resumes this loop on completion, so it precedes the block. - if (report_unservable()) + if (report_unservable(gate)) return; if (backlog_.empty()) return; @@ -251,14 +252,14 @@ void protocol_block_out_106::send_block(const code& ec) NOEXCEPT backlog_.pop_front(); if (handle_unservable(item)) - report_unservable(); + report_unservable(gate); return; } backlog_.pop_front(); span(events::block_usecs, start); - SEND(std::move(out), send_block, _1); + SEND(std::move(out), send_block, _1, gate); } // The checkpoint, milestone and association queries assume an archived header. @@ -308,7 +309,7 @@ bool protocol_block_out_106::handle_unservable( } // There is nothing to report below bip37, the channel is stopped above. -bool protocol_block_out_106::report_unservable() NOEXCEPT +bool protocol_block_out_106::report_unservable(const gate_t::ptr&) NOEXCEPT { BC_ASSERT(stranded()); return false; diff --git a/src/protocols/protocol_block_out_70001.cpp b/src/protocols/protocol_block_out_70001.cpp index 06bc39b0..7902a2f3 100644 --- a/src/protocols/protocol_block_out_70001.cpp +++ b/src/protocols/protocol_block_out_70001.cpp @@ -52,7 +52,8 @@ bool protocol_block_out_70001::handle_unservable( // The items are answered and the send loop resumed, as with a block, so // nothing is produced until the prior write completes. -bool protocol_block_out_70001::report_unservable() NOEXCEPT +bool protocol_block_out_70001::report_unservable( + const gate_t::ptr& gate) NOEXCEPT { BC_ASSERT(stranded()); @@ -62,7 +63,7 @@ bool protocol_block_out_70001::report_unservable() NOEXCEPT auto items = std::move(unservable_); unservable_.clear(); - SEND(not_found{ std::move(items) }, send_block, _1); + SEND(not_found{ std::move(items) }, send_block, _1, gate); return true; } diff --git a/src/protocols/protocol_filter_out_70015.cpp b/src/protocols/protocol_filter_out_70015.cpp index 52bd329d..be211f17 100644 --- a/src/protocols/protocol_filter_out_70015.cpp +++ b/src/protocols/protocol_filter_out_70015.cpp @@ -222,12 +222,12 @@ bool protocol_filter_out_70015::handle_receive_get_filters(const code& ec, // Post so the completion resubscribe runs outside the current notify(). span(events::ancestry_msecs, start); - POST(send_filter, error::success, ancestry); + POST(send_filter, error::success, ancestry, gate()); return false; } void protocol_filter_out_70015::send_filter(const code& ec, - const ancestry_ptr& ancestry) NOEXCEPT + const ancestry_ptr& ancestry, const gate_t::ptr& gate) NOEXCEPT { BC_ASSERT(stranded()); if (stopped(ec)) @@ -255,7 +255,7 @@ void protocol_filter_out_70015::send_filter(const code& ec, out.block_hash = query.get_header_key(link); out.filter_type = client_filter::type_id::neutrino; span(events::filter_msecs, start); - SEND(out, send_filter, _1, ancestry); + SEND(out, send_filter, _1, ancestry, gate); } BC_POP_WARNING() diff --git a/src/protocols/protocol_transaction_in_106.cpp b/src/protocols/protocol_transaction_in_106.cpp index 71a79088..3b331f70 100644 --- a/src/protocols/protocol_transaction_in_106.cpp +++ b/src/protocols/protocol_transaction_in_106.cpp @@ -148,19 +148,21 @@ bool protocol_transaction_in_106::handle_receive_transaction(const code& ec, } submit(to_shared(chain::transaction_cptrs{ tx }), false, - BIND(handle_submit, _1, _2)); + BIND(handle_submit, _1, _2, gate())); return true; } // protected -void protocol_transaction_in_106::handle_submit(const code& ec, size_t) NOEXCEPT +void protocol_transaction_in_106::handle_submit(const code& ec, size_t, + const gate_t::ptr& gate) NOEXCEPT { - POST(do_handle_submit, ec); + POST(do_handle_submit, ec, gate); } // protected -void protocol_transaction_in_106::do_handle_submit(const code& ec) NOEXCEPT +void protocol_transaction_in_106::do_handle_submit(const code& ec, + const gate_t::ptr&) NOEXCEPT { BC_ASSERT(stranded()); diff --git a/src/protocols/protocol_transaction_in_70013.cpp b/src/protocols/protocol_transaction_in_70013.cpp index 7457e8cb..0dddc1f2 100644 --- a/src/protocols/protocol_transaction_in_70013.cpp +++ b/src/protocols/protocol_transaction_in_70013.cpp @@ -36,7 +36,8 @@ BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR) // bip133: the peer is sent our rate when current and the maximum otherwise, so // a tx below the rate, or any tx while suspended, is sent against instruction. -void protocol_transaction_in_70013::do_handle_submit(const code& ec) NOEXCEPT +void protocol_transaction_in_70013::do_handle_submit(const code& ec, + const gate_t::ptr& gate) NOEXCEPT { BC_ASSERT(stranded()); @@ -50,7 +51,7 @@ void protocol_transaction_in_70013::do_handle_submit(const code& ec) NOEXCEPT return; } - protocol_transaction_in_70001::do_handle_submit(ec); + protocol_transaction_in_70001::do_handle_submit(ec, gate); } BC_POP_WARNING() diff --git a/src/protocols/protocol_transaction_out_106.cpp b/src/protocols/protocol_transaction_out_106.cpp index a2b71837..2b8bd3e9 100644 --- a/src/protocols/protocol_transaction_out_106.cpp +++ b/src/protocols/protocol_transaction_out_106.cpp @@ -134,7 +134,7 @@ bool protocol_transaction_out_106::handle_receive_get_data(const code& ec, return false; // Post so the completion resubscribe runs outside the current notify(). - POST(send_transaction, error::success, zero, message); + POST(send_transaction, error::success, zero, message, gate()); return false; } @@ -146,7 +146,8 @@ bool protocol_transaction_out_106::handle_receive_get_data(const code& ec, // using MSG_TX getdata messages." (derived protocol) void protocol_transaction_out_106::send_transaction(const code& ec, - size_t index, const get_data::cptr& message) NOEXCEPT + size_t index, const get_data::cptr& message, + const gate_t::ptr& gate) NOEXCEPT { BC_ASSERT(stranded()); @@ -189,7 +190,7 @@ void protocol_transaction_out_106::send_transaction(const code& ec, } // The report resumes this loop on completion, so it precedes the tx. - if (report_unservable(index, message)) + if (report_unservable(index, message, gate)) return; // BUGBUG: registration race. @@ -200,7 +201,7 @@ void protocol_transaction_out_106::send_transaction(const code& ec, return; } - SEND(transaction{ ptr }, send_transaction, _1, add1(index), message); + SEND(transaction{ ptr }, send_transaction, _1, add1(index), message, gate); } // not_found is undefined below bip37, so the channel is stopped instead. @@ -218,7 +219,7 @@ bool protocol_transaction_out_106::handle_unservable( // There is nothing to report below bip37, the channel is stopped above. bool protocol_transaction_out_106::report_unservable(size_t, - const get_data::cptr&) NOEXCEPT + const get_data::cptr&, const gate_t::ptr&) NOEXCEPT { return false; } diff --git a/src/protocols/protocol_transaction_out_70001.cpp b/src/protocols/protocol_transaction_out_70001.cpp index dbe638ee..5a9a415b 100644 --- a/src/protocols/protocol_transaction_out_70001.cpp +++ b/src/protocols/protocol_transaction_out_70001.cpp @@ -52,7 +52,7 @@ bool protocol_transaction_out_70001::handle_unservable( // The items are answered and the send loop resumed, as with a transaction, so // nothing is produced until the prior write completes. bool protocol_transaction_out_70001::report_unservable(size_t index, - const get_data::cptr& message) NOEXCEPT + const get_data::cptr& message, const gate_t::ptr& gate) NOEXCEPT { BC_ASSERT(stranded()); @@ -62,7 +62,8 @@ bool protocol_transaction_out_70001::report_unservable(size_t index, auto items = std::move(unservable_); unservable_.clear(); - SEND(not_found{ std::move(items) }, send_transaction, _1, index, message); + SEND(not_found{ std::move(items) }, send_transaction, _1, index, + message, gate); return true; } From cf1a17dec7172a7ae25dbcb3dc3817e7252e51dc Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Mon, 21 Sep 2026 14:18:18 -0400 Subject: [PATCH 2/5] Drop allow_overlapped and implementation. --- .../node/protocols/protocol_block_out_106.hpp | 15 ++-- .../protocols/protocol_block_out_70001.hpp | 3 +- include/bitcoin/node/settings.hpp | 1 - src/protocols/protocol_block_out_106.cpp | 69 +++++-------------- src/protocols/protocol_block_out_70001.cpp | 6 +- src/protocols/protocol_filter_out_70015.cpp | 9 +-- .../protocol_transaction_out_106.cpp | 10 +-- src/settings.cpp | 1 - test/settings.cpp | 1 - 9 files changed, 32 insertions(+), 83 deletions(-) diff --git a/include/bitcoin/node/protocols/protocol_block_out_106.hpp b/include/bitcoin/node/protocols/protocol_block_out_106.hpp index 995305a8..d58e31ea 100644 --- a/include/bitcoin/node/protocols/protocol_block_out_106.hpp +++ b/include/bitcoin/node/protocols/protocol_block_out_106.hpp @@ -19,7 +19,6 @@ #ifndef LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_BLOCK_OUT_106_HPP #define LIBBITCOIN_NODE_PROTOCOLS_PROTOCOL_BLOCK_OUT_106_HPP -#include #include #include @@ -40,8 +39,7 @@ class BCN_API protocol_block_out_106 session->system_settings().top_checkpoint().height()), node_pruned_(session->node_settings().limited_blocks), node_witness_(session->node_settings().provide_witness), - allow_overlapped_(session->node_settings().allow_overlapped), - network::tracker(session->log) + network::tracker(session->log) { } @@ -71,13 +69,15 @@ 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 gate_t::ptr& gate) NOEXCEPT; + virtual void send_block(const code& ec, size_t index, + const get_data::cptr& message, const gate_t::ptr& gate) NOEXCEPT; /// The item cannot be served, stops the channel and returns false. virtual bool handle_unservable(const inventory_item& item) NOEXCEPT; /// Send any unservable items accumulated above, false if none. - virtual bool report_unservable(const gate_t::ptr& gate) NOEXCEPT; + virtual bool report_unservable(size_t index, + const get_data::cptr& message, const gate_t::ptr& gate) NOEXCEPT; private: using inventory = network::messages::peer::inventory; @@ -86,16 +86,11 @@ class BCN_API protocol_block_out_106 const database::header_link& link) NOEXCEPT; bool is_under_checkpoint(const database::header_link& link) NOEXCEPT; inventory create_inventory(const get_blocks& locator) const NOEXCEPT; - void merge_inventory(const inventory_items& items) NOEXCEPT; // These are thread safe. const size_t top_checkpoint_height_; const bool node_pruned_; const bool node_witness_; - const bool allow_overlapped_; - - // This is protected by strand. - std::deque backlog_{}; }; } // namespace node diff --git a/include/bitcoin/node/protocols/protocol_block_out_70001.hpp b/include/bitcoin/node/protocols/protocol_block_out_70001.hpp index a4bfe2cf..f9ed70d3 100644 --- a/include/bitcoin/node/protocols/protocol_block_out_70001.hpp +++ b/include/bitcoin/node/protocols/protocol_block_out_70001.hpp @@ -45,7 +45,8 @@ class BCN_API protocol_block_out_70001 bool handle_unservable(const inventory_item& item) NOEXCEPT override; /// Replies not_found with the accumulated items, false if none. - bool report_unservable(const gate_t::ptr& gate) NOEXCEPT override; + bool report_unservable(size_t index, const get_data::cptr& message, + const gate_t::ptr& gate) NOEXCEPT override; private: // This is thread safe. diff --git a/include/bitcoin/node/settings.hpp b/include/bitcoin/node/settings.hpp index d398af19..d6d48501 100644 --- a/include/bitcoin/node/settings.hpp +++ b/include/bitcoin/node/settings.hpp @@ -39,7 +39,6 @@ class BCN_API settings bool headers_first; bool thread_priority; bool memory_priority; - bool allow_overlapped; bool provide_blocks; bool require_blocks; bool provide_witness; diff --git a/src/protocols/protocol_block_out_106.cpp b/src/protocols/protocol_block_out_106.cpp index aa0e68b5..ed7699da 100644 --- a/src/protocols/protocol_block_out_106.cpp +++ b/src/protocols/protocol_block_out_106.cpp @@ -163,43 +163,15 @@ bool protocol_block_out_106::handle_receive_get_data(const code& ec, if (is_zero(size)) return true; - const auto total = ceilinged_add(backlog_.size(), size); - if (total > network::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; - } - - // 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, gate()); - + send_block(error::success, zero, message, gate()); return true; } // Outbound (block). // ---------------------------------------------------------------------------- -void protocol_block_out_106::send_block(const code& ec, - const gate_t::ptr& gate) NOEXCEPT +void protocol_block_out_106::send_block(const code& ec, size_t index, + const get_data::cptr& message, const gate_t::ptr& gate) NOEXCEPT { BC_ASSERT(stranded()); if (stopped(ec)) @@ -207,13 +179,16 @@ void protocol_block_out_106::send_block(const code& ec, const auto& query = archive(); - // Drain unservable items from the front of the backlog. The derived + // Drain unservable items, skipping non-block inventory. The derived // protocol accumulates them if it reports them, and otherwise stops the - // channel on the first. Copied because each is handled after the pop. + // channel on the first. database::header_link link{}; - while (!backlog_.empty()) + for (; index < message->items.size(); ++index) { - const auto item = backlog_.front(); + const auto& item = message->items.at(index); + if (!item.is_block()) + continue; + if (item.is_witness_type() && !node_witness_) { LOGR("Unsupported witness get_data from [" << opposite() << "]."); @@ -225,18 +200,18 @@ void protocol_block_out_106::send_block(const code& ec, if (is_servable(item, link)) break; - backlog_.pop_front(); if (!handle_unservable(item)) return; } // The report resumes this loop on completion, so it precedes the block. - if (report_unservable(gate)) + if (report_unservable(index, message, gate)) return; - if (backlog_.empty()) return; + if (index >= message->items.size()) + return; - const auto item = backlog_.front(); + const auto& item = message->items.at(index); const auto witness = item.is_witness_type(); const auto start = logger::now(); messages::peer::block out @@ -250,16 +225,14 @@ void protocol_block_out_106::send_block(const code& ec, LOGV("Requested block " << encode_hash(item.hash) << " from [" << opposite() << "] not obtained."); - backlog_.pop_front(); if (handle_unservable(item)) - report_unservable(gate); + report_unservable(add1(index), message, gate); return; } - backlog_.pop_front(); span(events::block_usecs, start); - SEND(std::move(out), send_block, _1, gate); + SEND(std::move(out), send_block, _1, add1(index), message, gate); } // The checkpoint, milestone and association queries assume an archived header. @@ -309,7 +282,8 @@ bool protocol_block_out_106::handle_unservable( } // There is nothing to report below bip37, the channel is stopped above. -bool protocol_block_out_106::report_unservable(const gate_t::ptr&) NOEXCEPT +bool protocol_block_out_106::report_unservable(size_t, + const get_data::cptr&, const gate_t::ptr&) NOEXCEPT { BC_ASSERT(stranded()); return false; @@ -318,13 +292,6 @@ bool protocol_block_out_106::report_unservable(const gate_t::ptr&) NOEXCEPT // 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 diff --git a/src/protocols/protocol_block_out_70001.cpp b/src/protocols/protocol_block_out_70001.cpp index 7902a2f3..2ce81d94 100644 --- a/src/protocols/protocol_block_out_70001.cpp +++ b/src/protocols/protocol_block_out_70001.cpp @@ -52,8 +52,8 @@ bool protocol_block_out_70001::handle_unservable( // The items are answered and the send loop resumed, as with a block, so // nothing is produced until the prior write completes. -bool protocol_block_out_70001::report_unservable( - const gate_t::ptr& gate) NOEXCEPT +bool protocol_block_out_70001::report_unservable(size_t index, + const get_data::cptr& message, const gate_t::ptr& gate) NOEXCEPT { BC_ASSERT(stranded()); @@ -63,7 +63,7 @@ bool protocol_block_out_70001::report_unservable( auto items = std::move(unservable_); unservable_.clear(); - SEND(not_found{ std::move(items) }, send_block, _1, gate); + SEND(not_found{ std::move(items) }, send_block, _1, index, message, gate); return true; } diff --git a/src/protocols/protocol_filter_out_70015.cpp b/src/protocols/protocol_filter_out_70015.cpp index be211f17..46a16d9f 100644 --- a/src/protocols/protocol_filter_out_70015.cpp +++ b/src/protocols/protocol_filter_out_70015.cpp @@ -220,10 +220,9 @@ bool protocol_filter_out_70015::handle_receive_get_filters(const code& ec, return false; } - // Post so the completion resubscribe runs outside the current notify(). span(events::ancestry_msecs, start); - POST(send_filter, error::success, ancestry, gate()); - return false; + send_filter(error::success, ancestry, gate()); + return true; } void protocol_filter_out_70015::send_filter(const code& ec, @@ -234,11 +233,7 @@ void protocol_filter_out_70015::send_filter(const code& ec, return; if (ancestry->empty()) - { - // Complete, resubscribe to get_client_filters. - SUBSCRIBE_CHANNEL(get_client_filters, handle_receive_get_filters, _1, _2); return; - } const auto& query = archive(); const auto start = logger::now(); diff --git a/src/protocols/protocol_transaction_out_106.cpp b/src/protocols/protocol_transaction_out_106.cpp index 2b8bd3e9..a4a66553 100644 --- a/src/protocols/protocol_transaction_out_106.cpp +++ b/src/protocols/protocol_transaction_out_106.cpp @@ -133,9 +133,8 @@ bool protocol_transaction_out_106::handle_receive_get_data(const code& ec, if (stopped(ec)) return false; - // Post so the completion resubscribe runs outside the current notify(). - POST(send_transaction, error::success, zero, message, gate()); - return false; + send_transaction(error::success, zero, message, gate()); + return true; } // Outbound (tx). @@ -193,13 +192,8 @@ void protocol_transaction_out_106::send_transaction(const code& ec, if (report_unservable(index, message, gate)) return; - // BUGBUG: registration race. if (index >= message->items.size()) - { - // Complete, resubscribe to transaction requests. - SUBSCRIBE_CHANNEL(get_data, handle_receive_get_data, _1, _2); return; - } SEND(transaction{ ptr }, send_transaction, _1, add1(index), message, gate); } diff --git a/src/settings.cpp b/src/settings.cpp index 5c0fe32e..f699509b 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -34,7 +34,6 @@ settings::settings() NOEXCEPT headers_first{ true }, memory_priority{ true }, thread_priority{ true }, - allow_overlapped{ true }, provide_blocks{ true }, require_blocks{ true }, provide_witness{ true }, diff --git a/test/settings.cpp b/test/settings.cpp index ab380d32..534b63bb 100644 --- a/test/settings.cpp +++ b/test/settings.cpp @@ -35,7 +35,6 @@ BOOST_AUTO_TEST_CASE(settings__node__default_context__expected) 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.provide_blocks, true); BOOST_REQUIRE_EQUAL(node.require_blocks, true); BOOST_REQUIRE_EQUAL(node.provide_witness, true); From 10c3a10ee290b438a247f4ff77f1abec04d19c3b Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Mon, 21 Sep 2026 17:28:32 -0400 Subject: [PATCH 3/5] Fix stale debug assertion. --- include/bitcoin/node/impl/sessions/session_peer.ipp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/bitcoin/node/impl/sessions/session_peer.ipp b/include/bitcoin/node/impl/sessions/session_peer.ipp index 95e62383..16ef7727 100644 --- a/include/bitcoin/node/impl/sessions/session_peer.ipp +++ b/include/bitcoin/node/impl/sessions/session_peer.ipp @@ -50,7 +50,7 @@ inline void CLASS::attach_handshake(const channel_ptr& channel, network::result_handler&& handler) NOEXCEPT { BC_ASSERT(channel->stranded()); - BC_ASSERT(channel->paused()); + BC_ASSERT(!channel->held()); // Set the current top for version protocol, before handshake. const auto top = this->archive().get_top_confirmed(); @@ -65,7 +65,7 @@ TEMPLATE inline void CLASS::attach_protocols(const channel_ptr& channel) NOEXCEPT { BC_ASSERT(channel->stranded()); - BC_ASSERT(channel->paused()); + BC_ASSERT(channel->held()); using namespace system; using namespace network; From e52e31a86097a071d55ff14856ca98c028184b1c Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Mon, 21 Sep 2026 21:00:10 -0400 Subject: [PATCH 4/5] Replace the chase event enum parameter with typed event payloads. --- include/bitcoin/node/chase.hpp | 329 ++++++++++++++++-- include/bitcoin/node/chasers/chaser.hpp | 5 +- include/bitcoin/node/chasers/chaser_check.hpp | 3 +- .../bitcoin/node/chasers/chaser_confirm.hpp | 3 +- .../bitcoin/node/chasers/chaser_estimate.hpp | 3 +- .../bitcoin/node/chasers/chaser_organize.hpp | 21 +- .../bitcoin/node/chasers/chaser_snapshot.hpp | 3 +- .../bitcoin/node/chasers/chaser_storage.hpp | 3 +- .../bitcoin/node/chasers/chaser_template.hpp | 3 +- .../node/chasers/chaser_transaction.hpp | 3 +- .../bitcoin/node/chasers/chaser_validate.hpp | 3 +- include/bitcoin/node/define.hpp | 30 +- include/bitcoin/node/full_node.hpp | 9 +- .../node/impl/chasers/chaser_organize.ipp | 43 ++- .../protocols/protocol_block_in_31800.hpp | 3 +- .../node/protocols/protocol_block_out_106.hpp | 3 +- .../protocols/protocol_header_out_70012.hpp | 3 +- .../node/protocols/protocol_observer.hpp | 3 +- .../bitcoin/node/protocols/protocol_peer.hpp | 5 +- .../protocol_transaction_out_106.hpp | 3 +- .../protocol_transaction_out_70013.hpp | 3 +- include/bitcoin/node/sessions/session.hpp | 5 +- src/chasers/chaser.cpp | 9 +- src/chasers/chaser_check.cpp | 40 +-- src/chasers/chaser_confirm.cpp | 35 +- src/chasers/chaser_estimate.cpp | 18 +- src/chasers/chaser_snapshot.cpp | 13 +- src/chasers/chaser_storage.cpp | 9 +- src/chasers/chaser_template.cpp | 10 +- src/chasers/chaser_transaction.cpp | 9 +- src/chasers/chaser_validate.cpp | 37 +- src/full_node.cpp | 30 +- src/protocols/protocol_block_in_31800.cpp | 18 +- src/protocols/protocol_block_out_106.cpp | 10 +- src/protocols/protocol_header_out_70012.cpp | 10 +- src/protocols/protocol_observer.cpp | 7 +- src/protocols/protocol_peer.cpp | 9 +- .../protocol_transaction_out_106.cpp | 10 +- .../protocol_transaction_out_70013.cpp | 6 +- src/sessions/session.cpp | 9 +- 40 files changed, 496 insertions(+), 282 deletions(-) diff --git a/include/bitcoin/node/chase.hpp b/include/bitcoin/node/chase.hpp index 3a691ae2..faa11d8c 100644 --- a/include/bitcoin/node/chase.hpp +++ b/include/bitcoin/node/chase.hpp @@ -19,37 +19,49 @@ #ifndef LIBBITCOIN_NODE_CHASE_HPP #define LIBBITCOIN_NODE_CHASE_HPP +#include #include namespace libbitcoin { namespace node { +/// Event desubscriber key type. +using object_key = uint64_t; + +/// Event payload types. +using count_t = size_t; +using height_t = size_t; +using peer_t = uint64_t; +using object_t = object_key; +using header_t = database::header_link::integer; +using transaction_t = database::tx_link::integer; + enum class chase { /// Work shuffling. /// ----------------------------------------------------------------------- - /// Chasers directed to start operating (height_t). + /// Chasers directed to start operating. /// Issued by 'full_node' and handled by 'check', 'validate', 'confirm'. start, - /// Disk space is limited (count_t). - /// Issued by 'full_node' and handled by 'snapshot' and 'storage'. + /// Disk space is limited. + /// Issued by 'full_node' and handled by 'storage'. space, - /// Take a snapshot (height_t). + /// Take a snapshot. /// Issued by 'confirm' and handled by 'snapshot'. snap, - /// Chaser directed to attempt start from its current position (height_t). + /// Chaser directed to attempt start from its current position. /// Issued by 'organize' and handled by 'check', 'validate', 'confirm'. bump, - /// Channels (all) directed to stop with the given code (default). + /// Channels (all) directed to stop with the given code. /// Issued by 'full_node' and handled by 'observer'. suspend, - /// Chasers (all) directed to resume following suspend (default). + /// Chasers (all) directed to resume following suspend. /// Issued by 'full_node' and handled by 'check', 'validate', 'confirm'. resume, @@ -57,38 +69,38 @@ enum class chase /// Issued by 'storage' and handled by 'validate'. unfull, - /// Channel starved for work (object_t). - /// Issued by 'block_in_31800' and handled by 'session_outbound'. + /// Channel starved for work. + /// Issued by 'block_in_31800' and handled by 'check'. starved, - /// Channel (slow) directed to split work and stop (object_t). - /// Issued by 'session_outbound' and handled by 'block_in_31800'. + /// Channel (slow) directed to split work and stop. + /// Issued by 'check' and handled by 'block_in_31800'. split, - /// Channels (all with work) directed to split work and stop (peer_t). - /// Issued by 'session_outbound' and handled by 'block_in_31800'. + /// Channels (all with work) directed to split work and stop. + /// Issued by 'check' and handled by 'block_in_31800'. stall, - /// Channels (all with work) directed to drop work and stop (peer_t). + /// Channels (all with work) directed to drop work and stop. /// Issued by 'check' and handled by 'block_in_31800'. purge, - /// Channels (all) directed to write work count to the log (count_t). + /// Channels (all) directed to write work count to the log. /// Issued by 'executor' and handled by 'block_in_31800'. report, /// Candidate Chain. /// ----------------------------------------------------------------------- - /// A new candidate branch exists from given branch point (height_t). - /// Issued by 'block' and handled by 'confirm' and 'snapshot'. + /// A new candidate branch exists from given branch point. + /// Issued by 'block' and handled by none. blocks, - /// A new candidate branch exists from given branch point (height_t). + /// A new candidate branch exists from given branch point. /// Issued by 'header' and handled by 'check'. headers, - /// New candidate headers without txs exist (count_t). + /// New candidate headers without txs exist. /// Issued by 'check' and handled by 'block_in_31800'. download, @@ -96,85 +108,328 @@ enum class chase /// Issued by 'organize' and handled by 'check', 'validate', 'confirm'. regressed, - /// unchecked, unvalid or unconfirmable was handled (height_t). + /// unchecked, unvalid or unconfirmable was handled. /// Issued by 'organize' and handled by 'check', 'validate', 'confirm'. disorganized, /// Check/Identify. /// ----------------------------------------------------------------------- - /// A block has been downloaded, checked and stored (height_t). - /// Issued by 'block_in_31800', handled by 'check', 'validate', 'snapshot'. + /// A block has been downloaded, checked and stored. + /// Issued by 'block_in_31800', handled by 'check', 'validate'. /// Populate is bypassed for checkpoint/milestone blocks. checked, - /// A downloaded block has failed check (header_t). + /// A downloaded block has failed check. /// Issued by 'block_in_31800' and handled by 'organize'. unchecked, - /// A downloaded window is completed by check (height_t). + /// A downloaded window is completed by check. /// Issued by 'check' and handled by 'validate'. windowed, /// Accept/Connect. /// ----------------------------------------------------------------------- - /// A branch has become valid (height_t). - /// Issued by 'validate' and handled by 'check', 'confirm', 'snapshot'. + /// A branch has become valid. + /// Issued by 'validate' and handled by 'check', 'confirm'. valid, - /// A checked block has failed validation (header_t). + /// A checked block has failed validation. /// Issued by 'validate' and handled by 'organize'. unvalid, /// Confirm (block). /// ----------------------------------------------------------------------- - /// A connected block has become confirmable (header_t). - /// Issued by 'confirm' and handled by 'snapshot'. + /// A connected block has become confirmable. + /// Issued by 'confirm' and handled by none. confirmable, - /// A connected block has failed confirmability (header_t). + /// A connected block has failed confirmability. /// Issued by 'confirm' and handled by 'organize'. unconfirmable, /// Confirm (chain). /// ----------------------------------------------------------------------- - /// A current block has been organized (header_t). + /// A current block has been organized. /// Issued by 'confirm' and handled by 'protocol_header/block_out/estimator'. block, - /// The confirmed chain is no longer current (default). + /// The confirmed chain is no longer current. /// Issued by 'confirm' and handled by 'protocol_transaction_out'. stale, - /// A confirmable block has been confirmed (header_t). + /// A confirmable block has been confirmed. /// Issued by 'confirm' and handled by 'transaction'. organized, - /// A previously confirmed block has been unconfirmed (header_t). + /// A previously confirmed block has been unconfirmed. /// Issued by 'confirm' and handled by 'transaction'. reorganized, /// Mining. /// ----------------------------------------------------------------------- - /// A transaction has been added to the pool (transaction_t). + /// A transaction has been added to the pool. /// Issued by 'transaction' and handled by 'template'. transaction, - /// A candidate block (template) has been created (height_t). + /// A candidate block (template) has been created. /// Issued by 'template' and handled by [miners]. template_, /// Stop. /// ----------------------------------------------------------------------- - /// Service is stopping, accompanied by error::service_stopped (default). + /// Service is stopping, accompanied by error::service_stopped. stop }; +/// Event payloads, one per chase value, declared in chase order. +namespace chases { + +struct start +{ + static constexpr chase id{ chase::start }; +}; + +struct space +{ + static constexpr chase id{ chase::space }; +}; + +struct snap +{ + static constexpr chase id{ chase::snap }; + height_t height; +}; + +struct bump +{ + static constexpr chase id{ chase::bump }; + height_t height; +}; + +struct suspend +{ + static constexpr chase id{ chase::suspend }; +}; + +struct resume +{ + static constexpr chase id{ chase::resume }; +}; + +struct unfull +{ + static constexpr chase id{ chase::unfull }; +}; + +struct starved +{ + static constexpr chase id{ chase::starved }; + object_t channel; +}; + +struct split +{ + static constexpr chase id{ chase::split }; + object_t channel; +}; + +struct stall +{ + static constexpr chase id{ chase::stall }; + object_t channel; +}; + +struct purge +{ + static constexpr chase id{ chase::purge }; + height_t branch_point; +}; + +struct report +{ + static constexpr chase id{ chase::report }; + count_t sequence; +}; + +struct blocks +{ + static constexpr chase id{ chase::blocks }; + height_t branch_point; +}; + +struct headers +{ + static constexpr chase id{ chase::headers }; + height_t branch_point; +}; + +struct download +{ + static constexpr chase id{ chase::download }; + count_t count; +}; + +struct regressed +{ + static constexpr chase id{ chase::regressed }; + height_t branch_point; +}; + +struct disorganized +{ + static constexpr chase id{ chase::disorganized }; + height_t branch_point; +}; + +struct checked +{ + static constexpr chase id{ chase::checked }; + height_t height; +}; + +struct unchecked +{ + static constexpr chase id{ chase::unchecked }; + header_t link; +}; + +struct windowed +{ + static constexpr chase id{ chase::windowed }; + height_t height; +}; + +struct valid +{ + static constexpr chase id{ chase::valid }; + height_t height; +}; + +struct unvalid +{ + static constexpr chase id{ chase::unvalid }; + header_t link; +}; + +struct confirmable +{ + static constexpr chase id{ chase::confirmable }; + header_t link; +}; + +struct unconfirmable +{ + static constexpr chase id{ chase::unconfirmable }; + header_t link; +}; + +struct block +{ + static constexpr chase id{ chase::block }; + header_t link; +}; + +struct stale +{ + static constexpr chase id{ chase::stale }; +}; + +struct organized +{ + static constexpr chase id{ chase::organized }; + header_t link; +}; + +struct reorganized +{ + static constexpr chase id{ chase::reorganized }; + header_t link; +}; + +struct transaction +{ + static constexpr chase id{ chase::transaction }; + transaction_t link; +}; + +struct template_ +{ + static constexpr chase id{ chase::template_ }; + height_t height; +}; + +struct stop +{ + static constexpr chase id{ chase::stop }; +}; + +} // namespace chases + +/// Alternative position is the chase value, so the event carries its own type. +using event_value = std::variant +< + chases::start, + chases::space, + chases::snap, + chases::bump, + chases::suspend, + chases::resume, + chases::unfull, + chases::starved, + chases::split, + chases::stall, + chases::purge, + chases::report, + chases::blocks, + chases::headers, + chases::download, + chases::regressed, + chases::disorganized, + chases::checked, + chases::unchecked, + chases::windowed, + chases::valid, + chases::unvalid, + chases::confirmable, + chases::unconfirmable, + chases::block, + chases::stale, + chases::organized, + chases::reorganized, + chases::transaction, + chases::template_, + chases::stop +>; + +template +constexpr bool is_chase_ordered(std::index_sequence) NOEXCEPT +{ + return ((std::variant_alternative_t::id == + static_cast(Index)) && ...); +} + +static_assert(is_chase_ordered(std::make_index_sequence< + std::variant_size_v>{})); + +/// The event's chase value. +constexpr chase to_chase(const event_value& value) NOEXCEPT +{ + return static_cast(value.index()); +} + +/// The event's payload, guarded by the alternative ordering above. +template +constexpr const auto& to_payload(const event_value& value) NOEXCEPT +{ + return std::get(value); +} + } // namespace node } // namespace libbitcoin diff --git a/include/bitcoin/node/chasers/chaser.hpp b/include/bitcoin/node/chasers/chaser.hpp index ce4252da..c2b6bc64 100644 --- a/include/bitcoin/node/chasers/chaser.hpp +++ b/include/bitcoin/node/chasers/chaser.hpp @@ -101,11 +101,10 @@ class BCN_API chaser virtual object_key subscribe_chase(event_notifier&& handler) NOEXCEPT; /// Set event (does not require node strand). - virtual void notify(const code& ec, chase event_, - event_value value) const NOEXCEPT; + virtual void notify(const code& ec, event_value value) const NOEXCEPT; /// Set event to one subscriber (does not require node strand). - virtual void notify_one(object_key key, const code& ec, chase event_, + virtual void notify_one(object_key key, const code& ec, event_value value) const NOEXCEPT; /// Strand. diff --git a/include/bitcoin/node/chasers/chaser_check.hpp b/include/bitcoin/node/chasers/chaser_check.hpp index 3bf5dbd8..3c6e3bdf 100644 --- a/include/bitcoin/node/chasers/chaser_check.hpp +++ b/include/bitcoin/node/chasers/chaser_check.hpp @@ -60,8 +60,7 @@ class BCN_API chaser_check protected: virtual void handle_purged(const code& ec) NOEXCEPT; - virtual bool handle_chase(const code& ec, chase event_, - event_value value) NOEXCEPT; + virtual bool handle_chase(const code& ec, event_value value) NOEXCEPT; /// block tracking virtual void do_bump(height_t height) NOEXCEPT; diff --git a/include/bitcoin/node/chasers/chaser_confirm.hpp b/include/bitcoin/node/chasers/chaser_confirm.hpp index 8aec0191..8c83396a 100644 --- a/include/bitcoin/node/chasers/chaser_confirm.hpp +++ b/include/bitcoin/node/chasers/chaser_confirm.hpp @@ -44,8 +44,7 @@ class BCN_API chaser_confirm using header_links = database::header_links; using header_states = database::header_states; - virtual bool handle_chase(const code& ec, chase event_, - event_value value) NOEXCEPT; + virtual bool handle_chase(const code& ec, event_value value) NOEXCEPT; virtual void do_regressed(height_t branch_point) NOEXCEPT; virtual void do_validated(height_t height) NOEXCEPT; diff --git a/include/bitcoin/node/chasers/chaser_estimate.hpp b/include/bitcoin/node/chasers/chaser_estimate.hpp index a4ed0b49..832d117a 100644 --- a/include/bitcoin/node/chasers/chaser_estimate.hpp +++ b/include/bitcoin/node/chasers/chaser_estimate.hpp @@ -52,8 +52,7 @@ class BCN_API chaser_estimate bool initialized() const NOEXCEPT; protected: - virtual bool handle_chase(const code& ec, chase event_, - event_value value) NOEXCEPT; + virtual bool handle_chase(const code& ec, event_value value) NOEXCEPT; virtual void do_initialize(header_t link) NOEXCEPT; virtual void do_organized(header_t link) NOEXCEPT; diff --git a/include/bitcoin/node/chasers/chaser_organize.hpp b/include/bitcoin/node/chasers/chaser_organize.hpp index 81d30ce5..869807a5 100644 --- a/include/bitcoin/node/chasers/chaser_organize.hpp +++ b/include/bitcoin/node/chasers/chaser_organize.hpp @@ -90,8 +90,7 @@ class chaser_organize /// ----------------------------------------------------------------------- /// Handle chaser events. - virtual bool handle_chase(const code&, chase event_, - event_value value) NOEXCEPT; + virtual bool handle_chase(const code&, event_value value) NOEXCEPT; /// Organize a discovered Block, prioritized accepts a tied branch. virtual void do_organize(typename Block::cptr block, bool prioritized, @@ -120,32 +119,40 @@ class chaser_organize // Template differentiators. // ------------------------------------------------------------------------ + static constexpr bool is_block_ + { + is_same_type + }; + + using chase_object = iif; + static constexpr bool is_block() NOEXCEPT { - return is_same_type; + return is_block_; } + static constexpr auto error_duplicate() NOEXCEPT { return is_block() ? error::duplicate_block : error::duplicate_header; } + static constexpr auto error_orphan() NOEXCEPT { return is_block() ? error::orphan_block : error::orphan_header; } - static constexpr auto chase_object() NOEXCEPT - { - return is_block() ? chase::blocks : chase::headers; - } + static constexpr auto events_object_archived() NOEXCEPT { return is_block() ? events::block_archived : events::header_archived; } + static constexpr auto events_object_organized() NOEXCEPT { // Using header because block organization is in confirmation chaser. ////return is_block() ? events::block_organized : events::header_organized; return events::header_organized; } + static constexpr auto events_object_reorganized() NOEXCEPT { // Using header because block reorganized is in confirmation chaser. diff --git a/include/bitcoin/node/chasers/chaser_snapshot.hpp b/include/bitcoin/node/chasers/chaser_snapshot.hpp index 61ba3ff7..0333e5c7 100644 --- a/include/bitcoin/node/chasers/chaser_snapshot.hpp +++ b/include/bitcoin/node/chasers/chaser_snapshot.hpp @@ -46,8 +46,7 @@ class BCN_API chaser_snapshot ////virtual void do_valid(height_t height) NOEXCEPT; ////virtual void do_confirm(height_t height) NOEXCEPT; - virtual bool handle_chase(const code& ec, chase event_, - event_value value) NOEXCEPT; + virtual bool handle_chase(const code& ec, event_value value) NOEXCEPT; private: void take_snapshot(height_t height) NOEXCEPT; diff --git a/include/bitcoin/node/chasers/chaser_storage.hpp b/include/bitcoin/node/chasers/chaser_storage.hpp index 61211a8d..5b3fd42e 100644 --- a/include/bitcoin/node/chasers/chaser_storage.hpp +++ b/include/bitcoin/node/chasers/chaser_storage.hpp @@ -43,8 +43,7 @@ class BCN_API chaser_storage protected: virtual void do_reload() NOEXCEPT; virtual void do_space(size_t space) NOEXCEPT; - virtual bool handle_chase(const code& ec, chase event_, - event_value value) NOEXCEPT; + virtual bool handle_chase(const code& ec, event_value value) NOEXCEPT; private: void do_stopping(const code& ec) NOEXCEPT; diff --git a/include/bitcoin/node/chasers/chaser_template.hpp b/include/bitcoin/node/chasers/chaser_template.hpp index 7f21874e..16529511 100644 --- a/include/bitcoin/node/chasers/chaser_template.hpp +++ b/include/bitcoin/node/chasers/chaser_template.hpp @@ -39,8 +39,7 @@ class BCN_API chaser_template code start() NOEXCEPT override; protected: - virtual bool handle_chase(const code& ec, chase event_, - event_value value) NOEXCEPT; + virtual bool handle_chase(const code& ec, event_value value) NOEXCEPT; virtual void do_transaction(transaction_t value) NOEXCEPT; }; diff --git a/include/bitcoin/node/chasers/chaser_transaction.hpp b/include/bitcoin/node/chasers/chaser_transaction.hpp index 4cc6fe34..d9d0ab80 100644 --- a/include/bitcoin/node/chasers/chaser_transaction.hpp +++ b/include/bitcoin/node/chasers/chaser_transaction.hpp @@ -44,8 +44,7 @@ class BCN_API chaser_transaction bool test, submit_handler&& handler) NOEXCEPT; protected: - virtual bool handle_chase(const code& ec, chase event_, - event_value value) NOEXCEPT; + virtual bool handle_chase(const code& ec, event_value value) NOEXCEPT; virtual void do_submit(const system::chain::transactions_cptr& txs, bool test, const submit_handler& handler) NOEXCEPT; diff --git a/include/bitcoin/node/chasers/chaser_validate.hpp b/include/bitcoin/node/chasers/chaser_validate.hpp index e2d76eb8..786f98f2 100644 --- a/include/bitcoin/node/chasers/chaser_validate.hpp +++ b/include/bitcoin/node/chasers/chaser_validate.hpp @@ -55,8 +55,7 @@ class BCN_API chaser_validate BIND_TO(method, args)); } - virtual bool handle_chase(const code& ec, chase event_, - event_value value) NOEXCEPT; + virtual bool handle_chase(const code& ec, event_value value) NOEXCEPT; virtual void do_regressed(height_t branch_point) NOEXCEPT; virtual void do_checked(height_t height) NOEXCEPT; diff --git a/include/bitcoin/node/define.hpp b/include/bitcoin/node/define.hpp index 91e4f3a5..cdd799db 100644 --- a/include/bitcoin/node/define.hpp +++ b/include/bitcoin/node/define.hpp @@ -40,7 +40,6 @@ #endif /// For common types below. -#include #include namespace libbitcoin { @@ -64,41 +63,14 @@ typedef std::shared_ptr map_ptr; typedef std::function map_handler; -/// Event desubscriber key type. -using object_key = uint64_t; - -/// Event value types. -using count_t = size_t; -using height_t = size_t; -using peer_t = uint64_t; -using object_t = object_key; -using header_t = database::header_link::integer; -using transaction_t = database::tx_link::integer; - -/// std::variant types must be distinct, and xcode size_t is neither uint32_t -/// nor uint64_t, so this ensures we have the distinct set of necessary types. -using event_value = - iif, - std::variant, - iif, - std::variant, - std::variant>>; - /// Event desubscriber. -typedef network::desubscriber event_subscriber; +typedef network::desubscriber event_subscriber; typedef event_subscriber::handler event_notifier; typedef event_subscriber::completer event_completer; // Inventory messages. using type_id = network::messages::peer::inventory_item::type_id; -// NDEBUG MSVC -////static_assert(sizeof(uint64_t) == 8u); -////static_assert(sizeof(block_t) == 16u); -////static_assert(sizeof(xblock_t) == 32u); -////static_assert(sizeof(event_value) == 24u); -////static_assert(sizeof(xevent_value) == 40u); - } // namespace node } // namespace libbitcoin diff --git a/include/bitcoin/node/full_node.hpp b/include/bitcoin/node/full_node.hpp index 9eba4fa9..7a01dada 100644 --- a/include/bitcoin/node/full_node.hpp +++ b/include/bitcoin/node/full_node.hpp @@ -87,11 +87,10 @@ class BCN_API full_node /// ----------------------------------------------------------------------- /// Set chaser event. - virtual void notify(const code& ec, chase event_, - event_value value) NOEXCEPT; + virtual void notify(const code& ec, event_value value) NOEXCEPT; /// Set chaser event for the given subscriber only. - virtual void notify_one(object_key key, const code& ec, chase event_, + virtual void notify_one(object_key key, const code& ec, event_value value) NOEXCEPT; /// Call from chaser start() methods (requires strand). @@ -186,8 +185,8 @@ class BCN_API full_node private: void do_subscribe_chase(const event_notifier& handler, const event_completer& complete) NOEXCEPT; - void do_notify(const code& ec, chase event_, event_value value) NOEXCEPT; - void do_notify_one(object_key key, const code& ec, chase event_, + void do_notify(const code& ec, event_value value) NOEXCEPT; + void do_notify_one(object_key key, const code& ec, event_value value) NOEXCEPT; // These are thread safe. diff --git a/include/bitcoin/node/impl/chasers/chaser_organize.ipp b/include/bitcoin/node/impl/chasers/chaser_organize.ipp index 31df6e5d..c45b9c6f 100644 --- a/include/bitcoin/node/impl/chasers/chaser_organize.ipp +++ b/include/bitcoin/node/impl/chasers/chaser_organize.ipp @@ -61,7 +61,7 @@ code CLASS::start() NOEXCEPT << state_->height() << "]."); update_checkpoint(top); - SUBSCRIBE_CHASE(handle_chase, _1, _2, _3); + SUBSCRIBE_CHASE(handle_chase, _1, _2); return error::success; } @@ -89,23 +89,40 @@ void CLASS::prioritize(const system::hash_digest& hash, // ---------------------------------------------------------------------------- TEMPLATE -bool CLASS::handle_chase(const code&, chase event_, event_value value) NOEXCEPT +bool CLASS::handle_chase(const code&, event_value value) NOEXCEPT { if (closed()) return false; - switch (event_) + switch (to_chase(value)) { + // Roll back the candidate chain to confirmed top (via fork point). case chase::unchecked: + { + if (database_settings().mark_unconfirmable) + { + POST(do_disorganize, to_payload(value).link); + } + + break; + } case chase::unvalid: + { + if (database_settings().mark_unconfirmable) + { + POST(do_disorganize, to_payload(value).link); + } + + break; + } case chase::unconfirmable: { - if (!database_settings().mark_unconfirmable) - break; + if (database_settings().mark_unconfirmable) + { + POST(do_disorganize, + to_payload(value).link); + } - // Roll back the candidate chain to confirmed top (via fork point). - BC_ASSERT(std::holds_alternative(value)); - POST(do_disorganize, std::get(value)); break; } case chase::stop: @@ -273,7 +290,7 @@ void CLASS::do_organize(typename Block::cptr block, bool prioritized, // Reset chasers to the branch point. if (regress) { - notify(error::success, chase::regressed, branch_point); + notify(error::success, chases::regressed{ branch_point }); } // Push stored strong headers to candidate chain. @@ -322,14 +339,14 @@ void CLASS::do_organize(typename Block::cptr block, bool prioritized, // be skipped, resulting in stall until restart at which time the start // event will advance through all downloaded candidates and progress on // arrivals. This bumps validation once for current strong headers. - notify(error::success, chase::bump, add1(branch_point)); + notify(error::success, chases::bump{ add1(branch_point) }); bumped_ = true; } // chase::headers | chase::blocks // This prevents download stall, the check chaser races ahead. // Start block downloads, which upon completion bumps validation. - notify(error::success, chase_object(), branch_point); + notify(error::success, chase_object{ branch_point }); } // Logs from candidate block parent to the candidate (forward sequential). @@ -486,10 +503,10 @@ void CLASS::do_disorganize(header_t link) NOEXCEPT state_ = state; // Candidate is same as confirmed, reset chasers to new top. - notify(error::success, chase::disorganized, fork_point); + notify(error::success, chases::disorganized{ fork_point }); // Reset all connections to ensure that new connections exist. - notify(error::success, chase::suspend, {}); + notify(error::success, chases::suspend{}); } // Private setters diff --git a/include/bitcoin/node/protocols/protocol_block_in_31800.hpp b/include/bitcoin/node/protocols/protocol_block_in_31800.hpp index 4f1358be..7dd789bd 100644 --- a/include/bitcoin/node/protocols/protocol_block_in_31800.hpp +++ b/include/bitcoin/node/protocols/protocol_block_in_31800.hpp @@ -61,8 +61,7 @@ class BCN_API protocol_block_in_31800 virtual void do_get_downloads(count_t count) NOEXCEPT; /// Handle chaser events. - virtual bool handle_chase(const code& ec, chase event_, - event_value value) NOEXCEPT; + virtual bool handle_chase(const code& ec, event_value value) NOEXCEPT; /// Manage work splitting. bool is_idle() const NOEXCEPT override; diff --git a/include/bitcoin/node/protocols/protocol_block_out_106.hpp b/include/bitcoin/node/protocols/protocol_block_out_106.hpp index d58e31ea..53424f5c 100644 --- a/include/bitcoin/node/protocols/protocol_block_out_106.hpp +++ b/include/bitcoin/node/protocols/protocol_block_out_106.hpp @@ -59,8 +59,7 @@ class BCN_API protocol_block_out_106 virtual bool superseded() const NOEXCEPT; /// Handle chaser events. - virtual bool handle_chase(const code& ec, chase event_, - event_value value) NOEXCEPT; + virtual bool handle_chase(const code& ec, event_value value) NOEXCEPT; /// Process block announcement. virtual bool do_announce(header_t link) NOEXCEPT; diff --git a/include/bitcoin/node/protocols/protocol_header_out_70012.hpp b/include/bitcoin/node/protocols/protocol_header_out_70012.hpp index b97e8268..52d29ab1 100644 --- a/include/bitcoin/node/protocols/protocol_header_out_70012.hpp +++ b/include/bitcoin/node/protocols/protocol_header_out_70012.hpp @@ -47,8 +47,7 @@ class BCN_API protocol_header_out_70012 protected: /// Handle chaser events. - virtual bool handle_chase(const code& ec, chase event_, - event_value value) NOEXCEPT; + virtual bool handle_chase(const code& ec, event_value value) NOEXCEPT; /// Process block announcement. virtual bool do_announce(header_t link) NOEXCEPT; diff --git a/include/bitcoin/node/protocols/protocol_observer.hpp b/include/bitcoin/node/protocols/protocol_observer.hpp index b7423596..0522cbdd 100644 --- a/include/bitcoin/node/protocols/protocol_observer.hpp +++ b/include/bitcoin/node/protocols/protocol_observer.hpp @@ -57,8 +57,7 @@ class BCN_API protocol_observer protected: /// Handle chaser events. - virtual bool handle_chase(const code& ec, chase event_, - event_value value) NOEXCEPT; + virtual bool handle_chase(const code& ec, event_value value) NOEXCEPT; /// Accept incoming inventory message. virtual bool handle_receive_inventory(const code& ec, diff --git a/include/bitcoin/node/protocols/protocol_peer.hpp b/include/bitcoin/node/protocols/protocol_peer.hpp index 1f3504fc..36a0404d 100644 --- a/include/bitcoin/node/protocols/protocol_peer.hpp +++ b/include/bitcoin/node/protocols/protocol_peer.hpp @@ -95,11 +95,10 @@ class BCN_API protocol_peer using network::protocol_peer::notify; /// Set a chaser event. - virtual void notify(const code& ec, chase event_, - event_value value) const NOEXCEPT; + virtual void notify(const code& ec, event_value value) const NOEXCEPT; /// Set a chaser event. - virtual void notify_one(object_key key, const code& ec, chase event_, + virtual void notify_one(object_key key, const code& ec, event_value value) const NOEXCEPT; private: diff --git a/include/bitcoin/node/protocols/protocol_transaction_out_106.hpp b/include/bitcoin/node/protocols/protocol_transaction_out_106.hpp index fe4a01ef..06528d00 100644 --- a/include/bitcoin/node/protocols/protocol_transaction_out_106.hpp +++ b/include/bitcoin/node/protocols/protocol_transaction_out_106.hpp @@ -50,8 +50,7 @@ class BCN_API protocol_transaction_out_106 using inventory_items = network::messages::peer::inventory_items; /// Handle chaser events. - virtual bool handle_chase(const code& ec, chase event_, - event_value value) NOEXCEPT; + virtual bool handle_chase(const code& ec, event_value value) NOEXCEPT; /// Process tx announcement. virtual bool do_announce(transaction_t link) NOEXCEPT; diff --git a/include/bitcoin/node/protocols/protocol_transaction_out_70013.hpp b/include/bitcoin/node/protocols/protocol_transaction_out_70013.hpp index 687ee6a3..289c012f 100644 --- a/include/bitcoin/node/protocols/protocol_transaction_out_70013.hpp +++ b/include/bitcoin/node/protocols/protocol_transaction_out_70013.hpp @@ -44,8 +44,7 @@ class BCN_API protocol_transaction_out_70013 protected: /// Handle chaser events. - bool handle_chase(const code& ec, chase event_, - event_value value) NOEXCEPT override; + bool handle_chase(const code& ec, event_value value) NOEXCEPT override; /// Capture the peer's minimum fee rate for announcements. virtual bool handle_receive_fee_filter(const code& ec, diff --git a/include/bitcoin/node/sessions/session.hpp b/include/bitcoin/node/sessions/session.hpp index ebc3d85b..0dff8aa2 100644 --- a/include/bitcoin/node/sessions/session.hpp +++ b/include/bitcoin/node/sessions/session.hpp @@ -65,11 +65,10 @@ class BCN_API session /// ----------------------------------------------------------------------- /// Set a chaser event. - virtual void notify(const code& ec, chase event_, - event_value value) const NOEXCEPT; + virtual void notify(const code& ec, event_value value) const NOEXCEPT; /// Set chaser event for the given subscriber only. - virtual void notify_one(object_key key, const code& ec, chase event_, + virtual void notify_one(object_key key, const code& ec, event_value value) const NOEXCEPT; /// Subscribe to chaser events (requires node strand). diff --git a/src/chasers/chaser.cpp b/src/chasers/chaser.cpp index 7d6f7c35..3e0ea12c 100644 --- a/src/chasers/chaser.cpp +++ b/src/chasers/chaser.cpp @@ -93,16 +93,15 @@ object_key chaser::subscribe_chase(event_notifier&& handler) NOEXCEPT return node_.subscribe_chase(std::move(handler)); } -void chaser::notify(const code& ec, chase event_, - event_value value) const NOEXCEPT +void chaser::notify(const code& ec, event_value value) const NOEXCEPT { - node_.notify(ec, event_, value); + node_.notify(ec, value); } -void chaser::notify_one(object_key key, const code& ec, chase event_, +void chaser::notify_one(object_key key, const code& ec, event_value value) const NOEXCEPT { - node_.notify_one(key, ec, event_, value); + node_.notify_one(key, ec, value); } // Strand. diff --git a/src/chasers/chaser_check.cpp b/src/chasers/chaser_check.cpp index bb27cc7c..5ca4c019 100644 --- a/src/chasers/chaser_check.cpp +++ b/src/chasers/chaser_check.cpp @@ -99,7 +99,7 @@ code chaser_check::start() NOEXCEPT const auto added = set_unassociated(); LOGN("Fork point (" << requested_ << ") unassociated (" << added << ")."); - SUBSCRIBE_CHASE(handle_chase, _1, _2, _3); + SUBSCRIBE_CHASE(handle_chase, _1, _2); return error::success; } @@ -110,21 +110,19 @@ void chaser_check::stopping(const code& ec) NOEXCEPT chaser::stopping(ec); } -bool chaser_check::handle_chase(const code&, chase event_, - event_value value) NOEXCEPT +bool chaser_check::handle_chase(const code&, event_value value) NOEXCEPT { if (closed()) return false; - switch (event_) + switch (to_chase(value)) { // Performance. // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ case chase::starved: { // When a channel becomes starved notify other(s) to split work. - BC_ASSERT(std::holds_alternative(value)); - POST(do_starved, std::get(value)); + POST(do_starved, to_payload(value).channel); break; } // Track downloaded. @@ -138,30 +136,32 @@ bool chaser_check::handle_chase(const code&, chase event_, } case chase::checked: { - BC_ASSERT(std::holds_alternative(value)); - POST(do_checked, std::get(value)); + POST(do_checked, to_payload(value).height); break; } case chase::regressed: + { + POST(do_regressed, + to_payload(value).branch_point); + break; + } case chase::disorganized: { - BC_ASSERT(std::holds_alternative(value)); - POST(do_regressed, std::get(value)); + POST(do_regressed, + to_payload(value).branch_point); break; } // Track chain. // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ case chase::headers: { - BC_ASSERT(std::holds_alternative(value)); - POST(do_headers, std::get(value)); + POST(do_headers, to_payload(value).branch_point); break; } case chase::valid: ////case chase::prevalid: { - BC_ASSERT(std::holds_alternative(value)); - POST(do_advanced, std::get(value)); + POST(do_advanced, to_payload(value).height); break; } case chase::stop: @@ -239,12 +239,12 @@ void chaser_check::do_starved(object_t self) NOEXCEPT speeds_.erase(slowest); // Notify slow channel to split itself (in favor of 'self' channel). - notify_one(slow, error::success, chase::split, self); + notify_one(slow, error::success, chases::split{ self }); return; } // With no speeds recorded there may still be channels with work. - notify(error::success, chase::stall, self); + notify(error::success, chases::stall{ self }); } // update @@ -352,7 +352,7 @@ void chaser_check::do_regressed(height_t branch_point) NOEXCEPT set_position(branch_point); stop_tracking(); maps_.clear(); - notify(error::success, chase::purge, branch_point); + notify(error::success, chases::purge{ branch_point }); } // track downloaded in order (to move download window) @@ -396,7 +396,7 @@ void chaser_check::do_bump(height_t) NOEXCEPT // Notify validator that no more blocks are coming. if (height == requested_) - notify(error::success, chase::windowed, height); + notify(error::success, chases::windowed{ height }); } do_headers({}); @@ -410,7 +410,7 @@ void chaser_check::do_headers(height_t) NOEXCEPT BC_ASSERT(stranded()); if (const auto added = set_unassociated(); is_nonzero(added)) - notify(error::success, chase::download, added); + notify(error::success, chases::download{ added }); } // get/put hashes @@ -456,7 +456,7 @@ void chaser_check::do_put_hashes(const map_ptr& map, return; if (set_map(map)) - notify(error::success, chase::download, map->size()); + notify(error::success, chases::download{ map->size() }); handler(error::success); } diff --git a/src/chasers/chaser_confirm.cpp b/src/chasers/chaser_confirm.cpp index 4fe53701..2a164d29 100644 --- a/src/chasers/chaser_confirm.cpp +++ b/src/chasers/chaser_confirm.cpp @@ -53,7 +53,7 @@ code chaser_confirm::start() NOEXCEPT // Construct is too early to create the unstarted timer. stale_timer_ = std::make_shared(log, strand()); - SUBSCRIBE_CHASE(handle_chase, _1, _2, _3); + SUBSCRIBE_CHASE(handle_chase, _1, _2); return error::success; } @@ -74,8 +74,7 @@ void chaser_confirm::do_stopping(const code&) NOEXCEPT } } -bool chaser_confirm::handle_chase(const code&, chase event_, - event_value value) NOEXCEPT +bool chaser_confirm::handle_chase(const code&, event_value value) NOEXCEPT { if (closed()) return false; @@ -85,7 +84,7 @@ bool chaser_confirm::handle_chase(const code&, chase event_, if (suspended()) return true; - switch (event_) + switch (to_chase(value)) { case chase::resume: case chase::start: @@ -96,17 +95,19 @@ bool chaser_confirm::handle_chase(const code&, chase event_, } case chase::valid: { - // value is validated block height. - BC_ASSERT(std::holds_alternative(value)); - POST(do_validated, std::get(value)); + POST(do_validated, to_payload(value).height); break; } case chase::regressed: + { + POST(do_regressed, + to_payload(value).branch_point); + break; + } case chase::disorganized: { - // value is regression branch_point. - BC_ASSERT(std::holds_alternative(value)); - POST(do_regressed, std::get(value)); + POST(do_regressed, + to_payload(value).branch_point); break; } case chase::stop: @@ -297,7 +298,7 @@ void chaser_confirm::organize(header_states& fork, const header_links& popped, // Prevent stall by posting internal event, avoiding external handlers. // Posts new work, preventing recursion and releasing reorganization lock. - handle_chase(error::success, chase::bump, height_t{}); + handle_chase(error::success, chases::bump{}); } bool chaser_confirm::confirm_block(const header_link& link, size_t height, @@ -362,14 +363,14 @@ bool chaser_confirm::notify_block(const code& ec, size_t height, if (ec) { // UNCONFIRMABLE BLOCK (not a fault but discontinue) - notify(ec, chase::unconfirmable, link); + notify(ec, chases::unconfirmable{ link }); fire(events::block_unconfirmable, height); LOGR("Unconfirmable block [" << height << "] " << ec.message()); return false; } // CONFIRMABLE BLOCK - notify(error::success, chase::confirmable, link); + notify(error::success, chases::confirmable{ link }); fire(events::block_confirmed, height); LOGV("Block confirmed: " << height << (bypass ? " (bypass)" : "")); return true; @@ -387,7 +388,7 @@ bool chaser_confirm::set_reorganized(const header_link& link, if (!archive().pop_confirmed()) return false; - notify(error::success, chase::reorganized, link); + notify(error::success, chases::reorganized{ link }); fire(events::block_reorganized, confirmed_height); LOGV("Block reorganized: " << confirmed_height); return true; @@ -420,7 +421,7 @@ bool chaser_confirm::set_organized(const header_link& link, if (!query.push_confirmed(link, !is_under_checkpoint(confirmed_height))) return false; - notify(error::success, chase::organized, link); + notify(error::success, chases::organized{ link }); fire(events::block_organized, confirmed_height); LOGV("Block organized: " << confirmed_height); @@ -452,7 +453,7 @@ void chaser_confirm::announce(const header_link& link, height_t) NOEXCEPT // Announce newly-organized blocks when confirmed chain is current. if (is_current_chain(true)) { - notify(error::success, chase::block, link); + notify(error::success, chases::block{ link }); start_stale_timer(); } } @@ -489,7 +490,7 @@ void chaser_confirm::handle_stale_timer(const code& ec) NOEXCEPT return; } - notify(error::success, chase::stale, {}); + notify(error::success, chases::stale{}); } BC_POP_WARNING() diff --git a/src/chasers/chaser_estimate.cpp b/src/chasers/chaser_estimate.cpp index 254bdda2..46da0282 100644 --- a/src/chasers/chaser_estimate.cpp +++ b/src/chasers/chaser_estimate.cpp @@ -46,7 +46,7 @@ code chaser_estimate::start() NOEXCEPT { if (node_settings().fee_estimate_enabled()) { - SUBSCRIBE_CHASE(handle_chase, _1, _2, _3); + SUBSCRIBE_CHASE(handle_chase, _1, _2); } return error::success; @@ -108,8 +108,7 @@ bool chaser_estimate::initialized() const NOEXCEPT // ---------------------------------------------------------------------------- // protected -bool chaser_estimate::handle_chase(const code&, chase event_, - event_value value) NOEXCEPT +bool chaser_estimate::handle_chase(const code&, event_value value) NOEXCEPT { if (closed()) return false; @@ -118,7 +117,7 @@ bool chaser_estimate::handle_chase(const code&, chase event_, ////if (suspended()) //// return true; - switch (event_) + switch (to_chase(value)) { // chase::block is only sent when current. This is captured as a cheap // way to test currency for initialization. Once initialized it is not @@ -128,8 +127,7 @@ bool chaser_estimate::handle_chase(const code&, chase event_, { if (!initialized()) { - BC_ASSERT(std::holds_alternative(value)); - POST(do_initialize, std::get(value)); + POST(do_initialize, to_payload(value).link); } break; @@ -138,8 +136,8 @@ bool chaser_estimate::handle_chase(const code&, chase event_, { if (initialized()) { - BC_ASSERT(std::holds_alternative(value)); - POST(do_organized, std::get(value)); + POST(do_organized, + to_payload(value).link); } break; @@ -148,8 +146,8 @@ bool chaser_estimate::handle_chase(const code&, chase event_, { if (initialized()) { - BC_ASSERT(std::holds_alternative(value)); - POST(do_reorganized, std::get(value)); + POST(do_reorganized, + to_payload(value).link); } break; diff --git a/src/chasers/chaser_snapshot.cpp b/src/chasers/chaser_snapshot.cpp index 59c766a9..04df0e3f 100644 --- a/src/chasers/chaser_snapshot.cpp +++ b/src/chasers/chaser_snapshot.cpp @@ -65,15 +65,14 @@ code chaser_snapshot::start() NOEXCEPT ////if (enabled_confirm_) //// confirm_ = std::max(archive().get_top_confirmed(), checkpoint()); - SUBSCRIBE_CHASE(handle_chase, _1, _2, _3); + SUBSCRIBE_CHASE(handle_chase, _1, _2); return error::success; } // event handlers // ---------------------------------------------------------------------------- -bool chaser_snapshot::handle_chase(const code&, chase event_, - event_value value) NOEXCEPT +bool chaser_snapshot::handle_chase(const code&, event_value value) NOEXCEPT { if (closed()) return false; @@ -83,7 +82,7 @@ bool chaser_snapshot::handle_chase(const code&, chase event_, if (suspended()) return true; - switch (event_) + switch (to_chase(value)) { // blocks first and headers first (checked) messages ////case chase::blocks: @@ -119,14 +118,12 @@ bool chaser_snapshot::handle_chase(const code&, chase event_, if (pruned_.load(std::memory_order_relaxed)) break; - BC_ASSERT(std::holds_alternative(value)); - POST(do_prune, std::get(value)); + POST(do_prune, to_payload(value).link); break; } case chase::snap: { - BC_ASSERT(std::holds_alternative(value)); - POST(do_snap, std::get(value)); + POST(do_snap, to_payload(value).height); break; } default: diff --git a/src/chasers/chaser_storage.cpp b/src/chasers/chaser_storage.cpp index 5ab3a18e..40e2e9d9 100644 --- a/src/chasers/chaser_storage.cpp +++ b/src/chasers/chaser_storage.cpp @@ -50,7 +50,7 @@ code chaser_storage::start() NOEXCEPT // Construct is too early to create the unstarted timer. disk_timer_ = std::make_shared(log, strand(), seconds{1}); - SUBSCRIBE_CHASE(handle_chase, _1, _2, _3); + SUBSCRIBE_CHASE(handle_chase, _1, _2); return error::success; } @@ -73,13 +73,12 @@ void chaser_storage::do_stopping(const code&) NOEXCEPT // event handlers // ---------------------------------------------------------------------------- -bool chaser_storage::handle_chase(const code&, chase event_, - event_value) NOEXCEPT +bool chaser_storage::handle_chase(const code&, event_value value) NOEXCEPT { if (closed()) return false; - switch (event_) + switch (to_chase(value)) { case chase::space: { @@ -166,7 +165,7 @@ void chaser_storage::do_reload() NOEXCEPT else { // Recovery from disk full message sent in addition to chase::resume. - notify(error::success, chase::unfull, {}); + notify(error::success, chases::unfull{}); resume(); const auto span = duration_cast(logger::now() - start); diff --git a/src/chasers/chaser_template.cpp b/src/chasers/chaser_template.cpp index 3582a915..b235165b 100644 --- a/src/chasers/chaser_template.cpp +++ b/src/chasers/chaser_template.cpp @@ -43,15 +43,14 @@ chaser_template::chaser_template(full_node& node) NOEXCEPT // TODO: initialize template state. code chaser_template::start() NOEXCEPT { - SUBSCRIBE_CHASE(handle_chase, _1, _2, _3); + SUBSCRIBE_CHASE(handle_chase, _1, _2); return error::success; } // event handlers // ---------------------------------------------------------------------------- -bool chaser_template::handle_chase(const code&, chase event_, - event_value value) NOEXCEPT +bool chaser_template::handle_chase(const code&, event_value value) NOEXCEPT { if (closed()) return false; @@ -62,12 +61,11 @@ bool chaser_template::handle_chase(const code&, chase event_, return true; // TODO: also handle confirmed/unconfirmed. - switch (event_) + switch (to_chase(value)) { case chase::transaction: { - BC_ASSERT(std::holds_alternative(value)); - POST(do_transaction, std::get(value)); + POST(do_transaction, to_payload(value).link); break; } case chase::stop: diff --git a/src/chasers/chaser_transaction.cpp b/src/chasers/chaser_transaction.cpp index 9dcc6789..eb08c329 100644 --- a/src/chasers/chaser_transaction.cpp +++ b/src/chasers/chaser_transaction.cpp @@ -46,7 +46,7 @@ chaser_transaction::chaser_transaction(full_node& node) NOEXCEPT code chaser_transaction::start() NOEXCEPT { - SUBSCRIBE_CHASE(handle_chase, _1, _2, _3); + SUBSCRIBE_CHASE(handle_chase, _1, _2); POST(do_bump); return error::success; } @@ -54,13 +54,12 @@ code chaser_transaction::start() NOEXCEPT // event handlers // ---------------------------------------------------------------------------- -bool chaser_transaction::handle_chase(const code&, chase event_, - event_value) NOEXCEPT +bool chaser_transaction::handle_chase(const code&, event_value value) NOEXCEPT { if (closed()) return false; - switch (event_) + switch (to_chase(value)) { case chase::organized: case chase::reorganized: @@ -167,7 +166,7 @@ void chaser_transaction::do_submit(const transactions_cptr& txs, bool test, } fire(events::tx_archived, to_rate(tx)); - notify(error::success, chase::transaction, transaction_t{ link }); + notify(error::success, chases::transaction{ link }); } handler(error::success, {}); diff --git a/src/chasers/chaser_validate.cpp b/src/chasers/chaser_validate.cpp index 03101aed..06786311 100644 --- a/src/chasers/chaser_validate.cpp +++ b/src/chasers/chaser_validate.cpp @@ -66,16 +66,17 @@ code chaser_validate::start() NOEXCEPT if (const auto ec = start_batch()) return fault(ec); - SUBSCRIBE_CHASE(handle_chase, _1, _2, _3); + SUBSCRIBE_CHASE(handle_chase, _1, _2); return error::success; } -bool chaser_validate::handle_chase(const code&, chase event_, - event_value value) NOEXCEPT +bool chaser_validate::handle_chase(const code&, event_value value) NOEXCEPT { if (closed()) return false; + const auto event_ = to_chase(value); + // Latch recovering from disk full, before suspension is lifted. // Because in-flight blocks are lost, reset position when backlog clears. if (event_ == chase::unfull) @@ -113,27 +114,28 @@ bool chaser_validate::handle_chase(const code&, chase event_, } case chase::checked: { - // value is checked block height. - BC_ASSERT(std::holds_alternative(value)); - POST(do_checked, std::get(value)); + POST(do_checked, to_payload(value).height); break; } case chase::windowed: { - // value is last height in window. Called directly (not posted): - // the drain must never depend on scheduling, which saturated - // validations can exhaust for the entire window (strand for - // admission-side control, never for release-side). - BC_ASSERT(std::holds_alternative(value)); + // Called directly (not posted): the drain must never depend on + // scheduling, which saturated validations can exhaust for the + // entire window (strand for admission-side control, never for + // release-side). process_batch(is_residual()); break; } case chase::regressed: + { + POST(do_regressed, + to_payload(value).branch_point); + break; + } case chase::disorganized: { - // value is regression branch_point. - BC_ASSERT(std::holds_alternative(value)); - POST(do_regressed, std::get(value)); + POST(do_regressed, + to_payload(value).branch_point); break; } case chase::stop: @@ -267,7 +269,7 @@ void chaser_validate::complete_block(const code& ec, const header_link& link, if (is_zero(validate_backlog_.load()) && !stranded()) { // Prevent stall by posting internal event, avoiding external handlers. - handle_chase({}, chase::bump, height_t{}); + handle_chase({}, chases::bump{}); } // Node errors are fatal (or disk full recoverable). @@ -327,14 +329,15 @@ void chaser_validate::notify_block(const code& ec, size_t height, if (ec) { // INVALID BLOCK (not a fault but discontinue) - if (!startup) notify(ec, chase::unvalid, link); + if (!startup) notify(ec, chases::unvalid{ link }); fire(events::block_unconfirmable, height); LOGR("Invalid block [" << height << "] " << ec.message()); return; } // VALID BLOCK - if (!startup) notify(ec, chase::valid, possible_wide_cast(height)); + if (!startup) + notify(ec, chases::valid{ possible_wide_cast(height) }); fire(events::block_validated, height); LOGV("Block validated: " << height << (bypass ? " (bypass)" : "")); } diff --git a/src/full_node.cpp b/src/full_node.cpp index a538b38a..e031e657 100644 --- a/src/full_node.cpp +++ b/src/full_node.cpp @@ -115,7 +115,7 @@ void full_node::do_run(const result_handler& handler) NOEXCEPT // Bump sequential chasers to their starting heights. // This will kick off lagging validations even if not current. - do_notify(error::success, chase::start, height_t{}); + do_notify(error::success, chases::start{}); // Start services after network is running. net::do_run(handler); @@ -156,7 +156,7 @@ void full_node::do_close() NOEXCEPT chaser_snapshot_.stopping(network::error::service_stopped); chaser_storage_.stopping(network::error::service_stopped); - event_subscriber_.stop(network::error::service_stopped, chase::stop, {}); + event_subscriber_.stop(network::error::service_stopped, chases::stop{}); net::do_close(); } @@ -204,36 +204,34 @@ void full_node::put_hashes(const map_ptr& map, // Events. // ---------------------------------------------------------------------------- -void full_node::notify(const code& ec, chase event_, - event_value value) NOEXCEPT +void full_node::notify(const code& ec, event_value value) NOEXCEPT { boost::asio::post(strand(), std::bind(&full_node::do_notify, - this, ec, event_, value)); + this, ec, value)); } // private -void full_node::do_notify(const code& ec, chase event_, - event_value value) NOEXCEPT +void full_node::do_notify(const code& ec, event_value value) NOEXCEPT { BC_ASSERT(stranded()); - event_subscriber_.notify(ec, event_, value); + event_subscriber_.notify(ec, value); } -void full_node::notify_one(object_key key, const code& ec, chase event_, +void full_node::notify_one(object_key key, const code& ec, event_value value) NOEXCEPT { boost::asio::post(strand(), std::bind(&full_node::do_notify_one, - this, key, ec, event_, value)); + this, key, ec, value)); } // private -void full_node::do_notify_one(object_key key, const code& ec, chase event_, +void full_node::do_notify_one(object_key key, const code& ec, event_value value) NOEXCEPT { BC_ASSERT(stranded()); - event_subscriber_.notify_one(key, ec, event_, value); + event_subscriber_.notify_one(key, ec, value); } object_key full_node::subscribe_chase(event_notifier&& handler) NOEXCEPT @@ -263,7 +261,7 @@ void full_node::do_subscribe_chase(const event_notifier& handler, void full_node::unsubscribe_chase(object_key key) NOEXCEPT { - notify_one(key, network::error::service_stopped, chase::stop, {}); + notify_one(key, network::error::service_stopped, chases::stop{}); } // Suspensions. @@ -284,7 +282,7 @@ bool full_node::resume() NOEXCEPT } LOGS("Resuming network."); - notify(error::success, chase::resume, {}); + notify(error::success, chases::resume{}); return net::resume(); } @@ -293,7 +291,7 @@ void full_node::suspend(const code& ec) NOEXCEPT { LOGS("Suspending network, " << ec.message()); net::suspend(ec); - notify(error::suspended_channel, chase::suspend, {}); + notify(error::suspended_channel, chases::suspend{}); } void full_node::fault(const code& ec) NOEXCEPT @@ -302,7 +300,7 @@ void full_node::fault(const code& ec) NOEXCEPT { LOGF("Disk full [" << query_.get_space() << "] bytes required following, " << ec.message()); - notify(ec, chase::space, {}); + notify(ec, chases::space{}); } else if (query_.is_fault()) { diff --git a/src/protocols/protocol_block_in_31800.cpp b/src/protocols/protocol_block_in_31800.cpp index 803efa29..27994db6 100644 --- a/src/protocols/protocol_block_in_31800.cpp +++ b/src/protocols/protocol_block_in_31800.cpp @@ -49,7 +49,7 @@ void protocol_block_in_31800::start() NOEXCEPT return; // Events subscription is asynchronous, events may be missed. - subscribe_chase(BIND(handle_chase, _1, _2, _3)); + subscribe_chase(BIND(handle_chase, _1, _2)); SUBSCRIBE_CHANNEL(block, handle_receive_block, _1, _2); protocol_performer::start(); } @@ -95,14 +95,14 @@ bool protocol_block_in_31800::is_idle() const NOEXCEPT return map_->empty(); } -bool protocol_block_in_31800::handle_chase(const code&, chase event_, +bool protocol_block_in_31800::handle_chase(const code&, event_value value) NOEXCEPT { // Do not pass ec to stopped as it is not a call status. if (stopped()) return false; - switch (event_) + switch (to_chase(value)) { case chase::split: { @@ -131,14 +131,12 @@ bool protocol_block_in_31800::handle_chase(const code&, chase event_, // There are count blocks to download at/above given header. // chase::headers is only sent for current candidate chain, and this // chase::download is only sent as a consequence of chase::headers. - BC_ASSERT(std::holds_alternative(value)); - POST(do_get_downloads, std::get(value)); + POST(do_get_downloads, to_payload(value).count); break; } case chase::report: { - BC_ASSERT(std::holds_alternative(value)); - POST(do_report, std::get(value)); + POST(do_report, to_payload(value).sequence); break; } case chase::stop: @@ -324,7 +322,7 @@ bool protocol_block_in_31800::handle_receive_block(const code& ec, LOGR("Block failed check [" << encode_hash(hash) << ":" << height << "] from [" << opposite() << "] " << code.message()); - notify(error::success, chase::unchecked, link); + notify(error::success, chases::unchecked{ link }); fire(events::block_unconfirmable, height); stop(code); return false; @@ -351,7 +349,7 @@ bool protocol_block_in_31800::handle_receive_block(const code& ec, LOGP("Downloaded block [" << encode_hash(hash) << ":" << height << "] from [" << opposite() << "]."); - notify(ec, chase::checked, height); + notify(ec, chases::checked{ height }); fire(events::block_archived, height); count(block.serialized_size(true)); @@ -425,7 +423,7 @@ void protocol_block_in_31800::handle_get_hashes(const code& ec, if (map->empty()) { - notify(error::success, chase::starved, events_key()); + notify(error::success, chases::starved{ events_key() }); return; } diff --git a/src/protocols/protocol_block_out_106.cpp b/src/protocols/protocol_block_out_106.cpp index ed7699da..18b8d023 100644 --- a/src/protocols/protocol_block_out_106.cpp +++ b/src/protocols/protocol_block_out_106.cpp @@ -51,7 +51,7 @@ void protocol_block_out_106::start() NOEXCEPT return; // Events subscription is asynchronous, events may be missed. - subscribe_chase(BIND(handle_chase, _1, _2, _3)); + subscribe_chase(BIND(handle_chase, _1, _2)); SUBSCRIBE_CHANNEL(get_data, handle_receive_get_data, _1, _2); SUBSCRIBE_CHANNEL(get_blocks, handle_receive_get_blocks, _1, _2); protocol_peer::start(); @@ -68,20 +68,18 @@ void protocol_block_out_106::stopping(const code& ec) NOEXCEPT // handle events (block) // ---------------------------------------------------------------------------- -bool protocol_block_out_106::handle_chase(const code&, chase event_, +bool protocol_block_out_106::handle_chase(const code&, event_value value) NOEXCEPT { // Do not pass ec to stopped as it is not a call status. if (stopped() || superseded()) return false; - switch (event_) + switch (to_chase(value)) { case chase::block: { - // value is organized block pk. - BC_ASSERT(std::holds_alternative(value)); - POST(do_announce, std::get(value)); + POST(do_announce, to_payload(value).link); break; } default: diff --git a/src/protocols/protocol_header_out_70012.cpp b/src/protocols/protocol_header_out_70012.cpp index 49e23952..2e8f49a4 100644 --- a/src/protocols/protocol_header_out_70012.cpp +++ b/src/protocols/protocol_header_out_70012.cpp @@ -59,20 +59,18 @@ void protocol_header_out_70012::stopping(const code& ec) NOEXCEPT // handle events (block) // ---------------------------------------------------------------------------- -bool protocol_header_out_70012::handle_chase(const code&, chase event_, +bool protocol_header_out_70012::handle_chase(const code&, event_value value) NOEXCEPT { // Do not pass ec to stopped as it is not a call status. if (stopped()) return false; - switch (event_) + switch (to_chase(value)) { case chase::block: { - // value is organized block pk. - BC_ASSERT(std::holds_alternative(value)); - POST(do_announce, std::get(value)); + POST(do_announce, to_payload(value).link); break; } default: @@ -130,7 +128,7 @@ bool protocol_header_out_70012::handle_receive_send_headers(const code& ec, return false; // Events subscription is asynchronous, events may be missed. - subscribe_chase(BIND(handle_chase, _1, _2, _3)); + subscribe_chase(BIND(handle_chase, _1, _2)); return false; } diff --git a/src/protocols/protocol_observer.cpp b/src/protocols/protocol_observer.cpp index faf45e36..586047f0 100644 --- a/src/protocols/protocol_observer.cpp +++ b/src/protocols/protocol_observer.cpp @@ -43,7 +43,7 @@ void protocol_observer::start() NOEXCEPT return; // Events subscription is asynchronous, events may be missed. - subscribe_chase(BIND(handle_chase, _1, _2, _3)); + subscribe_chase(BIND(handle_chase, _1, _2)); if (relay_disallowed_) { @@ -66,14 +66,13 @@ void protocol_observer::stopping(const code& ec) NOEXCEPT // handle events (suspend) // ---------------------------------------------------------------------------- -bool protocol_observer::handle_chase(const code& ec, chase event_, - event_value) NOEXCEPT +bool protocol_observer::handle_chase(const code& ec, event_value value) NOEXCEPT { // Do not pass ec to stopped as it is not a call status. if (stopped()) return false; - switch (event_) + switch (to_chase(value)) { case chase::suspend: { diff --git a/src/protocols/protocol_peer.cpp b/src/protocols/protocol_peer.cpp index e67b88f4..259ac16c 100644 --- a/src/protocols/protocol_peer.cpp +++ b/src/protocols/protocol_peer.cpp @@ -81,16 +81,15 @@ bool protocol_peer::was_announced(const system::hash_digest& hash) const NOEXCEP // Events notification. // ---------------------------------------------------------------------------- -void protocol_peer::notify(const code& ec, chase event_, - event_value value) const NOEXCEPT +void protocol_peer::notify(const code& ec, event_value value) const NOEXCEPT { - session_->notify(ec, event_, value); + session_->notify(ec, value); } -void protocol_peer::notify_one(object_key key, const code& ec, chase event_, +void protocol_peer::notify_one(object_key key, const code& ec, event_value value) const NOEXCEPT { - session_->notify_one(key, ec, event_, value); + session_->notify_one(key, ec, value); } } // namespace node diff --git a/src/protocols/protocol_transaction_out_106.cpp b/src/protocols/protocol_transaction_out_106.cpp index a4a66553..370df746 100644 --- a/src/protocols/protocol_transaction_out_106.cpp +++ b/src/protocols/protocol_transaction_out_106.cpp @@ -44,7 +44,7 @@ void protocol_transaction_out_106::start() NOEXCEPT return; // Events subscription is asynchronous, events may be missed. - subscribe_chase(BIND(handle_chase, _1, _2, _3)); + subscribe_chase(BIND(handle_chase, _1, _2)); SUBSCRIBE_CHANNEL(get_data, handle_receive_get_data, _1, _2); protocol_peer::start(); @@ -62,20 +62,18 @@ void protocol_transaction_out_106::stopping(const code& ec) NOEXCEPT // handle events (transaction) // ---------------------------------------------------------------------------- -bool protocol_transaction_out_106::handle_chase(const code&, chase event_, +bool protocol_transaction_out_106::handle_chase(const code&, event_value value) NOEXCEPT { // Do not pass ec to stopped as it is not a call status. if (stopped()) return false; - switch (event_) + switch (to_chase(value)) { case chase::transaction: { - // value is organized tx pk. - BC_ASSERT(std::holds_alternative(value)); - POST(do_announce, std::get(value)); + POST(do_announce, to_payload(value).link); break; } default: diff --git a/src/protocols/protocol_transaction_out_70013.cpp b/src/protocols/protocol_transaction_out_70013.cpp index 4c45f0a9..859ea349 100644 --- a/src/protocols/protocol_transaction_out_70013.cpp +++ b/src/protocols/protocol_transaction_out_70013.cpp @@ -69,13 +69,13 @@ void protocol_transaction_out_70013::do_send_fee_filter() NOEXCEPT } bool protocol_transaction_out_70013::handle_chase(const code& ec, - chase event_, event_value value) NOEXCEPT + event_value value) NOEXCEPT { // Do not pass ec to stopped as it is not a call status. if (stopped()) return false; - switch (event_) + switch (to_chase(value)) { case chase::block: case chase::stale: @@ -89,7 +89,7 @@ bool protocol_transaction_out_70013::handle_chase(const code& ec, } } - return protocol_transaction_out_70001::handle_chase(ec, event_, value); + return protocol_transaction_out_70001::handle_chase(ec, value); } // Inbound (feefilter). diff --git a/src/sessions/session.cpp b/src/sessions/session.cpp index 9088b546..74c92c3f 100644 --- a/src/sessions/session.cpp +++ b/src/sessions/session.cpp @@ -77,16 +77,15 @@ void session::put_hashes(const map_ptr& map, // Events. // ---------------------------------------------------------------------------- -void session::notify(const code& ec, chase event_, - event_value value) const NOEXCEPT +void session::notify(const code& ec, event_value value) const NOEXCEPT { - node_.notify(ec, event_, value); + node_.notify(ec, value); } -void session::notify_one(object_key key, const code& ec, chase event_, +void session::notify_one(object_key key, const code& ec, event_value value) const NOEXCEPT { - node_.notify_one(key, ec, event_, value); + node_.notify_one(key, ec, value); } object_key session::subscribe_chase(event_notifier&& handler) NOEXCEPT From de756d8bd967477568763ec093fc7399257ab626 Mon Sep 17 00:00:00 2001 From: Eric Chennells Date: Sun, 20 Sep 2026 01:44:06 +0000 Subject: [PATCH 5/5] Rewind the download window and bump validation on regression. After a reorganization the node can stop making progress while headers continue to arrive; a restart clears it. Two chasers are left waiting on events that never come. `chaser_check::do_regressed` rewinds the position and purges outstanding work, but leaves `requested_` and `advanced_` where they were, and ignores a regression that falls above the position but inside the requested window. `set_unassociated` then defers new requests while `position() < requested_`, waiting on work that was just purged, so the heights between the position and the old request are never requested. The guard now considers the window as well as the position, and both are rewound with it, as at start. `chaser_validate` advances on the checked event for the block above its position, or on a bump. When a reorganization returns to a branch whose next block is already downloaded, neither arrives. The organizer now bumps validation on regression as it does once at start. Reproduced on regtest against bitcoind and on replays of recorded testnet3 reorganizations, and observed handled on live testnet3. A regression organized while not current does not stall, so the bump stays within the currency gate. --- include/bitcoin/node/impl/chasers/chaser_organize.ipp | 6 ++++-- src/chasers/chaser_check.cpp | 11 +++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/include/bitcoin/node/impl/chasers/chaser_organize.ipp b/include/bitcoin/node/impl/chasers/chaser_organize.ipp index c45b9c6f..86593a1b 100644 --- a/include/bitcoin/node/impl/chasers/chaser_organize.ipp +++ b/include/bitcoin/node/impl/chasers/chaser_organize.ipp @@ -332,13 +332,15 @@ void CLASS::do_organize(typename Block::cptr block, bool prioritized, // Checking currency before notify also avoids excessive work backlog. if (is_block() || current) { - if (!bumped_) + if (!bumped_ || regress) { // If at start the fork point is top of both chains, and next candidate // is already downloaded, then new header will arrive and download will // be skipped, resulting in stall until restart at which time the start // event will advance through all downloaded candidates and progress on - // arrivals. This bumps validation once for current strong headers. + // arrivals. This bumps validation once for current strong headers, and + // again on regression, as the candidate above the branch point may + // already be downloaded when reorganizing back to a stored branch. notify(error::success, chases::bump{ add1(branch_point) }); bumped_ = true; } diff --git a/src/chasers/chaser_check.cpp b/src/chasers/chaser_check.cpp index 5ca4c019..dc93be89 100644 --- a/src/chasers/chaser_check.cpp +++ b/src/chasers/chaser_check.cpp @@ -344,12 +344,15 @@ void chaser_check::do_regressed(height_t branch_point) NOEXCEPT { BC_ASSERT(stranded()); - // Inconsequential regression, work isn't there yet. - if (branch_point >= position()) + // Inconsequential regression, neither position nor window is there yet. + if (branch_point >= std::max(position(), requested_)) return; - // Update position, purge outstanding work, and wait on track completion. - set_position(branch_point); + // Update position and window, purge outstanding work, and wait on track + // completion. The window must follow the position or the gap it awaits + // is never requested. + set_position(std::min(branch_point, position())); + requested_ = advanced_ = position(); stop_tracking(); maps_.clear(); notify(error::success, chases::purge{ branch_point });