net: introduce a new connection type for private broadcast
What changed, and why it matters
This commit adds a new type of network connection called 'private-broadcast' to Bitcoin Core. It is a feature for improving user privacy when broadcasting transactions over anonymity networks like Tor or I2P. There is no indication in the commit that this fixes a security vulnerability; it is a privacy enhancement.
No security action required. Review as normal feature code for correctness and privacy behavior.
Security signals we found
No security-relevant signals present in the commit message or diff
Change is a feature addition, not a vulnerability patch
Evidence from the diff
The change introduces ConnectionType::PRIVATE_BROADCAST across the networking stack, CLI, RPC, Qt GUI, and tests. The new connection type is treated as a short-lived outbound connection (similar to feeler/addr-fetch), excluded from inbound/manual connection logic, and marked as not eligible for certain relay behaviors. It is intended for one-off transaction broadcast to a random Tor/I2P peer.
Changed components
src/net.cppsrc/net.hsrc/node/connection_types.cppsrc/node/connection_types.hsrc/bitcoin-cli.cppsrc/rpc/net.cppsrc/qt/guiutil.cppsrc/qt/rpcconsole.cppsrc/test/util/net.hInspect captured patch +30 / −2
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index d6fcaa84..11fd819a 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -452,6 +452,7 @@ private:
if (conn_type == "block-relay-only") return "block";
if (conn_type == "manual" || conn_type == "feeler") return conn_type;
if (conn_type == "addr-fetch") return "addr";
+ if (conn_type == "private-broadcast") return "priv";
return "";
}
std::string FormatServices(const UniValue& services)
@@ -703,6 +704,7 @@ public:
" \"manual\" - peer we manually added using RPC addnode or the -addnode/-connect config options\n"
" \"feeler\" - short-lived connection for testing addresses\n"
" \"addr\" - address fetch; short-lived connection for requesting addresses\n"
+ " \"priv\" - private broadcast; short-lived connection for broadcasting our transactions\n"
" net Network the peer connected through (\"ipv4\", \"ipv6\", \"onion\", \"i2p\", \"cjdns\", or \"npr\" (not publicly routable))\n"
" serv Services offered by the peer\n"
" \"n\" - NETWORK: peer can serve the full block chain\n"
diff --git a/src/net.cpp b/src/net.cpp
index 4abcf20d..8ed61b80 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -1876,6 +1876,7 @@ bool CConnman::AddConnection(const std::string& address, ConnectionType conn_typ
switch (conn_type) {
case ConnectionType::INBOUND:
case ConnectionType::MANUAL:
+ case ConnectionType::PRIVATE_BROADCAST:
return false;
case ConnectionType::OUTBOUND_FULL_RELAY:
max_connections = m_max_outbound_full_relay;
@@ -2666,6 +2667,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
// peers from addrman.
case ConnectionType::ADDR_FETCH:
case ConnectionType::FEELER:
+ case ConnectionType::PRIVATE_BROADCAST:
break;
case ConnectionType::MANUAL:
case ConnectionType::OUTBOUND_FULL_RELAY:
diff --git a/src/net.h b/src/net.h
index 1f8f1771..dcbab754 100644
--- a/src/net.h
+++ b/src/net.h
@@ -775,6 +775,7 @@ public:
case ConnectionType::MANUAL:
case ConnectionType::ADDR_FETCH:
case ConnectionType::FEELER:
+ case ConnectionType::PRIVATE_BROADCAST:
return false;
} // no default case, so the compiler can warn about missing cases
@@ -796,6 +797,7 @@ public:
case ConnectionType::FEELER:
case ConnectionType::BLOCK_RELAY:
case ConnectionType::ADDR_FETCH:
+ case ConnectionType::PRIVATE_BROADCAST:
return false;
case ConnectionType::OUTBOUND_FULL_RELAY:
case ConnectionType::MANUAL:
@@ -817,6 +819,11 @@ public:
return m_conn_type == ConnectionType::ADDR_FETCH;
}
+ bool IsPrivateBroadcastConn() const
+ {
+ return m_conn_type == ConnectionType::PRIVATE_BROADCAST;
+ }
+
bool IsInboundConn() const {
return m_conn_type == ConnectionType::INBOUND;
}
@@ -830,6 +837,7 @@ public:
case ConnectionType::OUTBOUND_FULL_RELAY:
case ConnectionType::BLOCK_RELAY:
case ConnectionType::ADDR_FETCH:
+ case ConnectionType::PRIVATE_BROADCAST:
return true;
} // no default case, so the compiler can warn about missing cases
diff --git a/src/node/connection_types.cpp b/src/node/connection_types.cpp
index 5e4dc5bf..4cf98047 100644
--- a/src/node/connection_types.cpp
+++ b/src/node/connection_types.cpp
@@ -20,6 +20,8 @@ std::string ConnectionTypeAsString(ConnectionType conn_type)
return "block-relay-only";
case ConnectionType::ADDR_FETCH:
return "addr-fetch";
+ case ConnectionType::PRIVATE_BROADCAST:
+ return "private-broadcast";
} // no default case, so the compiler can warn about missing cases
assert(false);
diff --git a/src/node/connection_types.h b/src/node/connection_types.h
index a00895e2..eeb106b6 100644
--- a/src/node/connection_types.h
+++ b/src/node/connection_types.h
@@ -75,6 +75,13 @@ enum class ConnectionType {
* AddrMan is empty.
*/
ADDR_FETCH,
+
+ /**
+ * Private broadcast connections are short-lived and only opened to
+ * privacy networks (Tor, I2P) for relaying privacy-sensitive data (like
+ * our own transactions) and closed afterwards.
+ */
+ PRIVATE_BROADCAST,
};
/** Convert ConnectionType enum to a string value */
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 3aa48088..1619227a 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -722,6 +722,8 @@ QString ConnectionTypeToQString(ConnectionType conn_type, bool prepend_direction
case ConnectionType::FEELER: return prefix + QObject::tr("Feeler");
//: Short-lived peer connection type that solicits known addresses from a peer.
case ConnectionType::ADDR_FETCH: return prefix + QObject::tr("Address Fetch");
+ //: Short-lived peer connection type that is used for broadcasting privacy-sensitive data.
+ case ConnectionType::PRIVATE_BROADCAST: return prefix + QObject::tr("Private Broadcast");
} // no default case, so the compiler can warn about missing cases
assert(false);
}
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index d6d2be7b..8723a52a 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -484,7 +484,10 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
tr("Outbound Feeler: short-lived, for testing addresses"),
/*: Explanatory text for a short-lived outbound peer connection that is used
to request addresses from a peer. */
- tr("Outbound Address Fetch: short-lived, for soliciting addresses")};
+ tr("Outbound Address Fetch: short-lived, for soliciting addresses"),
+ /*: Explanatory text for a short-lived outbound peer connection that is used
+ to broadcast privacy-sensitive data (like our transactions). */
+ tr("Private broadcast: short-lived, for broadcasting privacy-sensitive transactions")};
const QString connection_types_list{"<ul><li>" + Join(CONNECTION_TYPE_DOC, QString("</li><li>")) + "</li></ul>"};
ui->peerConnectionTypeLabel->setToolTip(ui->peerConnectionTypeLabel->toolTip().arg(connection_types_list));
const std::vector<QString> TRANSPORT_TYPE_DOC{
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index c97d4c75..e48ca1a5 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -48,7 +48,8 @@ const std::vector<std::string> CONNECTION_TYPE_DOC{
"inbound (initiated by the peer)",
"manual (added via addnode RPC or -addnode/-connect configuration options)",
"addr-fetch (short-lived automatic connection for soliciting addresses)",
- "feeler (short-lived automatic connection for testing addresses)"
+ "feeler (short-lived automatic connection for testing addresses)",
+ "private-broadcast (short-lived automatic connection for broadcasting privacy-sensitive transactions)"
};
const std::vector<std::string> TRANSPORT_TYPE_DOC{
diff --git a/src/test/util/net.h b/src/test/util/net.h
index 77954d92..605b2fa8 100644
--- a/src/test/util/net.h
+++ b/src/test/util/net.h
@@ -143,6 +143,7 @@ constexpr ConnectionType ALL_CONNECTION_TYPES[]{
ConnectionType::FEELER,
ConnectionType::BLOCK_RELAY,
ConnectionType::ADDR_FETCH,
+ ConnectionType::PRIVATE_BROADCAST,
};
constexpr auto ALL_NETWORKS = std::array{
Why this scored 15/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.