From a6fe3596f1f1876bccd387b34085aaf1e50232d4 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Fri, 11 Sep 2026 16:56:54 -0400 Subject: [PATCH] Cache notified txs in tx_out protocol for relay. --- .../protocol_transaction_out_106.hpp | 20 +++++++ .../protocol_transaction_out_106.cpp | 55 ++++++++++++++++++- 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/include/bitcoin/node/protocols/protocol_transaction_out_106.hpp b/include/bitcoin/node/protocols/protocol_transaction_out_106.hpp index 05dcf659..192ae5f0 100644 --- a/include/bitcoin/node/protocols/protocol_transaction_out_106.hpp +++ b/include/bitcoin/node/protocols/protocol_transaction_out_106.hpp @@ -36,6 +36,7 @@ class BCN_API protocol_transaction_out_106 const network::channel::ptr& channel) NOEXCEPT : node::protocol_peer(session, channel), node_witness_(session->node_settings().provide_witness), + broadcast_(maximum_retained), network::tracker(session->log) { } @@ -70,9 +71,28 @@ class BCN_API protocol_transaction_out_106 virtual bool announce(const system::hash_digest& hash) NOEXCEPT; + /// Retain a broadcast tx, pending request by the peer. + virtual void retain(const system::hash_digest& hash, + const system::chain::transaction::cptr& tx) NOEXCEPT; + + /// Obtain and drop a retained tx, or nullptr. + virtual system::chain::transaction::cptr release( + const system::hash_digest& hash) NOEXCEPT; + private: + using retained_t = std::pair; + using retained_txs = boost::circular_buffer; + + static constexpr size_t maximum_retained = 42; + + retained_txs::iterator find(const system::hash_digest& hash) NOEXCEPT; + // These are thread safe. const bool node_witness_; + + // This is protected by strand. + retained_txs broadcast_; }; } // namespace node diff --git a/src/protocols/protocol_transaction_out_106.cpp b/src/protocols/protocol_transaction_out_106.cpp index 7d0d8e57..ca0faabb 100644 --- a/src/protocols/protocol_transaction_out_106.cpp +++ b/src/protocols/protocol_transaction_out_106.cpp @@ -18,6 +18,7 @@ */ #include +#include #include namespace libbitcoin { @@ -116,7 +117,13 @@ bool protocol_transaction_out_106::handle_broadcast_transaction(const code& ec, if (sender == identifier()) return true; - return announce(message->transaction_ptr->hash(false)); + const auto& tx = message->transaction_ptr; + const auto hash = tx->hash(false); + if (was_announced(hash)) + return true; + + retain(hash, tx); + return announce(hash); } bool protocol_transaction_out_106::announce(const hash_digest& hash) NOEXCEPT @@ -139,6 +146,45 @@ bool protocol_transaction_out_106::announce(const hash_digest& hash) NOEXCEPT return true; } +// Retention. +// ---------------------------------------------------------------------------- + +void protocol_transaction_out_106::retain(const hash_digest& hash, + const chain::transaction::cptr& tx) NOEXCEPT +{ + BC_ASSERT(stranded()); + + if (find(hash) != broadcast_.end()) + return; + + broadcast_.push_back({ hash, tx }); +} + +chain::transaction::cptr protocol_transaction_out_106::release( + const hash_digest& hash) NOEXCEPT +{ + BC_ASSERT(stranded()); + + const auto it = find(hash); + if (it == broadcast_.end()) + return {}; + + const auto tx = it->second; + broadcast_.erase(it); + return tx; +} + +protocol_transaction_out_106::retained_txs::iterator +protocol_transaction_out_106::find(const hash_digest& hash) NOEXCEPT +{ + const auto match = [&hash](const auto& entry) NOEXCEPT + { + return entry.first == hash; + }; + + return std::find_if(broadcast_.begin(), broadcast_.end(), match); +} + // Inbound (get_data). // ---------------------------------------------------------------------------- @@ -192,6 +238,13 @@ void protocol_transaction_out_106::send_transaction(const code& ec, return; } + // A broadcast tx is not archived, so is served from retention, once. + if (const auto retained = release(item.hash)) + { + SEND(transaction{ retained }, send_transaction, _1, add1(index), message); + return; + } + // Tx could be always queried with witness and therefore safely cached. // If can then be serialized according to channel configuration, however // that is currently fixed to witness as available in the object.