From 557260ca14ac5fb4732f4ce0692a2bf364bb5238 Mon Sep 17 00:00:00 2001 From: Andrew Toth Date: Sat, 17 Jan 2026 18:33:28 -0500 Subject: [PATCH] rpc: Add abortprivatebroadcast Co-authored-by: l0rinc --- src/net_processing.cpp | 21 ++++++++++++++++ src/net_processing.h | 13 ++++++++++ src/rpc/mempool.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++ src/test/fuzz/rpc.cpp | 1 + 4 files changed, 91 insertions(+) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 7ab05cd0789..80622d06efb 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -543,6 +543,7 @@ public: std::vector GetOrphanTransactions() override EXCLUSIVE_LOCKS_REQUIRED(!m_tx_download_mutex); PeerManagerInfo GetInfo() const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); std::vector GetPrivateBroadcastInfo() const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); + std::vector AbortPrivateBroadcast(const uint256& id) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); void SendPings() override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); void InitiateTxBroadcastToAll(const Txid& txid, const Wtxid& wtxid) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); void InitiateTxBroadcastPrivate(const CTransactionRef& tx) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); @@ -1861,6 +1862,26 @@ std::vector PeerManagerImpl::GetPrivateBroadc return m_tx_for_private_broadcast.GetBroadcastInfo(); } +std::vector PeerManagerImpl::AbortPrivateBroadcast(const uint256& id) +{ + const auto snapshot{m_tx_for_private_broadcast.GetBroadcastInfo()}; + std::vector removed_txs; + + size_t connections_cancelled{0}; + for (const auto& [tx, _] : snapshot) { + if (tx->GetHash().ToUint256() != id && tx->GetWitnessHash().ToUint256() != id) continue; + if (const auto peer_acks{m_tx_for_private_broadcast.Remove(tx)}) { + removed_txs.push_back(tx); + if (NUM_PRIVATE_BROADCAST_PER_TX > *peer_acks) { + connections_cancelled += (NUM_PRIVATE_BROADCAST_PER_TX - *peer_acks); + } + } + } + m_connman.m_private_broadcast.NumToOpenSub(connections_cancelled); + + return removed_txs; +} + void PeerManagerImpl::AddToCompactExtraTransactions(const CTransactionRef& tx) { if (m_opts.max_extra_txs <= 0) diff --git a/src/net_processing.h b/src/net_processing.h index 4aac8daa41e..36ae021f679 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -122,6 +123,18 @@ public: /** Get info about transactions currently being privately broadcast. */ virtual std::vector GetPrivateBroadcastInfo() const = 0; + /** + * Abort private broadcast attempts for transactions currently being privately broadcast. + * + * @param[in] id A transaction identifier. It will be matched against both txid and wtxid for + * all transactions in the private broadcast queue. + * + * @return Transactions removed from the private broadcast queue. If the provided id matches a + * txid that corresponds to multiple transactions with different wtxids, multiple + * transactions may be returned. + */ + virtual std::vector AbortPrivateBroadcast(const uint256& id) = 0; + /** * Initiate a transaction broadcast to eligible peers. * Queue the witness transaction id to `Peer::TxRelay::m_tx_inventory_to_send` diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp index 35870368705..12eef3ce755 100644 --- a/src/rpc/mempool.cpp +++ b/src/rpc/mempool.cpp @@ -202,6 +202,61 @@ static RPCHelpMan getprivatebroadcastinfo() }; } +static RPCHelpMan abortprivatebroadcast() +{ + return RPCHelpMan{ + "abortprivatebroadcast", + "Abort private broadcast attempts for a transaction currently being privately broadcast.\n" + "The transaction will be removed from the private broadcast queue.\n", + { + {"id", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A transaction identifier to abort. It will be matched against both txid and wtxid for all transactions in the private broadcast queue.\n" + "If the provided id matches a txid that corresponds to multiple transactions with different wtxids, multiple transactions will be removed and returned."}, + }, + RPCResult{ + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::ARR, "removed_transactions", "Transactions removed from the private broadcast queue", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"}, + {RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"}, + {RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded transaction data"}, + }}, + }}, + } + }, + RPCExamples{ + HelpExampleCli("abortprivatebroadcast", "\"id\"") + + HelpExampleRpc("abortprivatebroadcast", "\"id\"") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue + { + const uint256 id{ParseHashV(self.Arg("id"), "id")}; + + const NodeContext& node{EnsureAnyNodeContext(request.context)}; + PeerManager& peerman{EnsurePeerman(node)}; + + const auto removed_txs{peerman.AbortPrivateBroadcast(id)}; + if (removed_txs.empty()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in private broadcast queue. Check getprivatebroadcastinfo."); + } + + UniValue removed_transactions(UniValue::VARR); + for (const auto& tx : removed_txs) { + UniValue o(UniValue::VOBJ); + o.pushKV("txid", tx->GetHash().ToString()); + o.pushKV("wtxid", tx->GetWitnessHash().ToString()); + o.pushKV("hex", EncodeHexTx(*tx)); + removed_transactions.push_back(std::move(o)); + } + UniValue ret(UniValue::VOBJ); + ret.pushKV("removed_transactions", std::move(removed_transactions)); + return ret; + }, + }; +} + static RPCHelpMan testmempoolaccept() { return RPCHelpMan{ @@ -1395,6 +1450,7 @@ void RegisterMempoolRPCCommands(CRPCTable& t) static const CRPCCommand commands[]{ {"rawtransactions", &sendrawtransaction}, {"rawtransactions", &getprivatebroadcastinfo}, + {"rawtransactions", &abortprivatebroadcast}, {"rawtransactions", &testmempoolaccept}, {"blockchain", &getmempoolancestors}, {"blockchain", &getmempooldescendants}, diff --git a/src/test/fuzz/rpc.cpp b/src/test/fuzz/rpc.cpp index cfb36afde7c..e8daf9d390a 100644 --- a/src/test/fuzz/rpc.cpp +++ b/src/test/fuzz/rpc.cpp @@ -90,6 +90,7 @@ const std::vector RPC_COMMANDS_NOT_SAFE_FOR_FUZZING{ // RPC commands which are safe for fuzzing. const std::vector RPC_COMMANDS_SAFE_FOR_FUZZING{ + "abortprivatebroadcast", "analyzepsbt", "clearbanned", "combinepsbt",