Skip to content

Commit 189c19e

Browse files
committed
Merge #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/bitcoin@0ba0802 Tree-SHA512: 4c3629434472c7dd4125253417b1be41967a508c3cfec8af5a34cad685464fbebbb6558f0f8f5c0d4463e3ffa4fa3aabd58247692cb9ab8395f4993078b9bcdf
2 parents b5a8d0c + 0ba0802 commit 189c19e

File tree

6 files changed

+312
-225
lines changed

6 files changed

+312
-225
lines changed

src/init.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1760,7 +1760,8 @@ bool AppInitMain(InitInterfaces& interfaces)
17601760
CConnman::Options connOptions;
17611761
connOptions.nLocalServices = nLocalServices;
17621762
connOptions.nMaxConnections = nMaxConnections;
1763-
connOptions.nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, connOptions.nMaxConnections);
1763+
connOptions.m_max_outbound_full_relay = std::min(MAX_OUTBOUND_FULL_RELAY_CONNECTIONS, connOptions.nMaxConnections);
1764+
connOptions.m_max_outbound_block_relay = std::min(MAX_BLOCKS_ONLY_CONNECTIONS, connOptions.nMaxConnections-connOptions.m_max_outbound_full_relay);
17641765
connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS;
17651766
connOptions.nMaxFeeler = 1;
17661767
connOptions.nBestHeight = chain_active_height;

src/net.cpp

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ static CAddress GetBindAddress(SOCKET sock)
352352
return addr_bind;
353353
}
354354

355-
CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection)
355+
CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection, bool block_relay_only)
356356
{
357357
if (pszDest == nullptr) {
358358
if (IsLocal(addrConnect))
@@ -442,7 +442,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
442442
NodeId id = GetNewNodeId();
443443
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
444444
CAddress addr_bind = GetBindAddress(hSocket);
445-
CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false);
445+
CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false, block_relay_only);
446446
pnode->AddRef();
447447

448448
return pnode;
@@ -499,9 +499,11 @@ void CNode::copyStats(CNodeStats &stats)
499499
X(nServices);
500500
X(addr);
501501
X(addrBind);
502-
{
503-
LOCK(cs_filter);
504-
X(fRelayTxes);
502+
if (m_tx_relay != nullptr) {
503+
LOCK(m_tx_relay->cs_filter);
504+
stats.fRelayTxes = m_tx_relay->fRelayTxes;
505+
} else {
506+
stats.fRelayTxes = false;
505507
}
506508
X(nLastSend);
507509
X(nLastRecv);
@@ -528,9 +530,11 @@ void CNode::copyStats(CNodeStats &stats)
528530
}
529531
X(m_legacyWhitelisted);
530532
X(m_permissionFlags);
531-
{
532-
LOCK(cs_feeFilter);
533-
X(minFeeFilter);
533+
if (m_tx_relay != nullptr) {
534+
LOCK(m_tx_relay->cs_feeFilter);
535+
stats.minFeeFilter = m_tx_relay->minFeeFilter;
536+
} else {
537+
stats.minFeeFilter = 0;
534538
}
535539

536540
// It is common for nodes with good ping times to suddenly become lagged,
@@ -818,11 +822,17 @@ bool CConnman::AttemptToEvictConnection()
818822
continue;
819823
if (node->fDisconnect)
820824
continue;
821-
LOCK(node->cs_filter);
825+
bool peer_relay_txes = false;
826+
bool peer_filter_not_null = false;
827+
if (node->m_tx_relay != nullptr) {
828+
LOCK(node->m_tx_relay->cs_filter);
829+
peer_relay_txes = node->m_tx_relay->fRelayTxes;
830+
peer_filter_not_null = node->m_tx_relay->pfilter != nullptr;
831+
}
822832
NodeEvictionCandidate candidate = {node->GetId(), node->nTimeConnected, node->nMinPingUsecTime,
823833
node->nLastBlockTime, node->nLastTXTime,
824834
HasAllDesirableServiceFlags(node->nServices),
825-
node->fRelayTxes, node->pfilter != nullptr, node->addr, node->nKeyedNetGroup,
835+
peer_relay_txes, peer_filter_not_null, node->addr, node->nKeyedNetGroup,
826836
node->m_prefer_evict};
827837
vEvictionCandidates.push_back(candidate);
828838
}
@@ -895,7 +905,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
895905
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
896906
CAddress addr;
897907
int nInbound = 0;
898-
int nMaxInbound = nMaxConnections - (nMaxOutbound + nMaxFeeler);
908+
int nMaxInbound = nMaxConnections - m_max_outbound;
899909

900910
if (hSocket != INVALID_SOCKET) {
901911
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) {
@@ -1655,7 +1665,7 @@ int CConnman::GetExtraOutboundCount()
16551665
}
16561666
}
16571667
}
1658-
return std::max(nOutbound - nMaxOutbound, 0);
1668+
return std::max(nOutbound - m_max_outbound_full_relay - m_max_outbound_block_relay, 0);
16591669
}
16601670

