Skip to content

Commit f0ce263

Browse files
fanquakevijaydasmp
authored andcommitted
Merge bitcoin#15759: p2p: Add 2 outbound block-relay-only connections
0ba0802 Disconnect peers violating blocks-only mode (Suhas Daftuar) 937eba9 doc: improve comments relating to block-relay-only peers (Suhas Daftuar) 430f489 Don't relay addr messages to block-relay-only peers (Suhas Daftuar) 3a5e885 Add 2 outbound block-relay-only connections (Suhas Daftuar) b83f51a Add comment explaining intended use of m_tx_relay (Suhas Daftuar) e75c39c Check that tx_relay is initialized before access (Suhas Daftuar) c4aa2ba [refactor] Change tx_relay structure to be unique_ptr (Suhas Daftuar) 4de0dba [refactor] Move tx relay state to separate structure (Suhas Daftuar) 26a93bc Remove unused variable (Suhas Daftuar) Pull request description: Transaction relay is optimized for a combination of redundancy/robustness as well as bandwidth minimization -- as a result transaction relay leaks information that adversaries can use to infer the network topology. Network topology is better kept private for (at least) two reasons: (a) Knowledge of the network graph can make it easier to find the source IP of a given transaction. (b) Knowledge of the network graph could be used to split a target node or nodes from the honest network (eg by knowing which peers to attack in order to achieve a network split). We can eliminate the risks of (b) by separating block relay from transaction relay; inferring network connectivity from the relay of blocks/block headers is much more expensive for an adversary. After this commit, bitcoind will make 2 additional outbound connections that are only used for block relay. (In the future, we might consider rotating our transaction-relay peers to help limit the effects of (a).) ACKs for top commit: sipa: ACK 0ba0802 ajtowns: ACK 0ba0802 -- code review, ran tests. ran it on mainnet for a couple of days with MAX_BLOCKS_ONLY_CONNECTIONS upped from 2 to 16 and didn't observe any unexpected behaviour: it disconnected a couple of peers that tried sending inv's, and it successfully did compact block relay with some block relay peers. TheBlueMatt: re-utACK 0ba0802. Pointed out that stats.fRelayTxes was sometimes uninitialized for blocksonly peers (though its not a big deal and only effects RPC), which has since been fixed here. Otherwise changes are pretty trivial so looks good. jnewbery: utACK 0ba0802 jamesob: ACK bitcoin@0ba0802 Tree-SHA512: 4c3629434472c7dd4125253417b1be41967a508c3cfec8af5a34cad685464fbebbb6558f0f8f5c0d4463e3ffa4fa3aabd58247692cb9ab8395f4993078b9bcdf
1 parent 53776b4 commit f0ce263

File tree

6 files changed

+302
-166
lines changed

6 files changed

+302
-166
lines changed

src/init.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2447,7 +2447,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
24472447
CConnman::Options connOptions;
24482448
connOptions.nLocalServices = nLocalServices;
24492449
connOptions.nMaxConnections = nMaxConnections;
2450-
connOptions.nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, connOptions.nMaxConnections);
2450+
connOptions.m_max_outbound_full_relay = std::min(MAX_OUTBOUND_FULL_RELAY_CONNECTIONS, connOptions.nMaxConnections);
2451+
connOptions.m_max_outbound_block_relay = std::min(MAX_BLOCKS_ONLY_CONNECTIONS, connOptions.nMaxConnections-connOptions.m_max_outbound_full_relay);
24512452
connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS;
24522453
connOptions.nMaxFeeler = 1;
24532454
connOptions.nBestHeight = chain_active_height;

src/net.cpp

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ static CAddress GetBindAddress(SOCKET sock)
381381
return addr_bind;
382382
}
383383

384-
CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection)
384+
CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection, bool block_relay_only)
385385
{
386386
if (pszDest == nullptr) {
387387
bool fAllowLocal = Params().AllowMultiplePorts() && addrConnect.GetPort() != GetListenPort();
@@ -478,7 +478,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
478478
NodeId id = GetNewNodeId();
479479
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
480480
CAddress addr_bind = GetBindAddress(hSocket);
481-
CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false);
481+
CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false, block_relay_only);
482482
pnode->AddRef();
483483
statsClient.inc("peers.connect", 1.0f);
484484

