net_processing: handle ConnectionType::PRIVATE_BROADCAST connections

For connections of type `ConnectionType::PRIVATE_BROADCAST`:
* After receiving VERACK, send a transaction from the list of
  transactions for private broadcast and disconnect
* Don't process any messages after VERACK (modulo `GETDATA` and `PONG`)
* Don't send any messages other than the minimum required for the
  transaction send - `INV`, `TX`, `PING`.
This commit is contained in:
Vasil Dimov
2023-04-19 15:40:30 +02:00
parent 30a9853ad3
commit 2de53eee74
4 changed files with 331 additions and 14 deletions

View File

@@ -354,7 +354,16 @@ bool CConnman::CheckIncomingNonce(uint64_t nonce)
{
LOCK(m_nodes_mutex);
for (const CNode* pnode : m_nodes) {
if (!pnode->fSuccessfullyConnected && !pnode->IsInboundConn() && pnode->GetLocalNonce() == nonce)
// Omit private broadcast connections from this check to prevent this privacy attack:
// - We connect to a peer in an attempt to privately broadcast a transaction. From our
// VERSION message the peer deducts that this is a short-lived connection for
// broadcasting a transaction, takes our nonce and delays their VERACK.
// - The peer starts connecting to (clearnet) nodes and sends them a VERSION message
// which contains our nonce. If the peer manages to connect to us we would disconnect.
// - Upon a disconnect, the peer knows our clearnet address. They go back to the short
// lived privacy broadcast connection and continue with VERACK.
if (!pnode->fSuccessfullyConnected && !pnode->IsInboundConn() && !pnode->IsPrivateBroadcastConn() &&
pnode->GetLocalNonce() == nonce)
return false;
}
return true;
@@ -4048,10 +4057,26 @@ bool CConnman::NodeFullyConnected(const CNode* pnode)
return pnode && pnode->fSuccessfullyConnected && !pnode->fDisconnect;
}
/// Private broadcast connections only need to send certain message types.
/// Other messages are not needed and may degrade privacy.
static bool IsOutboundMessageAllowedInPrivateBroadcast(std::string_view type) noexcept
{
return type == NetMsgType::VERSION ||
type == NetMsgType::VERACK ||
type == NetMsgType::INV ||
type == NetMsgType::TX ||
type == NetMsgType::PING;
}
void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
{
AssertLockNotHeld(m_total_bytes_sent_mutex);
if (pnode->IsPrivateBroadcastConn() && !IsOutboundMessageAllowedInPrivateBroadcast(msg.m_type)) {
LogDebug(BCLog::PRIVBROADCAST, "Omitting send of message '%s', peer=%d%s", msg.m_type, pnode->GetId(), pnode->LogIP(fLogIPs));
return;
}
if (!m_private_broadcast.m_outbound_tor_ok_at_least_once.load() && !pnode->IsInboundConn() &&
pnode->addr.IsTor() && msg.m_type == NetMsgType::VERACK) {
// If we are sending the peer VERACK that means we successfully sent