In the commit message of 232f1c1970219b1f0580deb82384f1d78125f7fd net: deduplicate private broadcast state and snapshot:
As a narrow tradeoff, the m_transactions value now stores
TxBroadcastInfo, which includes tx, so CTransactionRef is present in
both the map key and value.
This is not good and can be avoided. Here is a change on top of this PR that further simplifies the things (and is net -10 lines):
- Remove
tx from TxBroadcastInfo
- Rename
TxBroadcastInfo to Status
- Return
m_transactions from GetBroadcastInfo()
- Required for the above: define the type of
m_transactions in the public: section
- Nice side effect:
find_tx_info() is not needed anymore, so drop it
<details>
<summary>[patch] drop CTransactionRef from TxBroadcastInfo and rename it</summary>
diff --git i/src/net_processing.cpp w/src/net_processing.cpp
index 4e102730fb..d91e7e6d46 100644
--- i/src/net_processing.cpp
+++ w/src/net_processing.cpp
@@ -535,13 +535,13 @@ public:
void CheckForStaleTipAndEvictPeers() override;
util::Expected<void, std::string> FetchBlock(NodeId peer_id, const CBlockIndex& block_index) override
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
std::vector<node::TxOrphanage::OrphanInfo> GetOrphanTransactions() override EXCLUSIVE_LOCKS_REQUIRED(!m_tx_download_mutex);
PeerManagerInfo GetInfo() const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
- std::vector<PrivateBroadcast::TxBroadcastInfo> GetPrivateBroadcastInfo() const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
+ PrivateBroadcast::Transactions GetPrivateBroadcastInfo() const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
std::vector<CTransactionRef> 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);
void SetBestBlock(int height, std::chrono::seconds time) override
{
@@ -1852,25 +1852,24 @@ PeerManagerInfo PeerManagerImpl::GetInfo() const
return PeerManagerInfo{
.median_outbound_time_offset = m_outbound_time_offsets.Median(),
.ignores_incoming_txs = m_opts.ignore_incoming_txs,
};
}
-std::vector<PrivateBroadcast::TxBroadcastInfo> PeerManagerImpl::GetPrivateBroadcastInfo() const
+PrivateBroadcast::Transactions PeerManagerImpl::GetPrivateBroadcastInfo() const
{
return m_tx_for_private_broadcast.GetBroadcastInfo();
}
std::vector<CTransactionRef> PeerManagerImpl::AbortPrivateBroadcast(const uint256& id)
{
const auto snapshot{m_tx_for_private_broadcast.GetBroadcastInfo()};
std::vector<CTransactionRef> removed_txs;
size_t connections_cancelled{0};
- for (const auto& tx_info : snapshot) {
- const CTransactionRef& tx{tx_info.tx};
+ 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);
}
diff --git i/src/net_processing.h w/src/net_processing.h
index d2050d8f3d..60a6ce6d00 100644
--- i/src/net_processing.h
+++ w/src/net_processing.h
@@ -116,13 +116,13 @@ public:
virtual std::vector<node::TxOrphanage::OrphanInfo> GetOrphanTransactions() = 0;
/** Get peer manager info. */
virtual PeerManagerInfo GetInfo() const = 0;
/** Get info about transactions currently being privately broadcast. */
- virtual std::vector<PrivateBroadcast::TxBroadcastInfo> GetPrivateBroadcastInfo() const = 0;
+ virtual PrivateBroadcast::Transactions GetPrivateBroadcastInfo() const = 0;
/**
* Abort private broadcast attempts for transactions currently being privately broadcast.
*
* [@param](/bitcoin-bitcoin/contributor/param/)[in] id A transaction identifier. It will be matched against both txid and wtxid for
* all transactions in the private broadcast queue.
diff --git i/src/private_broadcast.cpp w/src/private_broadcast.cpp
index 930da217c8..9763dfcf9c 100644
--- i/src/private_broadcast.cpp
+++ w/src/private_broadcast.cpp
@@ -9,13 +9,13 @@
bool PrivateBroadcast::Add(const CTransactionRef& tx)
EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
{
LOCK(m_mutex);
- const bool inserted{m_transactions.try_emplace(tx, TxBroadcastInfo{.tx = tx}).second};
+ const bool inserted{m_transactions.try_emplace(tx).second};
return inserted;
}
std::optional<size_t> PrivateBroadcast::Remove(const CTransactionRef& tx)
EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
{
@@ -100,24 +100,17 @@ std::vector<CTransactionRef> PrivateBroadcast::GetStale() const
if (p.last_confirmed < now - STALE_DURATION) stale.push_back(tx);
}
}
return stale;
}
-std::vector<PrivateBroadcast::TxBroadcastInfo> PrivateBroadcast::GetBroadcastInfo() const
+PrivateBroadcast::Transactions PrivateBroadcast::GetBroadcastInfo() const
EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
{
LOCK(m_mutex);
- std::vector<TxBroadcastInfo> entries;
- entries.reserve(m_transactions.size());
-
- for (const auto& entry : m_transactions) {
- entries.emplace_back(entry.second);
- }
-
- return entries;
+ return m_transactions;
}
PrivateBroadcast::Priority PrivateBroadcast::DerivePriority(const std::vector<SendStatus>& sent_to)
{
Priority p;
p.num_picked = sent_to.size();
diff --git i/src/private_broadcast.h w/src/private_broadcast.h
index 33878e0723..893c3b26f9 100644
--- i/src/private_broadcast.h
+++ w/src/private_broadcast.h
@@ -49,18 +49,38 @@ public:
/// When was the transaction reception confirmed by the node (by PONG).
std::optional<NodeClock::time_point> confirmed;
SendStatus(const NodeId& nodeid, const CService& address, const NodeClock::time_point& picked) : nodeid{nodeid}, address{address}, picked{picked} {}
};
- struct TxBroadcastInfo {
- CTransactionRef tx;
+ /// Complete status of a transaction.
+ struct Status {
+ /// When was the transaction added to the queue.
const NodeClock::time_point time_added{NodeClock::now()};
+ /// All send attempts.
std::vector<SendStatus> send_statuses{};
};
+ // No need for salted hasher because we are going to store just a bunch of locally originating transactions.
+
+ struct CTransactionRefHash {
+ size_t operator()(const CTransactionRef& tx) const
+ {
+ return static_cast<size_t>(tx->GetWitnessHash().ToUint256().GetUint64(0));
+ }
+ };
+
+ struct CTransactionRefComp {
+ bool operator()(const CTransactionRef& a, const CTransactionRef& b) const
+ {
+ return a->GetWitnessHash() == b->GetWitnessHash(); // If wtxid equals, then txid also equals.
+ }
+ };
+
+ using Transactions = std::unordered_map<CTransactionRef, Status, CTransactionRefHash, CTransactionRefComp>;
+
/**
* Add a transaction to the storage.
* [@param](/bitcoin-bitcoin/contributor/param/)[in] tx The transaction to add.
* [@retval](/bitcoin-bitcoin/contributor/retval/) true The transaction was added.
* [@retval](/bitcoin-bitcoin/contributor/retval/) false The transaction was already present.
*/
@@ -125,13 +145,13 @@ public:
std::vector<CTransactionRef> GetStale() const
EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
/**
* Get stats about all transactions currently being privately broadcast.
*/
- std::vector<TxBroadcastInfo> GetBroadcastInfo() const
+ Transactions GetBroadcastInfo() const
EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
private:
/// Cumulative stats from all the send attempts for a transaction. Used to prioritize transactions.
struct Priority {
size_t num_picked{0}; ///< Number of times the transaction was picked for sending.
@@ -152,28 +172,12 @@ private:
/// A pair of a transaction and a sent status for a given node. Convenience return type of GetSendStatusByNode().
struct TxAndSendStatusForNode {
const CTransactionRef& tx;
SendStatus& send_status;
};
- // No need for salted hasher because we are going to store just a bunch of locally originating transactions.
-
- struct CTransactionRefHash {
- size_t operator()(const CTransactionRef& tx) const
- {
- return static_cast<size_t>(tx->GetWitnessHash().ToUint256().GetUint64(0));
- }
- };
-
- struct CTransactionRefComp {
- bool operator()(const CTransactionRef& a, const CTransactionRef& b) const
- {
- return a->GetWitnessHash() == b->GetWitnessHash(); // If wtxid equals, then txid also equals.
- }
- };
-
/**
* Derive the sending priority of a transaction.
* [@param](/bitcoin-bitcoin/contributor/param/)[in] sent_to List of nodes that the transaction has been sent to.
*/
static Priority DerivePriority(const std::vector<SendStatus>& sent_to);
@@ -183,11 +187,10 @@ private:
* send any transaction to the given node.
*/
std::optional<TxAndSendStatusForNode> GetSendStatusByNode(const NodeId& nodeid)
EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
mutable Mutex m_mutex;
- std::unordered_map<CTransactionRef, TxBroadcastInfo, CTransactionRefHash, CTransactionRefComp>
- m_transactions GUARDED_BY(m_mutex);
+ Transactions m_transactions GUARDED_BY(m_mutex);
};
#endif // BITCOIN_PRIVATE_BROADCAST_H
diff --git i/src/rpc/mempool.cpp w/src/rpc/mempool.cpp
index 3cfe40aafb..c1036b05b9 100644
--- i/src/rpc/mempool.cpp
+++ w/src/rpc/mempool.cpp
@@ -173,23 +173,23 @@ static RPCMethod getprivatebroadcastinfo()
+ HelpExampleRpc("getprivatebroadcastinfo", "")
},
[](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
{
const NodeContext& node{EnsureAnyNodeContext(request.context)};
const PeerManager& peerman{EnsurePeerman(node)};
- const auto txs{peerman.GetPrivateBroadcastInfo()};
+ const auto pbinfo{peerman.GetPrivateBroadcastInfo()};
UniValue transactions(UniValue::VARR);
- for (const auto& tx_info : txs) {
+ for (const auto& [tx, status] : pbinfo) {
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));
- o.pushKV("time_added", TicksSinceEpoch<std::chrono::seconds>(tx_info.time_added));
+ o.pushKV("txid", tx->GetHash().ToString());
+ o.pushKV("wtxid", tx->GetWitnessHash().ToString());
+ o.pushKV("hex", EncodeHexTx(*tx));
+ o.pushKV("time_added", TicksSinceEpoch<std::chrono::seconds>(status.time_added));
UniValue peers(UniValue::VARR);
- for (const auto& ss : tx_info.send_statuses) {
+ for (const auto& ss : status.send_statuses) {
UniValue p(UniValue::VOBJ);
p.pushKV("address", ss.address.ToStringAddrPort());
p.pushKV("sent", TicksSinceEpoch<std::chrono::seconds>(ss.picked));
if (ss.confirmed.has_value()) {
p.pushKV("received", TicksSinceEpoch<std::chrono::seconds>(*ss.confirmed));
}
diff --git i/src/test/private_broadcast_tests.cpp w/src/test/private_broadcast_tests.cpp
index f9fd4167db..b59eb65bdb 100644
--- i/src/test/private_broadcast_tests.cpp
+++ w/src/test/private_broadcast_tests.cpp
@@ -51,22 +51,17 @@ BOOST_AUTO_TEST_CASE(basic)
const auto tx2{MakeDummyTx(/*id=*/1, /*num_witness=*/1)};
BOOST_REQUIRE(tx1->GetHash() == tx2->GetHash());
BOOST_REQUIRE(tx1->GetWitnessHash() != tx2->GetWitnessHash());
BOOST_CHECK(pb.Add(tx2));
const auto time_added{NodeClock::now()};
- const auto find_tx_info{[](const auto& infos, const CTransactionRef& tx) -> const PrivateBroadcast::TxBroadcastInfo& {
- const auto it{std::ranges::find(infos, tx->GetWitnessHash(), [](const auto& info) { return info.tx->GetWitnessHash(); })};
- BOOST_REQUIRE(it != infos.end());
- return *it;
- }};
const auto check_peer_counts{[&](size_t tx1_peer_count, size_t tx2_peer_count) {
const auto infos{pb.GetBroadcastInfo()};
BOOST_CHECK_EQUAL(infos.size(), 2);
- const auto& tx1_info{find_tx_info(infos, tx1)};
- const auto& tx2_info{find_tx_info(infos, tx2)};
+ const auto& tx1_info{infos.at(tx1)};
+ const auto& tx2_info{infos.at(tx2)};
BOOST_CHECK(tx1_info.time_added == time_added);
BOOST_CHECK(tx2_info.time_added == time_added);
BOOST_CHECK_EQUAL(tx1_info.send_statuses.size(), tx1_peer_count);
BOOST_CHECK_EQUAL(tx2_info.send_statuses.size(), tx2_peer_count);
}};
@@ -112,19 +107,19 @@ BOOST_AUTO_TEST_CASE(basic)
BOOST_CHECK(pb.DidNodeConfirmReception(recipient1));
BOOST_CHECK(!pb.DidNodeConfirmReception(recipient2));
const auto infos{pb.GetBroadcastInfo()};
BOOST_CHECK_EQUAL(infos.size(), 2);
{
- const auto& send_statuses{find_tx_info(infos, tx_for_recipient1).send_statuses};
+ const auto& send_statuses{infos.at(tx_for_recipient1).send_statuses};
BOOST_CHECK_EQUAL(send_statuses.size(), 1);
BOOST_CHECK_EQUAL(send_statuses[0].address.ToStringAddrPort(), addr1.ToStringAddrPort());
BOOST_CHECK(send_statuses[0].confirmed.has_value());
}
{
- const auto& send_statuses{find_tx_info(infos, tx_for_recipient2).send_statuses};
+ const auto& send_statuses{infos.at(tx_for_recipient2).send_statuses};
BOOST_CHECK_EQUAL(send_statuses.size(), 1);
BOOST_CHECK_EQUAL(send_statuses[0].address.ToStringAddrPort(), addr2.ToStringAddrPort());
BOOST_CHECK(!send_statuses[0].confirmed.has_value());
}
const auto stale_state{pb.GetStale()};
</details>