From 996f20c18af02281034c51af4b2766d8f4d37a2c Mon Sep 17 00:00:00 2001 From: Andrew Toth Date: Sat, 17 Jan 2026 18:32:36 -0500 Subject: [PATCH] rpc: Add getprivatebroadcastinfo Co-authored-by: Vasil Dimov --- src/net_processing.cpp | 6 ++++ src/net_processing.h | 4 +++ src/rpc/mempool.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++ src/test/fuzz/rpc.cpp | 1 + 4 files changed, 77 insertions(+) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 1aefbf77cb3..7ab05cd0789 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -542,6 +542,7 @@ public: bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); 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); 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); @@ -1855,6 +1856,11 @@ PeerManagerInfo PeerManagerImpl::GetInfo() const }; } +std::vector PeerManagerImpl::GetPrivateBroadcastInfo() const +{ + return m_tx_for_private_broadcast.GetBroadcastInfo(); +} + 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 504e708d702..4aac8daa41e 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -118,6 +119,9 @@ public: /** Get peer manager info. */ virtual PeerManagerInfo GetInfo() const = 0; + /** Get info about transactions currently being privately broadcast. */ + virtual std::vector GetPrivateBroadcastInfo() const = 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 66ce1c61582..35870368705 100644 --- a/src/rpc/mempool.cpp +++ b/src/rpc/mempool.cpp @@ -137,6 +137,71 @@ static RPCHelpMan sendrawtransaction() }; } +static RPCHelpMan getprivatebroadcastinfo() +{ + return RPCHelpMan{ + "getprivatebroadcastinfo", + "Returns information about transactions that are currently being privately broadcast.\n", + {}, + RPCResult{ + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::ARR, "transactions", "", + { + {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"}, + {RPCResult::Type::ARR, "peers", "Per-peer send and acknowledgment information for this transaction", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "address", "The address of the peer to which the transaction was sent"}, + {RPCResult::Type::NUM_TIME, "sent", "The time this transaction was picked for sending to this peer via private broadcast (seconds since epoch)"}, + {RPCResult::Type::NUM_TIME, "received", /*optional=*/true, "The time this peer acknowledged reception of the transaction (seconds since epoch)"}, + }}, + }}, + }}, + }}, + }}, + RPCExamples{ + HelpExampleCli("getprivatebroadcastinfo", "") + + HelpExampleRpc("getprivatebroadcastinfo", "") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue + { + const NodeContext& node{EnsureAnyNodeContext(request.context)}; + const PeerManager& peerman{EnsurePeerman(node)}; + const auto txs{peerman.GetPrivateBroadcastInfo()}; + + UniValue transactions(UniValue::VARR); + for (const auto& tx_info : txs) { + UniValue o(UniValue::VOBJ); + o.pushKV("txid", tx_info.tx->GetHash().ToString()); + o.pushKV("wtxid", tx_info.tx->GetWitnessHash().ToString()); + o.pushKV("hex", EncodeHexTx(*tx_info.tx)); + UniValue peers(UniValue::VARR); + for (const auto& peer : tx_info.peers) { + UniValue p(UniValue::VOBJ); + p.pushKV("address", peer.address.ToStringAddrPort()); + p.pushKV("sent", TicksSinceEpoch(peer.sent)); + if (peer.received.has_value()) { + p.pushKV("received", TicksSinceEpoch(*peer.received)); + } + peers.push_back(std::move(p)); + } + o.pushKV("peers", std::move(peers)); + transactions.push_back(std::move(o)); + } + + UniValue ret(UniValue::VOBJ); + ret.pushKV("transactions", std::move(transactions)); + return ret; + }, + }; +} + static RPCHelpMan testmempoolaccept() { return RPCHelpMan{ @@ -1329,6 +1394,7 @@ void RegisterMempoolRPCCommands(CRPCTable& t) { static const CRPCCommand commands[]{ {"rawtransactions", &sendrawtransaction}, + {"rawtransactions", &getprivatebroadcastinfo}, {"rawtransactions", &testmempoolaccept}, {"blockchain", &getmempoolancestors}, {"blockchain", &getmempooldescendants}, diff --git a/src/test/fuzz/rpc.cpp b/src/test/fuzz/rpc.cpp index ba052d3c738..cfb36afde7c 100644 --- a/src/test/fuzz/rpc.cpp +++ b/src/test/fuzz/rpc.cpp @@ -147,6 +147,7 @@ const std::vector RPC_COMMANDS_SAFE_FOR_FUZZING{ "getorphantxs", "getpeerinfo", "getprioritisedtransactions", + "getprivatebroadcastinfo", "getrawaddrman", "getrawmempool", "getrawtransaction",