mirror of
https://github.com/bitcoin/bitcoin.git
synced 2024-11-20 02:25:40 +01:00
Merge #15759: p2p: Add 2 outbound block-relay-only connections
0ba08020c9
Disconnect peers violating blocks-only mode (Suhas Daftuar)937eba91e1
doc: improve comments relating to block-relay-only peers (Suhas Daftuar)430f489027
Don't relay addr messages to block-relay-only peers (Suhas Daftuar)3a5e885306
Add 2 outbound block-relay-only connections (Suhas Daftuar)b83f51a4bb
Add comment explaining intended use of m_tx_relay (Suhas Daftuar)e75c39cd42
Check that tx_relay is initialized before access (Suhas Daftuar)c4aa2ba822
[refactor] Change tx_relay structure to be unique_ptr (Suhas Daftuar)4de0dbac9b
[refactor] Move tx relay state to separate structure (Suhas Daftuar)26a93bce29
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: ACK0ba08020c9
ajtowns: ACK0ba08020c9
-- 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-utACK0ba08020c9
. 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: utACK0ba08020c9
jamesob: ACK0ba08020c9
Tree-SHA512: 4c3629434472c7dd4125253417b1be41967a508c3cfec8af5a34cad685464fbebbb6558f0f8f5c0d4463e3ffa4fa3aabd58247692cb9ab8395f4993078b9bcdf
This commit is contained in:
commit
189c19e012
@ -1760,7 +1760,8 @@ bool AppInitMain(InitInterfaces& interfaces)
|
||||
CConnman::Options connOptions;
|
||||
connOptions.nLocalServices = nLocalServices;
|
||||
connOptions.nMaxConnections = nMaxConnections;
|
||||
connOptions.nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, connOptions.nMaxConnections);
|
||||
connOptions.m_max_outbound_full_relay = std::min(MAX_OUTBOUND_FULL_RELAY_CONNECTIONS, connOptions.nMaxConnections);
|
||||
connOptions.m_max_outbound_block_relay = std::min(MAX_BLOCKS_ONLY_CONNECTIONS, connOptions.nMaxConnections-connOptions.m_max_outbound_full_relay);
|
||||
connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS;
|
||||
connOptions.nMaxFeeler = 1;
|
||||
connOptions.nBestHeight = chain_active_height;
|
||||
|
74
src/net.cpp
74
src/net.cpp
@ -352,7 +352,7 @@ static CAddress GetBindAddress(SOCKET sock)
|
||||
return addr_bind;
|
||||
}
|
||||
|
||||
CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection)
|
||||
CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection, bool block_relay_only)
|
||||
{
|
||||
if (pszDest == nullptr) {
|
||||
if (IsLocal(addrConnect))
|
||||
@ -442,7 +442,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
||||
NodeId id = GetNewNodeId();
|
||||
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
|
||||
CAddress addr_bind = GetBindAddress(hSocket);
|
||||
CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false);
|
||||
CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false, block_relay_only);
|
||||
pnode->AddRef();
|
||||
|
||||
return pnode;
|
||||
@ -499,9 +499,11 @@ void CNode::copyStats(CNodeStats &stats)
|
||||
X(nServices);
|
||||
X(addr);
|
||||
X(addrBind);
|
||||
{
|
||||
LOCK(cs_filter);
|
||||
X(fRelayTxes);
|
||||
if (m_tx_relay != nullptr) {
|
||||
LOCK(m_tx_relay->cs_filter);
|
||||
stats.fRelayTxes = m_tx_relay->fRelayTxes;
|
||||
} else {
|
||||
stats.fRelayTxes = false;
|
||||
}
|
||||
X(nLastSend);
|
||||
X(nLastRecv);
|
||||
@ -528,9 +530,11 @@ void CNode::copyStats(CNodeStats &stats)
|
||||
}
|
||||
X(m_legacyWhitelisted);
|
||||
X(m_permissionFlags);
|
||||
{
|
||||
LOCK(cs_feeFilter);
|
||||
X(minFeeFilter);
|
||||
if (m_tx_relay != nullptr) {
|
||||
LOCK(m_tx_relay->cs_feeFilter);
|
||||
stats.minFeeFilter = m_tx_relay->minFeeFilter;
|
||||
} else {
|
||||
stats.minFeeFilter = 0;
|
||||
}
|
||||
|
||||
// It is common for nodes with good ping times to suddenly become lagged,
|
||||
@ -818,11 +822,17 @@ bool CConnman::AttemptToEvictConnection()
|
||||
continue;
|
||||
if (node->fDisconnect)
|
||||
continue;
|
||||
LOCK(node->cs_filter);
|
||||
bool peer_relay_txes = false;
|
||||
bool peer_filter_not_null = false;
|
||||
if (node->m_tx_relay != nullptr) {
|
||||
LOCK(node->m_tx_relay->cs_filter);
|
||||
peer_relay_txes = node->m_tx_relay->fRelayTxes;
|
||||
peer_filter_not_null = node->m_tx_relay->pfilter != nullptr;
|
||||
}
|
||||
NodeEvictionCandidate candidate = {node->GetId(), node->nTimeConnected, node->nMinPingUsecTime,
|
||||
node->nLastBlockTime, node->nLastTXTime,
|
||||
HasAllDesirableServiceFlags(node->nServices),
|
||||
node->fRelayTxes, node->pfilter != nullptr, node->addr, node->nKeyedNetGroup,
|
||||
peer_relay_txes, peer_filter_not_null, node->addr, node->nKeyedNetGroup,
|
||||
node->m_prefer_evict};
|
||||
vEvictionCandidates.push_back(candidate);
|
||||
}
|
||||
@ -895,7 +905,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
|
||||
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
|
||||
CAddress addr;
|
||||
int nInbound = 0;
|
||||
int nMaxInbound = nMaxConnections - (nMaxOutbound + nMaxFeeler);
|
||||
int nMaxInbound = nMaxConnections - m_max_outbound;
|
||||
|
||||
if (hSocket != INVALID_SOCKET) {
|
||||
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) {
|
||||
@ -1655,7 +1665,7 @@ int CConnman::GetExtraOutboundCount()
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::max(nOutbound - nMaxOutbound, 0);
|
||||
return std::max(nOutbound - m_max_outbound_full_relay - m_max_outbound_block_relay, 0);
|
||||
}
|
||||
|
||||
void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
|
||||
@ -1715,7 +1725,8 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
|
||||
CAddress addrConnect;
|
||||
|
||||
// Only connect out to one peer per network group (/16 for IPv4).
|
||||
int nOutbound = 0;
|
||||
int nOutboundFullRelay = 0;
|
||||
int nOutboundBlockRelay = 0;
|
||||
std::set<std::vector<unsigned char> > setConnected;
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
@ -1727,7 +1738,11 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
|
||||
// also have the added issue that they're attacker controlled and could be used
|
||||
// to prevent us from connecting to particular hosts if we used them here.
|
||||
setConnected.insert(pnode->addr.GetGroup());
|
||||
nOutbound++;
|
||||
if (pnode->m_tx_relay == nullptr) {
|
||||
nOutboundBlockRelay++;
|
||||
} else if (!pnode->fFeeler) {
|
||||
nOutboundFullRelay++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1746,7 +1761,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
|
||||
//
|
||||
bool fFeeler = false;
|
||||
|
||||
if (nOutbound >= nMaxOutbound && !GetTryNewOutboundPeer()) {
|
||||
if (nOutboundFullRelay >= m_max_outbound_full_relay && nOutboundBlockRelay >= m_max_outbound_block_relay && !GetTryNewOutboundPeer()) {
|
||||
int64_t nTime = GetTimeMicros(); // The current time right now (in microseconds).
|
||||
if (nTime > nNextFeeler) {
|
||||
nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);
|
||||
@ -1820,7 +1835,14 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
|
||||
LogPrint(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToString());
|
||||
}
|
||||
|
||||
OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, nullptr, false, fFeeler);
|
||||
// Open this connection as block-relay-only if we're already at our
|
||||
// full-relay capacity, but not yet at our block-relay peer limit.
|
||||
// (It should not be possible for fFeeler to be set if we're not
|
||||
// also at our block-relay peer limit, but check against that as
|
||||
// well for sanity.)
|
||||
bool block_relay_only = nOutboundBlockRelay < m_max_outbound_block_relay && !fFeeler && nOutboundFullRelay >= m_max_outbound_full_relay;
|
||||
|
||||
OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, nullptr, false, fFeeler, false, block_relay_only);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1907,7 +1929,7 @@ void CConnman::ThreadOpenAddedConnections()
|
||||
}
|
||||
|
||||
// if successful, this moves the passed grant to the constructed node
|
||||
void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool manual_connection)
|
||||
void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler, bool manual_connection, bool block_relay_only)
|
||||
{
|
||||
//
|
||||
// Initiate outbound network connection
|
||||
@ -1926,7 +1948,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
|
||||
} else if (FindNode(std::string(pszDest)))
|
||||
return;
|
||||
|
||||
CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, manual_connection);
|
||||
CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, manual_connection, block_relay_only);
|
||||
|
||||
if (!pnode)
|
||||
return;
|
||||
@ -2229,7 +2251,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
|
||||
|
||||
if (semOutbound == nullptr) {
|
||||
// initialize semaphore
|
||||
semOutbound = MakeUnique<CSemaphore>(std::min((nMaxOutbound + nMaxFeeler), nMaxConnections));
|
||||
semOutbound = MakeUnique<CSemaphore>(std::min(m_max_outbound, nMaxConnections));
|
||||
}
|
||||
if (semAddnode == nullptr) {
|
||||
// initialize semaphore
|
||||
@ -2307,7 +2329,7 @@ void CConnman::Interrupt()
|
||||
InterruptSocks5(true);
|
||||
|
||||
if (semOutbound) {
|
||||
for (int i=0; i<(nMaxOutbound + nMaxFeeler); i++) {
|
||||
for (int i=0; i<m_max_outbound; i++) {
|
||||
semOutbound->post();
|
||||
}
|
||||
}
|
||||
@ -2617,14 +2639,17 @@ int CConnman::GetBestHeight() const
|
||||
|
||||
unsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; }
|
||||
|
||||
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)
|
||||
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)
|
||||
: nTimeConnected(GetSystemTimeInSeconds()),
|
||||
addr(addrIn),
|
||||
addrBind(addrBindIn),
|
||||
fInbound(fInboundIn),
|
||||
nKeyedNetGroup(nKeyedNetGroupIn),
|
||||
addrKnown(5000, 0.001),
|
||||
filterInventoryKnown(50000, 0.000001),
|
||||
// Don't relay addr messages to peers that we connect to as block-relay-only
|
||||
// peers (to prevent adversaries from inferring these links from addr
|
||||
// traffic).
|
||||
m_addr_relay_peer(!block_relay_only),
|
||||
id(idIn),
|
||||
nLocalHostNonce(nLocalHostNonceIn),
|
||||
nLocalServices(nLocalServicesIn),
|
||||
@ -2633,8 +2658,9 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
|
||||
hSocket = hSocketIn;
|
||||
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
|
||||
hashContinue = uint256();
|
||||
filterInventoryKnown.reset();
|
||||
pfilter = MakeUnique<CBloomFilter>();
|
||||
if (!block_relay_only) {
|
||||
m_tx_relay = MakeUnique<TxRelay>();
|
||||
}
|
||||
|
||||
for (const std::string &msg : getAllNetMessageTypes())
|
||||
mapRecvBytesPerMsgCmd[msg] = 0;
|
||||
|
105
src/net.h
105
src/net.h
@ -61,10 +61,12 @@ static const unsigned int MAX_ADDR_TO_SEND = 1000;
|
||||
static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 4 * 1000 * 1000;
|
||||
/** Maximum length of the user agent string in `version` message */
|
||||
static const unsigned int MAX_SUBVERSION_LENGTH = 256;
|
||||
/** Maximum number of automatic outgoing nodes */
|
||||
static const int MAX_OUTBOUND_CONNECTIONS = 8;
|
||||
/** Maximum number of automatic outgoing nodes over which we'll relay everything (blocks, tx, addrs, etc) */
|
||||
static const int MAX_OUTBOUND_FULL_RELAY_CONNECTIONS = 8;
|
||||
/** Maximum number of addnode outgoing nodes */
|
||||
static const int MAX_ADDNODE_CONNECTIONS = 8;
|
||||
/** Maximum number of block-relay-only outgoing connections */
|
||||
static const int MAX_BLOCKS_ONLY_CONNECTIONS = 2;
|
||||
/** -listen default */
|
||||
static const bool DEFAULT_LISTEN = true;
|
||||
/** -upnp default */
|
||||
@ -131,7 +133,8 @@ public:
|
||||
{
|
||||
ServiceFlags nLocalServices = NODE_NONE;
|
||||
int nMaxConnections = 0;
|
||||
int nMaxOutbound = 0;
|
||||
int m_max_outbound_full_relay = 0;
|
||||
int m_max_outbound_block_relay = 0;
|
||||
int nMaxAddnode = 0;
|
||||
int nMaxFeeler = 0;
|
||||
int nBestHeight = 0;
|
||||
@ -155,10 +158,12 @@ public:
|
||||
void Init(const Options& connOptions) {
|
||||
nLocalServices = connOptions.nLocalServices;
|
||||
nMaxConnections = connOptions.nMaxConnections;
|
||||
nMaxOutbound = std::min(connOptions.nMaxOutbound, connOptions.nMaxConnections);
|
||||
m_max_outbound_full_relay = std::min(connOptions.m_max_outbound_full_relay, connOptions.nMaxConnections);
|
||||
m_max_outbound_block_relay = connOptions.m_max_outbound_block_relay;
|
||||
m_use_addrman_outgoing = connOptions.m_use_addrman_outgoing;
|
||||
nMaxAddnode = connOptions.nMaxAddnode;
|
||||
nMaxFeeler = connOptions.nMaxFeeler;
|
||||
m_max_outbound = m_max_outbound_full_relay + m_max_outbound_block_relay + nMaxFeeler;
|
||||
nBestHeight = connOptions.nBestHeight;
|
||||
clientInterface = connOptions.uiInterface;
|
||||
m_banman = connOptions.m_banman;
|
||||
@ -197,7 +202,7 @@ public:
|
||||
bool GetNetworkActive() const { return fNetworkActive; };
|
||||
bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; };
|
||||
void SetNetworkActive(bool active);
|
||||
void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false, bool fFeeler = false, bool manual_connection = false);
|
||||
void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false, bool fFeeler = false, bool manual_connection = false, bool block_relay_only = false);
|
||||
bool CheckIncomingNonce(uint64_t nonce);
|
||||
|
||||
bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func);
|
||||
@ -253,7 +258,7 @@ public:
|
||||
void AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty = 0);
|
||||
std::vector<CAddress> GetAddresses();
|
||||
|
||||
// This allows temporarily exceeding nMaxOutbound, with the goal of finding
|
||||
// This allows temporarily exceeding m_max_outbound_full_relay, with the goal of finding
|
||||
// a peer that is better than all our current peers.
|
||||
void SetTryNewOutboundPeer(bool flag);
|
||||
bool GetTryNewOutboundPeer();
|
||||
@ -355,7 +360,7 @@ private:
|
||||
CNode* FindNode(const CService& addr);
|
||||
|
||||
bool AttemptToEvictConnection();
|
||||
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection);
|
||||
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection, bool block_relay_only);
|
||||
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const;
|
||||
|
||||
void DeleteNode(CNode* pnode);
|
||||
@ -414,9 +419,17 @@ private:
|
||||
std::unique_ptr<CSemaphore> semOutbound;
|
||||
std::unique_ptr<CSemaphore> semAddnode;
|
||||
int nMaxConnections;
|
||||
int nMaxOutbound;
|
||||
|
||||
// How many full-relay (tx, block, addr) outbound peers we want
|
||||
int m_max_outbound_full_relay;
|
||||
|
||||
// How many block-relay only outbound peers we want
|
||||
// We do not relay tx or addr messages with these peers
|
||||
int m_max_outbound_block_relay;
|
||||
|
||||
int nMaxAddnode;
|
||||
int nMaxFeeler;
|
||||
int m_max_outbound;
|
||||
bool m_use_addrman_outgoing;
|
||||
std::atomic<int> nBestHeight;
|
||||
CClientUIInterface* clientInterface;
|
||||
@ -442,7 +455,7 @@ private:
|
||||
std::thread threadMessageHandler;
|
||||
|
||||
/** flag for deciding to connect to an extra outbound peer,
|
||||
* in excess of nMaxOutbound
|
||||
* in excess of m_max_outbound_full_relay
|
||||
* This takes the place of a feeler connection */
|
||||
std::atomic_bool m_try_another_outbound_peer;
|
||||
|
||||
@ -681,15 +694,8 @@ public:
|
||||
// Setting fDisconnect to true will cause the node to be disconnected the
|
||||
// next time DisconnectNodes() runs
|
||||
std::atomic_bool fDisconnect{false};
|
||||
// We use fRelayTxes for two purposes -
|
||||
// a) it allows us to not relay tx invs before receiving the peer's version message
|
||||
// b) the peer may tell us in its version message that we should not relay tx invs
|
||||
// unless it loads a bloom filter.
|
||||
bool fRelayTxes GUARDED_BY(cs_filter){false};
|
||||
bool fSentAddr{false};
|
||||
CSemaphoreGrant grantOutbound;
|
||||
mutable CCriticalSection cs_filter;
|
||||
std::unique_ptr<CBloomFilter> pfilter PT_GUARDED_BY(cs_filter);
|
||||
std::atomic<int> nRefCount{0};
|
||||
|
||||
const uint64_t nKeyedNetGroup;
|
||||
@ -708,28 +714,51 @@ public:
|
||||
std::vector<CAddress> vAddrToSend;
|
||||
CRollingBloomFilter addrKnown;
|
||||
bool fGetAddr{false};
|
||||
std::set<uint256> setKnown;
|
||||
int64_t nNextAddrSend GUARDED_BY(cs_sendProcessing){0};
|
||||
int64_t nNextLocalAddrSend GUARDED_BY(cs_sendProcessing){0};
|
||||
|
||||
// inventory based relay
|
||||
CRollingBloomFilter filterInventoryKnown GUARDED_BY(cs_inventory);
|
||||
// Set of transaction ids we still have to announce.
|
||||
// They are sorted by the mempool before relay, so the order is not important.
|
||||
std::set<uint256> setInventoryTxToSend;
|
||||
const bool m_addr_relay_peer;
|
||||
bool IsAddrRelayPeer() const { return m_addr_relay_peer; }
|
||||
|
||||
// List of block ids we still have announce.
|
||||
// There is no final sorting before sending, as they are always sent immediately
|
||||
// and in the order requested.
|
||||
std::vector<uint256> vInventoryBlockToSend GUARDED_BY(cs_inventory);
|
||||
CCriticalSection cs_inventory;
|
||||
int64_t nNextInvSend{0};
|
||||
// Used for headers announcements - unfiltered blocks to relay
|
||||
std::vector<uint256> vBlockHashesToAnnounce GUARDED_BY(cs_inventory);
|
||||
// Used for BIP35 mempool sending
|
||||
bool fSendMempool GUARDED_BY(cs_inventory){false};
|
||||
|
||||
struct TxRelay {
|
||||
TxRelay() { pfilter = MakeUnique<CBloomFilter>(); }
|
||||
mutable CCriticalSection cs_filter;
|
||||
// We use fRelayTxes for two purposes -
|
||||
// a) it allows us to not relay tx invs before receiving the peer's version message
|
||||
// b) the peer may tell us in its version message that we should not relay tx invs
|
||||
// unless it loads a bloom filter.
|
||||
bool fRelayTxes GUARDED_BY(cs_filter){false};
|
||||
std::unique_ptr<CBloomFilter> pfilter PT_GUARDED_BY(cs_filter) GUARDED_BY(cs_filter);
|
||||
|
||||
mutable CCriticalSection cs_tx_inventory;
|
||||
CRollingBloomFilter filterInventoryKnown GUARDED_BY(cs_tx_inventory){50000, 0.000001};
|
||||
// Set of transaction ids we still have to announce.
|
||||
// They are sorted by the mempool before relay, so the order is not important.
|
||||
std::set<uint256> setInventoryTxToSend;
|
||||
// Used for BIP35 mempool sending
|
||||
bool fSendMempool GUARDED_BY(cs_tx_inventory){false};
|
||||
// Last time a "MEMPOOL" request was serviced.
|
||||
std::atomic<int64_t> timeLastMempoolReq{0};
|
||||
int64_t nNextInvSend{0};
|
||||
|
||||
CCriticalSection cs_feeFilter;
|
||||
// Minimum fee rate with which to filter inv's to this node
|
||||
CAmount minFeeFilter GUARDED_BY(cs_feeFilter){0};
|
||||
CAmount lastSentFeeFilter{0};
|
||||
int64_t nextSendTimeFeeFilter{0};
|
||||
};
|
||||
|
||||
// m_tx_relay == nullptr if we're not relaying transactions with this peer
|
||||
std::unique_ptr<TxRelay> m_tx_relay;
|
||||
|
||||
// Used for headers announcements - unfiltered blocks to relay
|
||||
std::vector<uint256> vBlockHashesToAnnounce GUARDED_BY(cs_inventory);
|
||||
|
||||
// Block and TXN accept times
|
||||
std::atomic<int64_t> nLastBlockTime{0};
|
||||
@ -746,15 +775,10 @@ public:
|
||||
std::atomic<int64_t> nMinPingUsecTime{std::numeric_limits<int64_t>::max()};
|
||||
// Whether a ping is requested.
|
||||
std::atomic<bool> fPingQueued{false};
|
||||
// Minimum fee rate with which to filter inv's to this node
|
||||
CAmount minFeeFilter GUARDED_BY(cs_feeFilter){0};
|
||||
CCriticalSection cs_feeFilter;
|
||||
CAmount lastSentFeeFilter{0};
|
||||
int64_t nextSendTimeFeeFilter{0};
|
||||
|
||||
std::set<uint256> orphan_work_set;
|
||||
|
||||
CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn = "", bool fInboundIn = false);
|
||||
CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn = "", bool fInboundIn = false, bool block_relay_only = false);
|
||||
~CNode();
|
||||
CNode(const CNode&) = delete;
|
||||
CNode& operator=(const CNode&) = delete;
|
||||
@ -847,20 +871,21 @@ public:
|
||||
|
||||
void AddInventoryKnown(const CInv& inv)
|
||||
{
|
||||
{
|
||||
LOCK(cs_inventory);
|
||||
filterInventoryKnown.insert(inv.hash);
|
||||
if (m_tx_relay != nullptr) {
|
||||
LOCK(m_tx_relay->cs_tx_inventory);
|
||||
m_tx_relay->filterInventoryKnown.insert(inv.hash);
|
||||
}
|
||||
}
|
||||
|
||||
void PushInventory(const CInv& inv)
|
||||
{
|
||||
LOCK(cs_inventory);
|
||||
if (inv.type == MSG_TX) {
|
||||
if (!filterInventoryKnown.contains(inv.hash)) {
|
||||
setInventoryTxToSend.insert(inv.hash);
|
||||
if (inv.type == MSG_TX && m_tx_relay != nullptr) {
|
||||
LOCK(m_tx_relay->cs_tx_inventory);
|
||||
if (!m_tx_relay->filterInventoryKnown.contains(inv.hash)) {
|
||||
m_tx_relay->setInventoryTxToSend.insert(inv.hash);
|
||||
}
|
||||
} else if (inv.type == MSG_BLOCK) {
|
||||
LOCK(cs_inventory);
|
||||
vInventoryBlockToSend.push_back(inv.hash);
|
||||
}
|
||||
}
|
||||
|
@ -262,7 +262,7 @@ struct CNodeState {
|
||||
bool fSupportsDesiredCmpctVersion;
|
||||
|
||||
/** State used to enforce CHAIN_SYNC_TIMEOUT
|
||||
* Only in effect for outbound, non-manual connections, with
|
||||
* Only in effect for outbound, non-manual, full-relay connections, with
|
||||
* m_protect == false
|
||||
* Algorithm: if a peer's best known block has less work than our tip,
|
||||
* set a timeout CHAIN_SYNC_TIMEOUT seconds in the future:
|
||||
@ -425,7 +425,7 @@ static void PushNodeVersion(CNode *pnode, CConnman* connman, int64_t nTime)
|
||||
CAddress addrMe = CAddress(CService(), nLocalNodeServices);
|
||||
|
||||
connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe,
|
||||
nonce, strSubVersion, nNodeStartingHeight, ::g_relay_txes));
|
||||
nonce, strSubVersion, nNodeStartingHeight, ::g_relay_txes && pnode->m_tx_relay != nullptr));
|
||||
|
||||
if (fLogIPs) {
|
||||
LogPrint(BCLog::NET, "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nNodeStartingHeight, addrMe.ToString(), addrYou.ToString(), nodeid);
|
||||
@ -757,7 +757,7 @@ void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds)
|
||||
}
|
||||
|
||||
// Returns true for outbound peers, excluding manual connections, feelers, and
|
||||
// one-shots
|
||||
// one-shots.
|
||||
static bool IsOutboundDisconnectionCandidate(const CNode *node)
|
||||
{
|
||||
return !(node->fInbound || node->m_manual_connection || node->fFeeler || node->fOneShot);
|
||||
@ -1330,7 +1330,7 @@ static void RelayAddress(const CAddress& addr, bool fReachable, CConnman* connma
|
||||
assert(nRelayNodes <= best.size());
|
||||
|
||||
auto sortfunc = [&best, &hasher, nRelayNodes](CNode* pnode) {
|
||||
if (pnode->nVersion >= CADDR_TIME_VERSION) {
|
||||
if (pnode->nVersion >= CADDR_TIME_VERSION && pnode->IsAddrRelayPeer()) {
|
||||
uint64_t hashKey = CSipHasher(hasher).Write(pnode->GetId()).Finalize();
|
||||
for (unsigned int i = 0; i < nRelayNodes; i++) {
|
||||
if (hashKey > best[i].first) {
|
||||
@ -1449,11 +1449,11 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
|
||||
{
|
||||
bool sendMerkleBlock = false;
|
||||
CMerkleBlock merkleBlock;
|
||||
{
|
||||
LOCK(pfrom->cs_filter);
|
||||
if (pfrom->pfilter) {
|
||||
if (pfrom->m_tx_relay != nullptr) {
|
||||
LOCK(pfrom->m_tx_relay->cs_filter);
|
||||
if (pfrom->m_tx_relay->pfilter) {
|
||||
sendMerkleBlock = true;
|
||||
merkleBlock = CMerkleBlock(*pblock, *pfrom->pfilter);
|
||||
merkleBlock = CMerkleBlock(*pblock, *pfrom->m_tx_relay->pfilter);
|
||||
}
|
||||
}
|
||||
if (sendMerkleBlock) {
|
||||
@ -1513,7 +1513,12 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
|
||||
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
|
||||
std::vector<CInv> vNotFound;
|
||||
const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
|
||||
{
|
||||
|
||||
// Note that if we receive a getdata for a MSG_TX or MSG_WITNESS_TX from a
|
||||
// block-relay-only outbound peer, we will stop processing further getdata
|
||||
// messages from this peer (likely resulting in our peer eventually
|
||||
// disconnecting us).
|
||||
if (pfrom->m_tx_relay != nullptr) {
|
||||
LOCK(cs_main);
|
||||
|
||||
while (it != pfrom->vRecvGetData.end() && (it->type == MSG_TX || it->type == MSG_WITNESS_TX)) {
|
||||
@ -1533,11 +1538,11 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
|
||||
if (mi != mapRelay.end()) {
|
||||
connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second));
|
||||
push = true;
|
||||
} else if (pfrom->timeLastMempoolReq) {
|
||||
} else if (pfrom->m_tx_relay->timeLastMempoolReq) {
|
||||
auto txinfo = mempool.info(inv.hash);
|
||||
// To protect privacy, do not answer getdata using the mempool when
|
||||
// that TX couldn't have been INVed in reply to a MEMPOOL request.
|
||||
if (txinfo.tx && txinfo.nTime <= pfrom->timeLastMempoolReq) {
|
||||
if (txinfo.tx && txinfo.nTime <= pfrom->m_tx_relay->timeLastMempoolReq) {
|
||||
connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *txinfo.tx));
|
||||
push = true;
|
||||
}
|
||||
@ -1773,9 +1778,11 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
|
||||
}
|
||||
}
|
||||
|
||||
if (!pfrom->fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr) {
|
||||
// If this is an outbound peer, check to see if we should protect
|
||||
if (!pfrom->fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr && pfrom->m_tx_relay != nullptr) {
|
||||
// If this is an outbound full-relay peer, check to see if we should protect
|
||||
// it from the bad/lagging chain logic.
|
||||
// Note that block-relay-only peers are already implicitly protected, so we
|
||||
// only consider setting m_protect for the full-relay peers.
|
||||
if (g_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= ::ChainActive().Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) {
|
||||
LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom->GetId());
|
||||
nodestate->m_chain_sync.m_protect = true;
|
||||
@ -1996,9 +2003,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||
// set nodes not capable of serving the complete blockchain history as "limited nodes"
|
||||
pfrom->m_limited_node = (!(nServices & NODE_NETWORK) && (nServices & NODE_NETWORK_LIMITED));
|
||||
|
||||
{
|
||||
LOCK(pfrom->cs_filter);
|
||||
pfrom->fRelayTxes = fRelay; // set to true after we get the first filter* message
|
||||
if (pfrom->m_tx_relay != nullptr) {
|
||||
LOCK(pfrom->m_tx_relay->cs_filter);
|
||||
pfrom->m_tx_relay->fRelayTxes = fRelay; // set to true after we get the first filter* message
|
||||
}
|
||||
|
||||
// Change version
|
||||
@ -2017,7 +2024,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||
UpdatePreferredDownload(pfrom, State(pfrom->GetId()));
|
||||
}
|
||||
|
||||
if (!pfrom->fInbound)
|
||||
if (!pfrom->fInbound && pfrom->IsAddrRelayPeer())
|
||||
{
|
||||
// Advertise our address
|
||||
if (fListen && !::ChainstateActive().IsInitialBlockDownload())
|
||||
@ -2089,9 +2096,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||
// Mark this node as currently connected, so we update its timestamp later.
|
||||
LOCK(cs_main);
|
||||
State(pfrom->GetId())->fCurrentlyConnected = true;
|
||||
LogPrintf("New outbound peer connected: version: %d, blocks=%d, peer=%d%s\n",
|
||||
pfrom->nVersion.load(), pfrom->nStartingHeight, pfrom->GetId(),
|
||||
(fLogIPs ? strprintf(", peeraddr=%s", pfrom->addr.ToString()) : ""));
|
||||
LogPrintf("New outbound peer connected: version: %d, blocks=%d, peer=%d%s (%s)\n",
|
||||
pfrom->nVersion.load(), pfrom->nStartingHeight,
|
||||
pfrom->GetId(), (fLogIPs ? strprintf(", peeraddr=%s", pfrom->addr.ToString()) : ""),
|
||||
pfrom->m_tx_relay == nullptr ? "block-relay" : "full-relay");
|
||||
}
|
||||
|
||||
if (pfrom->nVersion >= SENDHEADERS_VERSION) {
|
||||
@ -2132,6 +2140,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||
// Don't want addr from older versions unless seeding
|
||||
if (pfrom->nVersion < CADDR_TIME_VERSION && connman->GetAddressCount() > 1000)
|
||||
return true;
|
||||
if (!pfrom->IsAddrRelayPeer()) {
|
||||
return true;
|
||||
}
|
||||
if (vAddr.size() > 1000)
|
||||
{
|
||||
LOCK(cs_main);
|
||||
@ -2215,7 +2226,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fBlocksOnly = !g_relay_txes;
|
||||
// We won't accept tx inv's if we're in blocks-only mode, or this is a
|
||||
// block-relay-only peer
|
||||
bool fBlocksOnly = !g_relay_txes || (pfrom->m_tx_relay == nullptr);
|
||||
|
||||
// Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true
|
||||
if (pfrom->HasPermission(PF_RELAY))
|
||||
@ -2254,7 +2267,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||
{
|
||||
pfrom->AddInventoryKnown(inv);
|
||||
if (fBlocksOnly) {
|
||||
LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->GetId());
|
||||
LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.hash.ToString(), pfrom->GetId());
|
||||
pfrom->fDisconnect = true;
|
||||
return true;
|
||||
} else if (!fAlreadyHave && !fImporting && !fReindex && !::ChainstateActive().IsInitialBlockDownload()) {
|
||||
RequestTx(State(pfrom->GetId()), inv.hash, current_time);
|
||||
}
|
||||
@ -2471,9 +2486,11 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||
if (strCommand == NetMsgType::TX) {
|
||||
// Stop processing the transaction early if
|
||||
// We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off
|
||||
if (!g_relay_txes && !pfrom->HasPermission(PF_RELAY))
|
||||
// or if this peer is supposed to be a block-relay-only peer
|
||||
if ((!g_relay_txes && !pfrom->HasPermission(PF_RELAY)) || (pfrom->m_tx_relay == nullptr))
|
||||
{
|
||||
LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom->GetId());
|
||||
pfrom->fDisconnect = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2990,6 +3007,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||
LogPrint(BCLog::NET, "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom->GetId());
|
||||
return true;
|
||||
}
|
||||
if (!pfrom->IsAddrRelayPeer()) {
|
||||
LogPrint(BCLog::NET, "Ignoring \"getaddr\" from block-relay-only connection. peer=%d\n", pfrom->GetId());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Only send one GetAddr response per connection to reduce resource waste
|
||||
// and discourage addr stamping of INV announcements.
|
||||
@ -3031,8 +3052,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||
return true;
|
||||
}
|
||||
|
||||
LOCK(pfrom->cs_inventory);
|
||||
pfrom->fSendMempool = true;
|
||||
if (pfrom->m_tx_relay != nullptr) {
|
||||
LOCK(pfrom->m_tx_relay->cs_tx_inventory);
|
||||
pfrom->m_tx_relay->fSendMempool = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3123,12 +3146,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||
LOCK(cs_main);
|
||||
Misbehaving(pfrom->GetId(), 100);
|
||||
}
|
||||
else
|
||||
else if (pfrom->m_tx_relay != nullptr)
|
||||
{
|
||||
LOCK(pfrom->cs_filter);
|
||||
pfrom->pfilter.reset(new CBloomFilter(filter));
|
||||
pfrom->pfilter->UpdateEmptyFull();
|
||||
pfrom->fRelayTxes = true;
|
||||
LOCK(pfrom->m_tx_relay->cs_filter);
|
||||
pfrom->m_tx_relay->pfilter.reset(new CBloomFilter(filter));
|
||||
pfrom->m_tx_relay->pfilter->UpdateEmptyFull();
|
||||
pfrom->m_tx_relay->fRelayTxes = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -3142,10 +3165,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||
bool bad = false;
|
||||
if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) {
|
||||
bad = true;
|
||||
} else {
|
||||
LOCK(pfrom->cs_filter);
|
||||
if (pfrom->pfilter) {
|
||||
pfrom->pfilter->insert(vData);
|
||||
} else if (pfrom->m_tx_relay != nullptr) {
|
||||
LOCK(pfrom->m_tx_relay->cs_filter);
|
||||
if (pfrom->m_tx_relay->pfilter) {
|
||||
pfrom->m_tx_relay->pfilter->insert(vData);
|
||||
} else {
|
||||
bad = true;
|
||||
}
|
||||
@ -3158,11 +3181,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||
}
|
||||
|
||||
if (strCommand == NetMsgType::FILTERCLEAR) {
|
||||
LOCK(pfrom->cs_filter);
|
||||
if (pfrom->GetLocalServices() & NODE_BLOOM) {
|
||||
pfrom->pfilter.reset(new CBloomFilter());
|
||||
if (pfrom->m_tx_relay == nullptr) {
|
||||
return true;
|
||||
}
|
||||
pfrom->fRelayTxes = true;
|
||||
LOCK(pfrom->m_tx_relay->cs_filter);
|
||||
if (pfrom->GetLocalServices() & NODE_BLOOM) {
|
||||
pfrom->m_tx_relay->pfilter.reset(new CBloomFilter());
|
||||
}
|
||||
pfrom->m_tx_relay->fRelayTxes = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3170,9 +3196,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||
CAmount newFeeFilter = 0;
|
||||
vRecv >> newFeeFilter;
|
||||
if (MoneyRange(newFeeFilter)) {
|
||||
{
|
||||
LOCK(pfrom->cs_feeFilter);
|
||||
pfrom->minFeeFilter = newFeeFilter;
|
||||
if (pfrom->m_tx_relay != nullptr) {
|
||||
LOCK(pfrom->m_tx_relay->cs_feeFilter);
|
||||
pfrom->m_tx_relay->minFeeFilter = newFeeFilter;
|
||||
}
|
||||
LogPrint(BCLog::NET, "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->GetId());
|
||||
}
|
||||
@ -3449,6 +3475,8 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
|
||||
if (state == nullptr) return; // shouldn't be possible, but just in case
|
||||
// Don't evict our protected peers
|
||||
if (state->m_chain_sync.m_protect) return;
|
||||
// Don't evict our block-relay-only peers.
|
||||
if (pnode->m_tx_relay == nullptr) return;
|
||||
if (state->m_last_block_announcement < oldest_block_announcement || (state->m_last_block_announcement == oldest_block_announcement && pnode->GetId() > worst_peer)) {
|
||||
worst_peer = pnode->GetId();
|
||||
oldest_block_announcement = state->m_last_block_announcement;
|
||||
@ -3576,7 +3604,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
|
||||
|
||||
// Address refresh broadcast
|
||||
int64_t nNow = GetTimeMicros();
|
||||
if (!::ChainstateActive().IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
|
||||
if (pto->IsAddrRelayPeer() && !::ChainstateActive().IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
|
||||
AdvertiseLocal(pto);
|
||||
pto->nNextLocalAddrSend = PoissonNextSend(nNow, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
|
||||
}
|
||||
@ -3584,7 +3612,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
|
||||
//
|
||||
// Message: addr
|
||||
//
|
||||
if (pto->nNextAddrSend < nNow) {
|
||||
if (pto->IsAddrRelayPeer() && pto->nNextAddrSend < nNow) {
|
||||
pto->nNextAddrSend = PoissonNextSend(nNow, AVG_ADDRESS_BROADCAST_INTERVAL);
|
||||
std::vector<CAddress> vAddr;
|
||||
vAddr.reserve(pto->vAddrToSend.size());
|
||||
@ -3792,69 +3820,71 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
|
||||
}
|
||||
pto->vInventoryBlockToSend.clear();
|
||||
|
||||
if (pto->m_tx_relay != nullptr) {
|
||||
LOCK(pto->m_tx_relay->cs_tx_inventory);
|
||||
// Check whether periodic sends should happen
|
||||
bool fSendTrickle = pto->HasPermission(PF_NOBAN);
|
||||
if (pto->nNextInvSend < nNow) {
|
||||
if (pto->m_tx_relay->nNextInvSend < nNow) {
|
||||
fSendTrickle = true;
|
||||
if (pto->fInbound) {
|
||||
pto->nNextInvSend = connman->PoissonNextSendInbound(nNow, INVENTORY_BROADCAST_INTERVAL);
|
||||
pto->m_tx_relay->nNextInvSend = connman->PoissonNextSendInbound(nNow, INVENTORY_BROADCAST_INTERVAL);
|
||||
} else {
|
||||
// Use half the delay for outbound peers, as there is less privacy concern for them.
|
||||
pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> 1);
|
||||
pto->m_tx_relay->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Time to send but the peer has requested we not relay transactions.
|
||||
if (fSendTrickle) {
|
||||
LOCK(pto->cs_filter);
|
||||
if (!pto->fRelayTxes) pto->setInventoryTxToSend.clear();
|
||||
LOCK(pto->m_tx_relay->cs_filter);
|
||||
if (!pto->m_tx_relay->fRelayTxes) pto->m_tx_relay->setInventoryTxToSend.clear();
|
||||
}
|
||||
|
||||
// Respond to BIP35 mempool requests
|
||||
if (fSendTrickle && pto->fSendMempool) {
|
||||
if (fSendTrickle && pto->m_tx_relay->fSendMempool) {
|
||||
auto vtxinfo = mempool.infoAll();
|
||||
pto->fSendMempool = false;
|
||||
pto->m_tx_relay->fSendMempool = false;
|
||||
CAmount filterrate = 0;
|
||||
{
|
||||
LOCK(pto->cs_feeFilter);
|
||||
filterrate = pto->minFeeFilter;
|
||||
LOCK(pto->m_tx_relay->cs_feeFilter);
|
||||
filterrate = pto->m_tx_relay->minFeeFilter;
|
||||
}
|
||||
|
||||
LOCK(pto->cs_filter);
|
||||
LOCK(pto->m_tx_relay->cs_filter);
|
||||
|
||||
for (const auto& txinfo : vtxinfo) {
|
||||
const uint256& hash = txinfo.tx->GetHash();
|
||||
CInv inv(MSG_TX, hash);
|
||||
pto->setInventoryTxToSend.erase(hash);
|
||||
pto->m_tx_relay->setInventoryTxToSend.erase(hash);
|
||||
if (filterrate) {
|
||||
if (txinfo.feeRate.GetFeePerK() < filterrate)
|
||||
continue;
|
||||
}
|
||||
if (pto->pfilter) {
|
||||
if (!pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
|
||||
if (pto->m_tx_relay->pfilter) {
|
||||
if (!pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
|
||||
}
|
||||
pto->filterInventoryKnown.insert(hash);
|
||||
pto->m_tx_relay->filterInventoryKnown.insert(hash);
|
||||
vInv.push_back(inv);
|
||||
if (vInv.size() == MAX_INV_SZ) {
|
||||
connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
|
||||
vInv.clear();
|
||||
}
|
||||
}
|
||||
pto->timeLastMempoolReq = GetTime();
|
||||
pto->m_tx_relay->timeLastMempoolReq = GetTime();
|
||||
}
|
||||
|
||||
// Determine transactions to relay
|
||||
if (fSendTrickle) {
|
||||
// Produce a vector with all candidates for sending
|
||||
std::vector<std::set<uint256>::iterator> vInvTx;
|
||||
vInvTx.reserve(pto->setInventoryTxToSend.size());
|
||||
for (std::set<uint256>::iterator it = pto->setInventoryTxToSend.begin(); it != pto->setInventoryTxToSend.end(); it++) {
|
||||
vInvTx.reserve(pto->m_tx_relay->setInventoryTxToSend.size());
|
||||
for (std::set<uint256>::iterator it = pto->m_tx_relay->setInventoryTxToSend.begin(); it != pto->m_tx_relay->setInventoryTxToSend.end(); it++) {
|
||||
vInvTx.push_back(it);
|
||||
}
|
||||
CAmount filterrate = 0;
|
||||
{
|
||||
LOCK(pto->cs_feeFilter);
|
||||
filterrate = pto->minFeeFilter;
|
||||
LOCK(pto->m_tx_relay->cs_feeFilter);
|
||||
filterrate = pto->m_tx_relay->minFeeFilter;
|
||||
}
|
||||
// Topologically and fee-rate sort the inventory we send for privacy and priority reasons.
|
||||
// A heap is used so that not all items need sorting if only a few are being sent.
|
||||
@ -3863,7 +3893,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
|
||||
// No reason to drain out at many times the network's capacity,
|
||||
// especially since we have many peers and some will draw much shorter delays.
|
||||
unsigned int nRelayedTransactions = 0;
|
||||
LOCK(pto->cs_filter);
|
||||
LOCK(pto->m_tx_relay->cs_filter);
|
||||
while (!vInvTx.empty() && nRelayedTransactions < INVENTORY_BROADCAST_MAX) {
|
||||
// Fetch the top element from the heap
|
||||
std::pop_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder);
|
||||
@ -3871,9 +3901,9 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
|
||||
vInvTx.pop_back();
|
||||
uint256 hash = *it;
|
||||
// Remove it from the to-be-sent set
|
||||
pto->setInventoryTxToSend.erase(it);
|
||||
pto->m_tx_relay->setInventoryTxToSend.erase(it);
|
||||
// Check if not in the filter already
|
||||
if (pto->filterInventoryKnown.contains(hash)) {
|
||||
if (pto->m_tx_relay->filterInventoryKnown.contains(hash)) {
|
||||
continue;
|
||||
}
|
||||
// Not in the mempool anymore? don't bother sending it.
|
||||
@ -3884,7 +3914,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
|
||||
if (filterrate && txinfo.feeRate.GetFeePerK() < filterrate) {
|
||||
continue;
|
||||
}
|
||||
if (pto->pfilter && !pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
|
||||
if (pto->m_tx_relay->pfilter && !pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
|
||||
// Send
|
||||
vInv.push_back(CInv(MSG_TX, hash));
|
||||
nRelayedTransactions++;
|
||||
@ -3905,7 +3935,8 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
|
||||
connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
|
||||
vInv.clear();
|
||||
}
|
||||
pto->filterInventoryKnown.insert(hash);
|
||||
pto->m_tx_relay->filterInventoryKnown.insert(hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4066,27 +4097,27 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
|
||||
// Message: feefilter
|
||||
//
|
||||
// We don't want white listed peers to filter txs to us if we have -whitelistforcerelay
|
||||
if (pto->nVersion >= FEEFILTER_VERSION && gArgs.GetBoolArg("-feefilter", DEFAULT_FEEFILTER) &&
|
||||
if (pto->m_tx_relay != nullptr && pto->nVersion >= FEEFILTER_VERSION && gArgs.GetBoolArg("-feefilter", DEFAULT_FEEFILTER) &&
|
||||
!pto->HasPermission(PF_FORCERELAY)) {
|
||||
CAmount currentFilter = mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
|
||||
int64_t timeNow = GetTimeMicros();
|
||||
if (timeNow > pto->nextSendTimeFeeFilter) {
|
||||
if (timeNow > pto->m_tx_relay->nextSendTimeFeeFilter) {
|
||||
static CFeeRate default_feerate(DEFAULT_MIN_RELAY_TX_FEE);
|
||||
static FeeFilterRounder filterRounder(default_feerate);
|
||||
CAmount filterToSend = filterRounder.round(currentFilter);
|
||||
// We always have a fee filter of at least minRelayTxFee
|
||||
filterToSend = std::max(filterToSend, ::minRelayTxFee.GetFeePerK());
|
||||
if (filterToSend != pto->lastSentFeeFilter) {
|
||||
if (filterToSend != pto->m_tx_relay->lastSentFeeFilter) {
|
||||
connman->PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend));
|
||||
pto->lastSentFeeFilter = filterToSend;
|
||||
pto->m_tx_relay->lastSentFeeFilter = filterToSend;
|
||||
}
|
||||
pto->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL);
|
||||
pto->m_tx_relay->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL);
|
||||
}
|
||||
// If the fee filter has changed substantially and it's still more than MAX_FEEFILTER_CHANGE_DELAY
|
||||
// until scheduled broadcast, then move the broadcast to within MAX_FEEFILTER_CHANGE_DELAY.
|
||||
else if (timeNow + MAX_FEEFILTER_CHANGE_DELAY * 1000000 < pto->nextSendTimeFeeFilter &&
|
||||
(currentFilter < 3 * pto->lastSentFeeFilter / 4 || currentFilter > 4 * pto->lastSentFeeFilter / 3)) {
|
||||
pto->nextSendTimeFeeFilter = timeNow + GetRandInt(MAX_FEEFILTER_CHANGE_DELAY) * 1000000;
|
||||
else if (timeNow + MAX_FEEFILTER_CHANGE_DELAY * 1000000 < pto->m_tx_relay->nextSendTimeFeeFilter &&
|
||||
(currentFilter < 3 * pto->m_tx_relay->lastSentFeeFilter / 4 || currentFilter > 4 * pto->m_tx_relay->lastSentFeeFilter / 3)) {
|
||||
pto->m_tx_relay->nextSendTimeFeeFilter = timeNow + GetRandInt(MAX_FEEFILTER_CHANGE_DELAY) * 1000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -151,17 +151,17 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
|
||||
auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, scheduler, false);
|
||||
|
||||
const Consensus::Params& consensusParams = Params().GetConsensus();
|
||||
constexpr int nMaxOutbound = 8;
|
||||
constexpr int max_outbound_full_relay = 8;
|
||||
CConnman::Options options;
|
||||
options.nMaxConnections = 125;
|
||||
options.nMaxOutbound = nMaxOutbound;
|
||||
options.m_max_outbound_full_relay = max_outbound_full_relay;
|
||||
options.nMaxFeeler = 1;
|
||||
|
||||
connman->Init(options);
|
||||
std::vector<CNode *> vNodes;
|
||||
|
||||
// Mock some outbound peers
|
||||
for (int i=0; i<nMaxOutbound; ++i) {
|
||||
for (int i=0; i<max_outbound_full_relay; ++i) {
|
||||
AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
|
||||
}
|
||||
|
||||
@ -190,7 +190,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
|
||||
AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
|
||||
|
||||
peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
|
||||
for (int i=0; i<nMaxOutbound; ++i) {
|
||||
for (int i=0; i<max_outbound_full_relay; ++i) {
|
||||
BOOST_CHECK(vNodes[i]->fDisconnect == false);
|
||||
}
|
||||
// Last added node should get marked for eviction
|
||||
@ -203,10 +203,10 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
|
||||
UpdateLastBlockAnnounceTime(vNodes.back()->GetId(), GetTime());
|
||||
|
||||
peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
|
||||
for (int i=0; i<nMaxOutbound-1; ++i) {
|
||||
for (int i=0; i<max_outbound_full_relay-1; ++i) {
|
||||
BOOST_CHECK(vNodes[i]->fDisconnect == false);
|
||||
}
|
||||
BOOST_CHECK(vNodes[nMaxOutbound-1]->fDisconnect == true);
|
||||
BOOST_CHECK(vNodes[max_outbound_full_relay-1]->fDisconnect == true);
|
||||
BOOST_CHECK(vNodes.back()->fDisconnect == false);
|
||||
|
||||
bool dummy;
|
||||
|
@ -19,7 +19,7 @@ class P2PBlocksOnly(BitcoinTestFramework):
|
||||
def run_test(self):
|
||||
self.nodes[0].add_p2p_connection(P2PInterface())
|
||||
|
||||
self.log.info('Check that txs from p2p are rejected')
|
||||
self.log.info('Check that txs from p2p are rejected and result in disconnect')
|
||||
prevtx = self.nodes[0].getblock(self.nodes[0].getblockhash(1), 2)['tx'][0]
|
||||
rawtx = self.nodes[0].createrawtransaction(
|
||||
inputs=[{
|
||||
@ -42,13 +42,17 @@ class P2PBlocksOnly(BitcoinTestFramework):
|
||||
assert_equal(self.nodes[0].getnetworkinfo()['localrelay'], False)
|
||||
with self.nodes[0].assert_debug_log(['transaction sent in violation of protocol peer=0']):
|
||||
self.nodes[0].p2p.send_message(msg_tx(FromHex(CTransaction(), sigtx)))
|
||||
self.nodes[0].p2p.sync_with_ping()
|
||||
self.nodes[0].p2p.wait_for_disconnect()
|
||||
assert_equal(self.nodes[0].getmempoolinfo()['size'], 0)
|
||||
|
||||
# Remove the disconnected peer and add a new one.
|
||||
del self.nodes[0].p2ps[0]
|
||||
self.nodes[0].add_p2p_connection(P2PInterface())
|
||||
|
||||
self.log.info('Check that txs from rpc are not rejected and relayed to other peers')
|
||||
assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], True)
|
||||
txid = self.nodes[0].testmempoolaccept([sigtx])[0]['txid']
|
||||
with self.nodes[0].assert_debug_log(['received getdata for: tx {} peer=0'.format(txid)]):
|
||||
with self.nodes[0].assert_debug_log(['received getdata for: tx {} peer=1'.format(txid)]):
|
||||
self.nodes[0].sendrawtransaction(sigtx)
|
||||
self.nodes[0].p2p.wait_for_tx(txid)
|
||||
assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
|
||||
|
Loading…
Reference in New Issue
Block a user