@@ -561,10 +561,12 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
561561
X(nServices);
562562
X(addr);
563563
X(addrBind);
564-
stats.m_mapped_as = addr.GetMappedAS(m_asmap);
565-
{
566-
LOCK(cs_filter);
567-
X(fRelayTxes);
564+
stats.m_mapped_as = addr.GetMappedAS(m_asmap);
565+
if (m_tx_relay != nullptr) {
566+
LOCK(m_tx_relay->cs_filter);
567+
stats.fRelayTxes = m_tx_relay->fRelayTxes;
568+
} else {
569+
stats.fRelayTxes = false;
568570
}
569571
X(nLastSend);
570572
X(nLastRecv);
@@ -936,11 +938,17 @@ bool CConnman::AttemptToEvictConnection()
936938
}
937939
}
938940

939-
LOCK(node->cs_filter);
941+
bool peer_relay_txes = false;
942+
bool peer_filter_not_null = false;
943+
if (node->m_tx_relay != nullptr) {
944+
LOCK(node->m_tx_relay->cs_filter);
945+
peer_relay_txes = node->m_tx_relay->fRelayTxes;
946+
peer_filter_not_null = node->m_tx_relay->pfilter != nullptr;
947+
}
940948
NodeEvictionCandidate candidate = {node->GetId(), node->nTimeConnected, node->nMinPingUsecTime,
941949
node->nLastBlockTime, node->nLastTXTime,
942950
HasAllDesirableServiceFlags(node->nServices),
943-
node->fRelayTxes, node->pfilter != nullptr, node->nKeyedNetGroup,
951+
peer_relay_txes, peer_filter_not_null, node->nKeyedNetGroup,
944952
node->m_prefer_evict};
945953
vEvictionCandidates.push_back(candidate);
946954
}
@@ -1014,7 +1022,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
10141022
CAddress addr;
10151023
int nInbound = 0;
10161024
int nVerifiedInboundMasternodes = 0;
1017-
int nMaxInbound = nMaxConnections - (nMaxOutbound + nMaxFeeler);
1025+
int nMaxInbound = nMaxConnections - m_max_outbound;
10181026

10191027
if (hSocket != INVALID_SOCKET) {
10201028
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) {
@@ -2060,7 +2068,7 @@ int CConnman::GetExtraOutboundCount()
20602068
}
20612069
}
20622070
}
2063-
return std::max(nOutbound - nMaxOutbound, 0);
2071+
return std::max(nOutbound - m_max_outbound_full_relay - m_max_outbound_block_relay, 0);
20642072
}
20652073

20662074
void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
@@ -2120,8 +2128,8 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
21202128
CAddress addrConnect;
21212129

21222130
// Only connect out to one peer per network group (/16 for IPv4).
2123-
// This is only done for mainnet and testnet
2124-
int nOutbound = 0;
2131+
int nOutboundFullRelay = 0;
2132+
int nOutboundBlockRelay = 0;
21252133
std::set<std::vector<unsigned char> > setConnected;
21262134
if (!Params().AllowMultipleAddressesFromGroup()) {
21272135
LOCK(cs_vNodes);
@@ -2133,7 +2141,11 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
21332141
// also have the added issue that they're attacker controlled and could be used
21342142
// to prevent us from connecting to particular hosts if we used them here.
21352143
setConnected.insert(pnode->addr.GetGroup(addrman.m_asmap));
2136-
nOutbound++;
2144+
if (pnode->m_tx_relay == nullptr) {
2145+
nOutboundBlockRelay++;
2146+
} else if (!pnode->fFeeler) {
2147+
nOutboundFullRelay++;
2148+
}
21372149
}
21382150
}
21392151
}
@@ -2163,7 +2175,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
21632175
//
21642176
bool fFeeler = false;
21652177

2166-
if (nOutbound >= nMaxOutbound && !GetTryNewOutboundPeer()) {
2178+
if (nOutboundFullRelay >= m_max_outbound_full_relay && nOutboundBlockRelay >= m_max_outbound_block_relay && !GetTryNewOutboundPeer()) {
21672179
int64_t nTime = GetTimeMicros(); // The current time right now (in microseconds).
21682180
if (nTime > nNextFeeler) {
21692181
nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);
@@ -2255,7 +2267,14 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
22552267
}
22562268
}
22572269

