net: Store recipient node address in private broadcast
What changed, and why it matters
This commit adds the recipient's network address to the bookkeeping records used by Bitcoin Core's private transaction broadcast feature. It does not change how transactions are sent or validated; it only stores extra metadata for internal tracking. There is no direct security vulnerability visible in the change, and the commit message does not describe it as a security fix.
No immediate action required. Review as part of normal code maintenance. If this commit is part of a larger private-broadcast improvement, monitor related commits for actual security or privacy behavior changes.
Security signals we found
No security-relevant keywords in commit title or message
No validation, authorization, or cryptographic logic modified
Change is additive metadata storage only
No memory safety issues evident (CService is a standard copyable type)
No CVE, advisory, or vendor security disclosure referenced
Evidence from the diff
The change extends PrivateBroadcast::SendStatus to include a CService address field and updates PickTxForSend to accept and store the peer’s address alongside the NodeId and timestamp. The address is taken from node.addr in net_processing.cpp and passed into the private broadcast module. This is a data-model/observability change with no logic alteration to transaction selection, broadcast rules, or network behavior. Tests are updated to pass a dummy CService.
Changed components
src/net_processing.cppsrc/private_broadcast.cppsrc/private_broadcast.hsrc/test/private_broadcast_tests.cppInspect captured patch +14 / −9
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index e5b4bc77..1aefbf77 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -3531,7 +3531,7 @@ void PeerManagerImpl::PushPrivateBroadcastTx(CNode& node)
{
Assume(node.IsPrivateBroadcastConn());
- const auto opt_tx{m_tx_for_private_broadcast.PickTxForSend(node.GetId())};
+ const auto opt_tx{m_tx_for_private_broadcast.PickTxForSend(node.GetId(), CService{node.addr})};
if (!opt_tx) {
LogDebug(BCLog::PRIVBROADCAST, "Disconnecting: no more transactions for private broadcast (connected in vain), peer=%d%s", node.GetId(), node.LogIP(fLogIPs));
node.fDisconnect = true;
diff --git a/src/private_broadcast.cpp b/src/private_broadcast.cpp
index c7c311c0..fd36a940 100644
--- a/src/private_broadcast.cpp
+++ b/src/private_broadcast.cpp
@@ -31,7 +31,7 @@ std::optional<size_t> PrivateBroadcast::Remove(const CTransactionRef& tx)
return std::nullopt;
}
-std::optional<CTransactionRef> PrivateBroadcast::PickTxForSend(const NodeId& will_send_to_nodeid)
+std::optional<CTransactionRef> PrivateBroadcast::PickTxForSend(const NodeId& will_send_to_nodeid, const CService& will_send_to_address)
EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
{
LOCK(m_mutex);
@@ -43,7 +43,7 @@ std::optional<CTransactionRef> PrivateBroadcast::PickTxForSend(const NodeId& wil
if (it != m_transactions.end()) {
auto& [tx, sent_to]{*it};
- sent_to.emplace_back(will_send_to_nodeid, NodeClock::now());
+ sent_to.emplace_back(will_send_to_nodeid, will_send_to_address, NodeClock::now());
return tx;
}
diff --git a/src/private_broadcast.h b/src/private_broadcast.h
index e88db6bb..3beed5ee 100644
--- a/src/private_broadcast.h
+++ b/src/private_broadcast.h
@@ -54,9 +54,11 @@ public:
* and oldest send/confirm times.
* @param[in] will_send_to_nodeid Will remember that the returned transaction
* was picked for sending to this node.
+ * @param[in] will_send_to_address Address of the peer to which this transaction
+ * will be sent.
* @return Most urgent transaction or nullopt if there are no transactions.
*/
- std::optional<CTransactionRef> PickTxForSend(const NodeId& will_send_to_nodeid)
+ std::optional<CTransactionRef> PickTxForSend(const NodeId& will_send_to_nodeid, const CService& will_send_to_address)
EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
/**
@@ -99,10 +101,11 @@ private:
/// Status of a transaction sent to a given node.
struct SendStatus {
const NodeId nodeid; /// Node to which the transaction will be sent (or was sent).
+ const CService address; /// Address of the node.
const NodeClock::time_point picked; ///< When was the transaction picked for sending to the node.
std::optional<NodeClock::time_point> confirmed; ///< When was the transaction reception confirmed by the node (by PONG).
- SendStatus(const NodeId& nodeid, const NodeClock::time_point& picked) : nodeid{nodeid}, picked{picked} {}
+ SendStatus(const NodeId& nodeid, const CService& address, const NodeClock::time_point& picked) : nodeid{nodeid}, address{address}, picked{picked} {}
};
/// Cumulative stats from all the send attempts for a transaction. Used to prioritize transactions.
diff --git a/src/test/private_broadcast_tests.cpp b/src/test/private_broadcast_tests.cpp
index 6c5ef36f..e0ab64cd 100644
--- a/src/test/private_broadcast_tests.cpp
+++ b/src/test/private_broadcast_tests.cpp
@@ -29,9 +29,10 @@ BOOST_AUTO_TEST_CASE(basic)
PrivateBroadcast pb;
const NodeId recipient1{1};
+ const CService addr1{};
// No transactions initially.
- BOOST_CHECK(!pb.PickTxForSend(/*will_send_to_nodeid=*/recipient1).has_value());
+ BOOST_CHECK(!pb.PickTxForSend(/*will_send_to_nodeid=*/recipient1, /*will_send_to_address=*/addr1).has_value());
BOOST_CHECK_EQUAL(pb.GetStale().size(), 0);
BOOST_CHECK(!pb.HavePendingTransactions());
@@ -48,12 +49,13 @@ BOOST_AUTO_TEST_CASE(basic)
BOOST_CHECK(pb.Add(tx2));
- const auto tx_for_recipient1{pb.PickTxForSend(/*will_send_to_nodeid=*/recipient1).value()};
+ const auto tx_for_recipient1{pb.PickTxForSend(/*will_send_to_nodeid=*/recipient1, /*will_send_to_address=*/addr1).value()};
BOOST_CHECK(tx_for_recipient1 == tx1 || tx_for_recipient1 == tx2);
// A second pick must return the other transaction.
const NodeId recipient2{2};
- const auto tx_for_recipient2{pb.PickTxForSend(/*will_send_to_nodeid=*/recipient2).value()};
+ const CService addr2{};
+ const auto tx_for_recipient2{pb.PickTxForSend(/*will_send_to_nodeid=*/recipient2, /*will_send_to_address=*/addr2).value()};
BOOST_CHECK(tx_for_recipient2 == tx1 || tx_for_recipient2 == tx2);
BOOST_CHECK_NE(tx_for_recipient1, tx_for_recipient2);
@@ -90,7 +92,7 @@ BOOST_AUTO_TEST_CASE(basic)
BOOST_CHECK_EQUAL(pb.Remove(tx_for_recipient2).value(), 0);
BOOST_CHECK(!pb.Remove(tx_for_recipient2).has_value());
- BOOST_CHECK(!pb.PickTxForSend(/*will_send_to_nodeid=*/nonexistent_recipient).has_value());
+ BOOST_CHECK(!pb.PickTxForSend(/*will_send_to_nodeid=*/nonexistent_recipient, /*will_send_to_address=*/CService{}).has_value());
}
BOOST_AUTO_TEST_SUITE_END()
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.