16611671
void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
@@ -1715,7 +1725,8 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
17151725
CAddress addrConnect;
17161726

17171727
// Only connect out to one peer per network group (/16 for IPv4).
1718-
int nOutbound = 0;
1728+
int nOutboundFullRelay = 0;
1729+
int nOutboundBlockRelay = 0;
17191730
std::set<std::vector<unsigned char> > setConnected;
17201731
{
17211732
LOCK(cs_vNodes);
@@ -1727,7 +1738,11 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
17271738
// also have the added issue that they're attacker controlled and could be used
17281739
// to prevent us from connecting to particular hosts if we used them here.
17291740
setConnected.insert(pnode->addr.GetGroup());
1730-
nOutbound++;
1741+
if (pnode->m_tx_relay == nullptr) {
1742+
nOutboundBlockRelay++;
1743+
} else if (!pnode->fFeeler) {
1744+
nOutboundFullRelay++;
1745+
}
17311746
}
17321747
}
17331748
}
@@ -1746,7 +1761,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
17461761
//
17471762
bool fFeeler = false;
17481763

1749-
if (nOutbound >= nMaxOutbound && !GetTryNewOutboundPeer()) {
1764+
if (nOutboundFullRelay >= m_max_outbound_full_relay && nOutboundBlockRelay >= m_max_outbound_block_relay && !GetTryNewOutboundPeer()) {
17501765
int64_t nTime = GetTimeMicros(); // The current time right now (in microseconds).
17511766
if (nTime > nNextFeeler) {
17521767
nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);
@@ -1820,7 +1835,14 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
18201835
LogPrint(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToString());
18211836
}
18221837

1823-
OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, nullptr, false, fFeeler);
1838+
// Open this connection as block-relay-only if we're already at our
1839+
// full-relay capacity, but not yet at our block-relay peer limit.
1840+
// (It should not be possible for fFeeler to be set if we're not
1841+
// also at our block-relay peer limit, but check against that as
1842+
// well for sanity.)
1843+
bool block_relay_only = nOutboundBlockRelay < m_max_outbound_block_relay && !fFeeler && nOutboundFullRelay >= m_max_outbound_full_relay;
1844+
1845+
OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, nullptr, false, fFeeler, false, block_relay_only);
18241846
}
18251847
}
18261848
}
@@ -1907,7 +1929,7 @@ void CConnman::ThreadOpenAddedConnections()
19071929
}
19081930

19091931
// if successful, this moves the passed grant to the constructed node
1910-
void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool manual_connection)
1932+
void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool manual_connection, bool block_relay_only)
19111933
{
19121934
//
19131935
// Initiate outbound network connection
@@ -1926,7 +1948,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
19261948
} else if (FindNode(std::string(pszDest)))
19271949
return;
19281950

1929-
CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, manual_connection);
1951+
CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, manual_connection, block_relay_only);
19301952

19311953
if (!pnode)
19321954
return;
@@ -2229,7 +2251,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
22292251

22302252
if (semOutbound == nullptr) {
22312253
// initialize semaphore
2232-
semOutbound = MakeUnique<CSemaphore>(std::min((nMaxOutbound + nMaxFeeler), nMaxConnections));
2254+
semOutbound = MakeUnique<CSemaphore>(std::min(m_max_outbound, nMaxConnections));
22332255
}
22342256
if (semAddnode == nullptr) {
22352257
// initialize semaphore
@@ -2307,7 +2329,7 @@ void CConnman::Interrupt()
23072329
InterruptSocks5(true);
23082330

23092331
if (semOutbound) {
2310-
for (int i=0; i<(nMaxOutbound + nMaxFeeler); i++) {
2332+
for (int i=0; i<m_max_outbound; i++) {
23112333
semOutbound->post();
23122334
}
23132335
}
@@ -2617,14 +2639,17 @@ int CConnman::GetBestHeight() const
26172639

26182640
unsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; }
26192641

2620-
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)
2642+
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)
26212643
: nTimeConnected(GetSystemTimeInSeconds()),
26222644
addr(addrIn),
26232645
addrBind(addrBindIn),
26242646
fInbound(fInboundIn),
26252647
nKeyedNetGroup(nKeyedNetGroupIn),
26262648
addrKnown(5000, 0.001),
2627-
filterInventoryKnown(50000, 0.000001),
2649+
// Don't relay addr messages to peers that we connect to as block-relay-only
2650+
// peers (to prevent adversaries from inferring these links from addr
2651+
// traffic).
2652+
m_addr_relay_peer(!block_relay_only),
26282653
id(idIn),
26292654
nLocalHostNonce(nLocalHostNonceIn),
26302655
nLocalServices(nLocalServicesIn),
@@ -2633,8 +2658,9 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
26332658
hSocket = hSocketIn;
26342659
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
26352660
hashContinue = uint256();
2636-
filterInventoryKnown.reset();
2637-
pfilter = MakeUnique<CBloomFilter>();
2661+
if (!block_relay_only) {
2662+
m_tx_relay = MakeUnique<TxRelay>();
2663+
}
26382664

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

0 commit comments

Comments
 (0)