2258-
OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, nullptr, false, fFeeler);
2270+
// Open this connection as block-relay-only if we're already at our
2271+
// full-relay capacity, but not yet at our block-relay peer limit.
2272+
// (It should not be possible for fFeeler to be set if we're not
2273+
// also at our block-relay peer limit, but check against that as
2274+
// well for sanity.)
2275+
bool block_relay_only = nOutboundBlockRelay < m_max_outbound_block_relay && !fFeeler && nOutboundFullRelay >= m_max_outbound_full_relay;
2276+
2277+
OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, nullptr, false, fFeeler, false, block_relay_only);
22592278
}
22602279
}
22612280
}
@@ -2484,7 +2503,7 @@ void CConnman::ThreadOpenMasternodeConnections()
24842503
}
24852504

24862505
// if successful, this moves the passed grant to the constructed node
2487-
void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool manual_connection, bool masternode_connection, bool masternode_probe_connection)
2506+
void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool manual_connection, bool masternode_connection, bool masternode_probe_connection, bool block_relay_only)
24882507
{
24892508
//
24902509
// Initiate outbound network connection
@@ -2519,7 +2538,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
25192538
};
25202539

25212540
LogPrint(BCLog::NET_NETCONN, "CConnman::%s -- connecting to %s\n", __func__, getIpStr());
2522-
CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, manual_connection);
2541+
CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, manual_connection, block_relay_only);
25232542

25242543
if (!pnode) {
25252544
LogPrint(BCLog::NET_NETCONN, "CConnman::%s -- ConnectNode failed for %s\n", __func__, getIpStr());
@@ -2894,7 +2913,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
28942913

28952914
if (semOutbound == nullptr) {
28962915
// initialize semaphore
2897-
semOutbound = MakeUnique<CSemaphore>(std::min((nMaxOutbound + nMaxFeeler), nMaxConnections));
2916+
semOutbound = MakeUnique<CSemaphore>(std::min(m_max_outbound, nMaxConnections));
28982917
}
28992918
if (semAddnode == nullptr) {
29002919
// initialize semaphore
@@ -3024,7 +3043,7 @@ void CConnman::Interrupt()
30243043
InterruptSocks5(true);
30253044

30263045
if (semOutbound) {
3027-
for (int i=0; i<(nMaxOutbound + nMaxFeeler); i++) {
3046+
for (int i=0; i<m_max_outbound; i++) {
30283047
semOutbound->post();
30293048
}
30303049
}
@@ -3596,14 +3615,17 @@ int CConnman::GetBestHeight() const
35963615

35973616
unsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; }
35983617

3599-
CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, bool fInboundIn)
3618+
CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, bool fInboundIn, bool block_relay_only)
36003619
: nTimeConnected(GetSystemTimeInSeconds()),
36013620
addr(addrIn),
36023621
addrBind(addrBindIn),
36033622
fInbound(fInboundIn),
36043623
nKeyedNetGroup(nKeyedNetGroupIn),
36053624
addrKnown(5000, 0.001),
3606-
filterInventoryKnown(50000, 0.000001),
3625+
// Don't relay addr messages to peers that we connect to as block-relay-only
3626+
// peers (to prevent adversaries from inferring these links from addr
3627+
// traffic).
3628+
m_addr_relay_peer(!block_relay_only),
36073629
id(idIn),
36083630
nLocalHostNonce(nLocalHostNonceIn),
36093631
nLocalServices(nLocalServicesIn),
@@ -3612,7 +3634,9 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
36123634
hSocket = hSocketIn;
36133635
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
36143636
hashContinue = uint256();
3615-
filterInventoryKnown.reset();
3637+
if (!block_relay_only) {
3638+
m_tx_relay = MakeUnique<TxRelay>();
3639+
}
36163640

36173641
for (const std::string &msg : getAllNetMessageTypes())
36183642
mapRecvBytesPerMsgCmd[msg] = 0;

0 commit comments

Comments
 (0)