mirror of
https://github.com/Retropex/bitcoin.git
synced 2025-05-16 21:20:43 +02: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;
|
CConnman::Options connOptions;
|
||||||
connOptions.nLocalServices = nLocalServices;
|
connOptions.nLocalServices = nLocalServices;
|
||||||
connOptions.nMaxConnections = nMaxConnections;
|
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.nMaxAddnode = MAX_ADDNODE_CONNECTIONS;
|
||||||
connOptions.nMaxFeeler = 1;
|
connOptions.nMaxFeeler = 1;
|
||||||
connOptions.nBestHeight = chain_active_height;
|
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;
|
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 (pszDest == nullptr) {
|
||||||
if (IsLocal(addrConnect))
|
if (IsLocal(addrConnect))
|
||||||
@ -442,7 +442,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|||||||
NodeId id = GetNewNodeId();
|
NodeId id = GetNewNodeId();
|
||||||
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
|
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
|
||||||
CAddress addr_bind = GetBindAddress(hSocket);
|
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();
|
pnode->AddRef();
|
||||||
|
|
||||||
return pnode;
|
return pnode;
|
||||||
@ -499,9 +499,11 @@ void CNode::copyStats(CNodeStats &stats)
|
|||||||
X(nServices);
|
X(nServices);
|
||||||
X(addr);
|
X(addr);
|
||||||
X(addrBind);
|
X(addrBind);
|
||||||
{
|
if (m_tx_relay != nullptr) {
|
||||||
LOCK(cs_filter);
|
LOCK(m_tx_relay->cs_filter);
|
||||||
X(fRelayTxes);
|
stats.fRelayTxes = m_tx_relay->fRelayTxes;
|
||||||
|
} else {
|
||||||
|
stats.fRelayTxes = false;
|
||||||
}
|
}
|
||||||
X(nLastSend);
|
X(nLastSend);
|
||||||
X(nLastRecv);
|
X(nLastRecv);
|
||||||
@ -528,9 +530,11 @@ void CNode::copyStats(CNodeStats &stats)
|
|||||||
}
|
}
|
||||||
X(m_legacyWhitelisted);
|
X(m_legacyWhitelisted);
|
||||||
X(m_permissionFlags);
|
X(m_permissionFlags);
|
||||||
{
|
if (m_tx_relay != nullptr) {
|
||||||
LOCK(cs_feeFilter);
|
LOCK(m_tx_relay->cs_feeFilter);
|
||||||
X(minFeeFilter);
|
stats.minFeeFilter = m_tx_relay->minFeeFilter;
|
||||||
|
} else {
|
||||||
|
stats.minFeeFilter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// It is common for nodes with good ping times to suddenly become lagged,
|
// It is common for nodes with good ping times to suddenly become lagged,
|
||||||
@ -818,11 +822,17 @@ bool CConnman::AttemptToEvictConnection()
|
|||||||
continue;
|
continue;
|
||||||
if (node->fDisconnect)
|
if (node->fDisconnect)
|
||||||
continue;
|
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,
|
NodeEvictionCandidate candidate = {node->GetId(), node->nTimeConnected, node->nMinPingUsecTime,
|
||||||
node->nLastBlockTime, node->nLastTXTime,
|
node->nLastBlockTime, node->nLastTXTime,
|
||||||
HasAllDesirableServiceFlags(node->nServices),
|
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};
|
node->m_prefer_evict};
|
||||||
vEvictionCandidates.push_back(candidate);
|
vEvictionCandidates.push_back(candidate);
|
||||||
}
|
}
|
||||||
@ -895,7 +905,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
|
|||||||
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
|
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
|
||||||
CAddress addr;
|
CAddress addr;
|
||||||
int nInbound = 0;
|
int nInbound = 0;
|
||||||
int nMaxInbound = nMaxConnections - (nMaxOutbound + nMaxFeeler);
|
int nMaxInbound = nMaxConnections - m_max_outbound;
|
||||||
|
|
||||||
if (hSocket != INVALID_SOCKET) {
|
if (hSocket != INVALID_SOCKET) {
|
||||||
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) {
|
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)
|
void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
|
||||||
@ -1715,7 +1725,8 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
|
|||||||
CAddress addrConnect;
|
CAddress addrConnect;
|
||||||
|
|
||||||
// Only connect out to one peer per network group (/16 for IPv4).
|
// 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;
|
std::set<std::vector<unsigned char> > setConnected;
|
||||||
{
|
{
|
||||||
LOCK(cs_vNodes);
|
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
|
// 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.
|
// to prevent us from connecting to particular hosts if we used them here.
|
||||||
setConnected.insert(pnode->addr.GetGroup());
|
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;
|
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).
|
int64_t nTime = GetTimeMicros(); // The current time right now (in microseconds).
|
||||||
if (nTime > nNextFeeler) {
|
if (nTime > nNextFeeler) {
|
||||||
nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);
|
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());
|
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
|
// 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
|
// Initiate outbound network connection
|
||||||
@ -1926,7 +1948,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
|
|||||||
} else if (FindNode(std::string(pszDest)))
|
} else if (FindNode(std::string(pszDest)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, manual_connection);
|
CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, manual_connection, block_relay_only);
|
||||||
|
|
||||||
if (!pnode)
|
if (!pnode)
|
||||||
return;
|
return;
|
||||||
@ -2229,7 +2251,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
|
|||||||
|
|
||||||
if (semOutbound == nullptr) {
|
if (semOutbound == nullptr) {
|
||||||
// initialize semaphore
|
// initialize semaphore
|
||||||
semOutbound = MakeUnique<CSemaphore>(std::min((nMaxOutbound + nMaxFeeler), nMaxConnections));
|
semOutbound = MakeUnique<CSemaphore>(std::min(m_max_outbound, nMaxConnections));
|
||||||
}
|
}
|
||||||
if (semAddnode == nullptr) {
|
if (semAddnode == nullptr) {
|
||||||
// initialize semaphore
|
// initialize semaphore
|
||||||
@ -2307,7 +2329,7 @@ void CConnman::Interrupt()
|
|||||||
InterruptSocks5(true);
|
InterruptSocks5(true);
|
||||||
|
|
||||||
if (semOutbound) {
|
if (semOutbound) {
|
||||||
for (int i=0; i<(nMaxOutbound + nMaxFeeler); i++) {
|
for (int i=0; i<m_max_outbound; i++) {
|
||||||
semOutbound->post();
|
semOutbound->post();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2617,14 +2639,17 @@ int CConnman::GetBestHeight() const
|
|||||||
|
|
||||||
unsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; }
|
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()),
|
: nTimeConnected(GetSystemTimeInSeconds()),
|
||||||
addr(addrIn),
|
addr(addrIn),
|
||||||
addrBind(addrBindIn),
|
addrBind(addrBindIn),
|
||||||
fInbound(fInboundIn),
|
fInbound(fInboundIn),
|
||||||
nKeyedNetGroup(nKeyedNetGroupIn),
|
nKeyedNetGroup(nKeyedNetGroupIn),
|
||||||
addrKnown(5000, 0.001),
|
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),
|
id(idIn),
|
||||||
nLocalHostNonce(nLocalHostNonceIn),
|
nLocalHostNonce(nLocalHostNonceIn),
|
||||||
nLocalServices(nLocalServicesIn),
|
nLocalServices(nLocalServicesIn),
|
||||||
@ -2633,8 +2658,9 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
|
|||||||
hSocket = hSocketIn;
|
hSocket = hSocketIn;
|
||||||
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
|
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
|
||||||
hashContinue = uint256();
|
hashContinue = uint256();
|
||||||
filterInventoryKnown.reset();
|
if (!block_relay_only) {
|
||||||
pfilter = MakeUnique<CBloomFilter>();
|
m_tx_relay = MakeUnique<TxRelay>();
|
||||||
|
}
|
||||||
|
|
||||||
for (const std::string &msg : getAllNetMessageTypes())
|
for (const std::string &msg : getAllNetMessageTypes())
|
||||||
mapRecvBytesPerMsgCmd[msg] = 0;
|
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;
|
static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 4 * 1000 * 1000;
|
||||||
/** Maximum length of the user agent string in `version` message */
|
/** Maximum length of the user agent string in `version` message */
|
||||||
static const unsigned int MAX_SUBVERSION_LENGTH = 256;
|
static const unsigned int MAX_SUBVERSION_LENGTH = 256;
|
||||||
/** Maximum number of automatic outgoing nodes */
|
/** Maximum number of automatic outgoing nodes over which we'll relay everything (blocks, tx, addrs, etc) */
|
||||||
static const int MAX_OUTBOUND_CONNECTIONS = 8;
|
static const int MAX_OUTBOUND_FULL_RELAY_CONNECTIONS = 8;
|
||||||
/** Maximum number of addnode outgoing nodes */
|
/** Maximum number of addnode outgoing nodes */
|
||||||
static const int MAX_ADDNODE_CONNECTIONS = 8;
|
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 */
|
/** -listen default */
|
||||||
static const bool DEFAULT_LISTEN = true;
|
static const bool DEFAULT_LISTEN = true;
|
||||||
/** -upnp default */
|
/** -upnp default */
|
||||||
@ -131,7 +133,8 @@ public:
|
|||||||
{
|
{
|
||||||
ServiceFlags nLocalServices = NODE_NONE;
|
ServiceFlags nLocalServices = NODE_NONE;
|
||||||
int nMaxConnections = 0;
|
int nMaxConnections = 0;
|
||||||
int nMaxOutbound = 0;
|
int m_max_outbound_full_relay = 0;
|
||||||
|
int m_max_outbound_block_relay = 0;
|
||||||
int nMaxAddnode = 0;
|
int nMaxAddnode = 0;
|
||||||
int nMaxFeeler = 0;
|
int nMaxFeeler = 0;
|
||||||
int nBestHeight = 0;
|
int nBestHeight = 0;
|
||||||
@ -155,10 +158,12 @@ public:
|
|||||||
void Init(const Options& connOptions) {
|
void Init(const Options& connOptions) {
|
||||||
nLocalServices = connOptions.nLocalServices;
|
nLocalServices = connOptions.nLocalServices;
|
||||||
nMaxConnections = connOptions.nMaxConnections;
|
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;
|
m_use_addrman_outgoing = connOptions.m_use_addrman_outgoing;
|
||||||
nMaxAddnode = connOptions.nMaxAddnode;
|
nMaxAddnode = connOptions.nMaxAddnode;
|
||||||
nMaxFeeler = connOptions.nMaxFeeler;
|
nMaxFeeler = connOptions.nMaxFeeler;
|
||||||
|
m_max_outbound = m_max_outbound_full_relay + m_max_outbound_block_relay + nMaxFeeler;
|
||||||
nBestHeight = connOptions.nBestHeight;
|
nBestHeight = connOptions.nBestHeight;
|
||||||
clientInterface = connOptions.uiInterface;
|
clientInterface = connOptions.uiInterface;
|
||||||
m_banman = connOptions.m_banman;
|
m_banman = connOptions.m_banman;
|
||||||
@ -197,7 +202,7 @@ public:
|
|||||||
bool GetNetworkActive() const { return fNetworkActive; };
|
bool GetNetworkActive() const { return fNetworkActive; };
|
||||||
bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; };
|
bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; };
|
||||||
void SetNetworkActive(bool active);
|
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 CheckIncomingNonce(uint64_t nonce);
|
||||||
|
|
||||||
bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func);
|
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);
|
void AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty = 0);
|
||||||
std::vector<CAddress> GetAddresses();
|
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.
|
// a peer that is better than all our current peers.
|
||||||
void SetTryNewOutboundPeer(bool flag);
|
void SetTryNewOutboundPeer(bool flag);
|
||||||
bool GetTryNewOutboundPeer();
|
bool GetTryNewOutboundPeer();
|
||||||
@ -355,7 +360,7 @@ private:
|
|||||||
CNode* FindNode(const CService& addr);
|
CNode* FindNode(const CService& addr);
|
||||||
|
|
||||||
bool AttemptToEvictConnection();
|
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 AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const;
|
||||||
|
|
||||||
void DeleteNode(CNode* pnode);
|
void DeleteNode(CNode* pnode);
|
||||||
@ -414,9 +419,17 @@ private:
|
|||||||
std::unique_ptr<CSemaphore> semOutbound;
|
std::unique_ptr<CSemaphore> semOutbound;
|
||||||
std::unique_ptr<CSemaphore> semAddnode;
|
std::unique_ptr<CSemaphore> semAddnode;
|
||||||
int nMaxConnections;
|
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 nMaxAddnode;
|
||||||
int nMaxFeeler;
|
int nMaxFeeler;
|
||||||
|
int m_max_outbound;
|
||||||
bool m_use_addrman_outgoing;
|
bool m_use_addrman_outgoing;
|
||||||
std::atomic<int> nBestHeight;
|
std::atomic<int> nBestHeight;
|
||||||
CClientUIInterface* clientInterface;
|
CClientUIInterface* clientInterface;
|
||||||
@ -442,7 +455,7 @@ private:
|
|||||||
std::thread threadMessageHandler;
|
std::thread threadMessageHandler;
|
||||||
|
|
||||||
/** flag for deciding to connect to an extra outbound peer,
|
/** 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 */
|
* This takes the place of a feeler connection */
|
||||||
std::atomic_bool m_try_another_outbound_peer;
|
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
|
// Setting fDisconnect to true will cause the node to be disconnected the
|
||||||
// next time DisconnectNodes() runs
|
// next time DisconnectNodes() runs
|
||||||
std::atomic_bool fDisconnect{false};
|
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};
|
bool fSentAddr{false};
|
||||||
CSemaphoreGrant grantOutbound;
|
CSemaphoreGrant grantOutbound;
|
||||||
mutable CCriticalSection cs_filter;
|
|
||||||
std::unique_ptr<CBloomFilter> pfilter PT_GUARDED_BY(cs_filter);
|
|
||||||
std::atomic<int> nRefCount{0};
|
std::atomic<int> nRefCount{0};
|
||||||
|
|
||||||
const uint64_t nKeyedNetGroup;
|
const uint64_t nKeyedNetGroup;
|
||||||
@ -708,28 +714,51 @@ public:
|
|||||||
std::vector<CAddress> vAddrToSend;
|
std::vector<CAddress> vAddrToSend;
|
||||||
CRollingBloomFilter addrKnown;
|
CRollingBloomFilter addrKnown;
|
||||||
bool fGetAddr{false};
|
bool fGetAddr{false};
|
||||||
std::set<uint256> setKnown;
|
|
||||||
int64_t nNextAddrSend GUARDED_BY(cs_sendProcessing){0};
|
int64_t nNextAddrSend GUARDED_BY(cs_sendProcessing){0};
|
||||||
int64_t nNextLocalAddrSend GUARDED_BY(cs_sendProcessing){0};
|
int64_t nNextLocalAddrSend GUARDED_BY(cs_sendProcessing){0};
|
||||||
|
|
||||||
// inventory based relay
|
const bool m_addr_relay_peer;
|
||||||
CRollingBloomFilter filterInventoryKnown GUARDED_BY(cs_inventory);
|
bool IsAddrRelayPeer() const { return m_addr_relay_peer; }
|
||||||
// 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;
|
|
||||||
// List of block ids we still have announce.
|
// List of block ids we still have announce.
|
||||||
// There is no final sorting before sending, as they are always sent immediately
|
// There is no final sorting before sending, as they are always sent immediately
|
||||||
// and in the order requested.
|
// and in the order requested.
|
||||||
std::vector<uint256> vInventoryBlockToSend GUARDED_BY(cs_inventory);
|
std::vector<uint256> vInventoryBlockToSend GUARDED_BY(cs_inventory);
|
||||||
CCriticalSection 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.
|
// Last time a "MEMPOOL" request was serviced.
|
||||||
std::atomic<int64_t> timeLastMempoolReq{0};
|
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
|
// Block and TXN accept times
|
||||||
std::atomic<int64_t> nLastBlockTime{0};
|
std::atomic<int64_t> nLastBlockTime{0};
|
||||||
@ -746,15 +775,10 @@ public:
|
|||||||
std::atomic<int64_t> nMinPingUsecTime{std::numeric_limits<int64_t>::max()};
|
std::atomic<int64_t> nMinPingUsecTime{std::numeric_limits<int64_t>::max()};
|
||||||
// Whether a ping is requested.
|
// Whether a ping is requested.
|
||||||
std::atomic<bool> fPingQueued{false};
|
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;
|
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();
|
||||||
CNode(const CNode&) = delete;
|
CNode(const CNode&) = delete;
|
||||||
CNode& operator=(const CNode&) = delete;
|
CNode& operator=(const CNode&) = delete;
|
||||||
@ -847,20 +871,21 @@ public:
|
|||||||
|
|
||||||
void AddInventoryKnown(const CInv& inv)
|
void AddInventoryKnown(const CInv& inv)
|
||||||
{
|
{
|
||||||
{
|
if (m_tx_relay != nullptr) {
|
||||||
LOCK(cs_inventory);
|
LOCK(m_tx_relay->cs_tx_inventory);
|
||||||
filterInventoryKnown.insert(inv.hash);
|
m_tx_relay->filterInventoryKnown.insert(inv.hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PushInventory(const CInv& inv)
|
void PushInventory(const CInv& inv)
|
||||||
{
|
{
|
||||||
LOCK(cs_inventory);
|
if (inv.type == MSG_TX && m_tx_relay != nullptr) {
|
||||||
if (inv.type == MSG_TX) {
|
LOCK(m_tx_relay->cs_tx_inventory);
|
||||||
if (!filterInventoryKnown.contains(inv.hash)) {
|
if (!m_tx_relay->filterInventoryKnown.contains(inv.hash)) {
|
||||||
setInventoryTxToSend.insert(inv.hash);
|
m_tx_relay->setInventoryTxToSend.insert(inv.hash);
|
||||||
}
|
}
|
||||||
} else if (inv.type == MSG_BLOCK) {
|
} else if (inv.type == MSG_BLOCK) {
|
||||||
|
LOCK(cs_inventory);
|
||||||
vInventoryBlockToSend.push_back(inv.hash);
|
vInventoryBlockToSend.push_back(inv.hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,7 +262,7 @@ struct CNodeState {
|
|||||||
bool fSupportsDesiredCmpctVersion;
|
bool fSupportsDesiredCmpctVersion;
|
||||||
|
|
||||||
/** State used to enforce CHAIN_SYNC_TIMEOUT
|
/** 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
|
* m_protect == false
|
||||||
* Algorithm: if a peer's best known block has less work than our tip,
|
* Algorithm: if a peer's best known block has less work than our tip,
|
||||||
* set a timeout CHAIN_SYNC_TIMEOUT seconds in the future:
|
* 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);
|
CAddress addrMe = CAddress(CService(), nLocalNodeServices);
|
||||||
|
|
||||||
connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe,
|
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) {
|
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);
|
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
|
// Returns true for outbound peers, excluding manual connections, feelers, and
|
||||||
// one-shots
|
// one-shots.
|
||||||
static bool IsOutboundDisconnectionCandidate(const CNode *node)
|
static bool IsOutboundDisconnectionCandidate(const CNode *node)
|
||||||
{
|
{
|
||||||
return !(node->fInbound || node->m_manual_connection || node->fFeeler || node->fOneShot);
|
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());
|
assert(nRelayNodes <= best.size());
|
||||||
|
|
||||||
auto sortfunc = [&best, &hasher, nRelayNodes](CNode* pnode) {
|
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();
|
uint64_t hashKey = CSipHasher(hasher).Write(pnode->GetId()).Finalize();
|
||||||
for (unsigned int i = 0; i < nRelayNodes; i++) {
|
for (unsigned int i = 0; i < nRelayNodes; i++) {
|
||||||
if (hashKey > best[i].first) {
|
if (hashKey > best[i].first) {
|
||||||
@ -1449,11 +1449,11 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
|
|||||||
{
|
{
|
||||||
bool sendMerkleBlock = false;
|
bool sendMerkleBlock = false;
|
||||||
CMerkleBlock merkleBlock;
|
CMerkleBlock merkleBlock;
|
||||||
{
|
if (pfrom->m_tx_relay != nullptr) {
|
||||||
LOCK(pfrom->cs_filter);
|
LOCK(pfrom->m_tx_relay->cs_filter);
|
||||||
if (pfrom->pfilter) {
|
if (pfrom->m_tx_relay->pfilter) {
|
||||||
sendMerkleBlock = true;
|
sendMerkleBlock = true;
|
||||||
merkleBlock = CMerkleBlock(*pblock, *pfrom->pfilter);
|
merkleBlock = CMerkleBlock(*pblock, *pfrom->m_tx_relay->pfilter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sendMerkleBlock) {
|
if (sendMerkleBlock) {
|
||||||
@ -1513,7 +1513,12 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
|
|||||||
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
|
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
|
||||||
std::vector<CInv> vNotFound;
|
std::vector<CInv> vNotFound;
|
||||||
const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
|
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);
|
LOCK(cs_main);
|
||||||
|
|
||||||
while (it != pfrom->vRecvGetData.end() && (it->type == MSG_TX || it->type == MSG_WITNESS_TX)) {
|
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()) {
|
if (mi != mapRelay.end()) {
|
||||||
connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second));
|
connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second));
|
||||||
push = true;
|
push = true;
|
||||||
} else if (pfrom->timeLastMempoolReq) {
|
} else if (pfrom->m_tx_relay->timeLastMempoolReq) {
|
||||||
auto txinfo = mempool.info(inv.hash);
|
auto txinfo = mempool.info(inv.hash);
|
||||||
// To protect privacy, do not answer getdata using the mempool when
|
// To protect privacy, do not answer getdata using the mempool when
|
||||||
// that TX couldn't have been INVed in reply to a MEMPOOL request.
|
// 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));
|
connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *txinfo.tx));
|
||||||
push = true;
|
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 (!pfrom->fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr && pfrom->m_tx_relay != nullptr) {
|
||||||
// If this is an outbound peer, check to see if we should protect
|
// If this is an outbound full-relay peer, check to see if we should protect
|
||||||
// it from the bad/lagging chain logic.
|
// 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) {
|
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());
|
LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom->GetId());
|
||||||
nodestate->m_chain_sync.m_protect = true;
|
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"
|
// set nodes not capable of serving the complete blockchain history as "limited nodes"
|
||||||
pfrom->m_limited_node = (!(nServices & NODE_NETWORK) && (nServices & NODE_NETWORK_LIMITED));
|
pfrom->m_limited_node = (!(nServices & NODE_NETWORK) && (nServices & NODE_NETWORK_LIMITED));
|
||||||
|
|
||||||
{
|
if (pfrom->m_tx_relay != nullptr) {
|
||||||
LOCK(pfrom->cs_filter);
|
LOCK(pfrom->m_tx_relay->cs_filter);
|
||||||
pfrom->fRelayTxes = fRelay; // set to true after we get the first filter* message
|
pfrom->m_tx_relay->fRelayTxes = fRelay; // set to true after we get the first filter* message
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change version
|
// Change version
|
||||||
@ -2017,7 +2024,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
|||||||
UpdatePreferredDownload(pfrom, State(pfrom->GetId()));
|
UpdatePreferredDownload(pfrom, State(pfrom->GetId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pfrom->fInbound)
|
if (!pfrom->fInbound && pfrom->IsAddrRelayPeer())
|
||||||
{
|
{
|
||||||
// Advertise our address
|
// Advertise our address
|
||||||
if (fListen && !::ChainstateActive().IsInitialBlockDownload())
|
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.
|
// Mark this node as currently connected, so we update its timestamp later.
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
State(pfrom->GetId())->fCurrentlyConnected = true;
|
State(pfrom->GetId())->fCurrentlyConnected = true;
|
||||||
LogPrintf("New outbound peer connected: version: %d, blocks=%d, peer=%d%s\n",
|
LogPrintf("New outbound peer connected: version: %d, blocks=%d, peer=%d%s (%s)\n",
|
||||||
pfrom->nVersion.load(), pfrom->nStartingHeight, pfrom->GetId(),
|
pfrom->nVersion.load(), pfrom->nStartingHeight,
|
||||||
(fLogIPs ? strprintf(", peeraddr=%s", pfrom->addr.ToString()) : ""));
|
pfrom->GetId(), (fLogIPs ? strprintf(", peeraddr=%s", pfrom->addr.ToString()) : ""),
|
||||||
|
pfrom->m_tx_relay == nullptr ? "block-relay" : "full-relay");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pfrom->nVersion >= SENDHEADERS_VERSION) {
|
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
|
// Don't want addr from older versions unless seeding
|
||||||
if (pfrom->nVersion < CADDR_TIME_VERSION && connman->GetAddressCount() > 1000)
|
if (pfrom->nVersion < CADDR_TIME_VERSION && connman->GetAddressCount() > 1000)
|
||||||
return true;
|
return true;
|
||||||
|
if (!pfrom->IsAddrRelayPeer()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (vAddr.size() > 1000)
|
if (vAddr.size() > 1000)
|
||||||
{
|
{
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
@ -2215,7 +2226,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
|||||||
return false;
|
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
|
// Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true
|
||||||
if (pfrom->HasPermission(PF_RELAY))
|
if (pfrom->HasPermission(PF_RELAY))
|
||||||
@ -2254,7 +2267,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
|||||||
{
|
{
|
||||||
pfrom->AddInventoryKnown(inv);
|
pfrom->AddInventoryKnown(inv);
|
||||||
if (fBlocksOnly) {
|
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()) {
|
} else if (!fAlreadyHave && !fImporting && !fReindex && !::ChainstateActive().IsInitialBlockDownload()) {
|
||||||
RequestTx(State(pfrom->GetId()), inv.hash, current_time);
|
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) {
|
if (strCommand == NetMsgType::TX) {
|
||||||
// Stop processing the transaction early if
|
// Stop processing the transaction early if
|
||||||
// We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off
|
// 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());
|
LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom->GetId());
|
||||||
|
pfrom->fDisconnect = true;
|
||||||
return 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());
|
LogPrint(BCLog::NET, "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom->GetId());
|
||||||
return true;
|
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
|
// Only send one GetAddr response per connection to reduce resource waste
|
||||||
// and discourage addr stamping of INV announcements.
|
// and discourage addr stamping of INV announcements.
|
||||||
@ -3031,8 +3052,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOCK(pfrom->cs_inventory);
|
if (pfrom->m_tx_relay != nullptr) {
|
||||||
pfrom->fSendMempool = true;
|
LOCK(pfrom->m_tx_relay->cs_tx_inventory);
|
||||||
|
pfrom->m_tx_relay->fSendMempool = true;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3123,12 +3146,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
|||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
Misbehaving(pfrom->GetId(), 100);
|
Misbehaving(pfrom->GetId(), 100);
|
||||||
}
|
}
|
||||||
else
|
else if (pfrom->m_tx_relay != nullptr)
|
||||||
{
|
{
|
||||||
LOCK(pfrom->cs_filter);
|
LOCK(pfrom->m_tx_relay->cs_filter);
|
||||||
pfrom->pfilter.reset(new CBloomFilter(filter));
|
pfrom->m_tx_relay->pfilter.reset(new CBloomFilter(filter));
|
||||||
pfrom->pfilter->UpdateEmptyFull();
|
pfrom->m_tx_relay->pfilter->UpdateEmptyFull();
|
||||||
pfrom->fRelayTxes = true;
|
pfrom->m_tx_relay->fRelayTxes = true;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -3142,10 +3165,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
|||||||
bool bad = false;
|
bool bad = false;
|
||||||
if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) {
|
if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) {
|
||||||
bad = true;
|
bad = true;
|
||||||
} else {
|
} else if (pfrom->m_tx_relay != nullptr) {
|
||||||
LOCK(pfrom->cs_filter);
|
LOCK(pfrom->m_tx_relay->cs_filter);
|
||||||
if (pfrom->pfilter) {
|
if (pfrom->m_tx_relay->pfilter) {
|
||||||
pfrom->pfilter->insert(vData);
|
pfrom->m_tx_relay->pfilter->insert(vData);
|
||||||
} else {
|
} else {
|
||||||
bad = true;
|
bad = true;
|
||||||
}
|
}
|
||||||
@ -3158,11 +3181,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (strCommand == NetMsgType::FILTERCLEAR) {
|
if (strCommand == NetMsgType::FILTERCLEAR) {
|
||||||
LOCK(pfrom->cs_filter);
|
if (pfrom->m_tx_relay == nullptr) {
|
||||||
if (pfrom->GetLocalServices() & NODE_BLOOM) {
|
return true;
|
||||||
pfrom->pfilter.reset(new CBloomFilter());
|
|
||||||
}
|
}
|
||||||
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3170,9 +3196,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
|||||||
CAmount newFeeFilter = 0;
|
CAmount newFeeFilter = 0;
|
||||||
vRecv >> newFeeFilter;
|
vRecv >> newFeeFilter;
|
||||||
if (MoneyRange(newFeeFilter)) {
|
if (MoneyRange(newFeeFilter)) {
|
||||||
{
|
if (pfrom->m_tx_relay != nullptr) {
|
||||||
LOCK(pfrom->cs_feeFilter);
|
LOCK(pfrom->m_tx_relay->cs_feeFilter);
|
||||||
pfrom->minFeeFilter = newFeeFilter;
|
pfrom->m_tx_relay->minFeeFilter = newFeeFilter;
|
||||||
}
|
}
|
||||||
LogPrint(BCLog::NET, "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->GetId());
|
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
|
if (state == nullptr) return; // shouldn't be possible, but just in case
|
||||||
// Don't evict our protected peers
|
// Don't evict our protected peers
|
||||||
if (state->m_chain_sync.m_protect) return;
|
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)) {
|
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();
|
worst_peer = pnode->GetId();
|
||||||
oldest_block_announcement = state->m_last_block_announcement;
|
oldest_block_announcement = state->m_last_block_announcement;
|
||||||
@ -3576,7 +3604,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
|
|||||||
|
|
||||||
// Address refresh broadcast
|
// Address refresh broadcast
|
||||||
int64_t nNow = GetTimeMicros();
|
int64_t nNow = GetTimeMicros();
|
||||||
if (!::ChainstateActive().IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
|
if (pto->IsAddrRelayPeer() && !::ChainstateActive().IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) {
|
||||||
AdvertiseLocal(pto);
|
AdvertiseLocal(pto);
|
||||||
pto->nNextLocalAddrSend = PoissonNextSend(nNow, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
|
pto->nNextLocalAddrSend = PoissonNextSend(nNow, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
|
||||||
}
|
}
|
||||||
@ -3584,7 +3612,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
|
|||||||
//
|
//
|
||||||
// Message: addr
|
// Message: addr
|
||||||
//
|
//
|
||||||
if (pto->nNextAddrSend < nNow) {
|
if (pto->IsAddrRelayPeer() && pto->nNextAddrSend < nNow) {
|
||||||
pto->nNextAddrSend = PoissonNextSend(nNow, AVG_ADDRESS_BROADCAST_INTERVAL);
|
pto->nNextAddrSend = PoissonNextSend(nNow, AVG_ADDRESS_BROADCAST_INTERVAL);
|
||||||
std::vector<CAddress> vAddr;
|
std::vector<CAddress> vAddr;
|
||||||
vAddr.reserve(pto->vAddrToSend.size());
|
vAddr.reserve(pto->vAddrToSend.size());
|
||||||
@ -3792,69 +3820,71 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
|
|||||||
}
|
}
|
||||||
pto->vInventoryBlockToSend.clear();
|
pto->vInventoryBlockToSend.clear();
|
||||||
|
|
||||||
|
if (pto->m_tx_relay != nullptr) {
|
||||||
|
LOCK(pto->m_tx_relay->cs_tx_inventory);
|
||||||
// Check whether periodic sends should happen
|
// Check whether periodic sends should happen
|
||||||
bool fSendTrickle = pto->HasPermission(PF_NOBAN);
|
bool fSendTrickle = pto->HasPermission(PF_NOBAN);
|
||||||
if (pto->nNextInvSend < nNow) {
|
if (pto->m_tx_relay->nNextInvSend < nNow) {
|
||||||
fSendTrickle = true;
|
fSendTrickle = true;
|
||||||
if (pto->fInbound) {
|
if (pto->fInbound) {
|
||||||
pto->nNextInvSend = connman->PoissonNextSendInbound(nNow, INVENTORY_BROADCAST_INTERVAL);
|
pto->m_tx_relay->nNextInvSend = connman->PoissonNextSendInbound(nNow, INVENTORY_BROADCAST_INTERVAL);
|
||||||
} else {
|
} else {
|
||||||
// Use half the delay for outbound peers, as there is less privacy concern for them.
|
// 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.
|
// Time to send but the peer has requested we not relay transactions.
|
||||||
if (fSendTrickle) {
|
if (fSendTrickle) {
|
||||||
LOCK(pto->cs_filter);
|
LOCK(pto->m_tx_relay->cs_filter);
|
||||||
if (!pto->fRelayTxes) pto->setInventoryTxToSend.clear();
|
if (!pto->m_tx_relay->fRelayTxes) pto->m_tx_relay->setInventoryTxToSend.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Respond to BIP35 mempool requests
|
// Respond to BIP35 mempool requests
|
||||||
if (fSendTrickle && pto->fSendMempool) {
|
if (fSendTrickle && pto->m_tx_relay->fSendMempool) {
|
||||||
auto vtxinfo = mempool.infoAll();
|
auto vtxinfo = mempool.infoAll();
|
||||||
pto->fSendMempool = false;
|
pto->m_tx_relay->fSendMempool = false;
|
||||||
CAmount filterrate = 0;
|
CAmount filterrate = 0;
|
||||||
{
|
{
|
||||||
LOCK(pto->cs_feeFilter);
|
LOCK(pto->m_tx_relay->cs_feeFilter);
|
||||||
filterrate = pto->minFeeFilter;
|
filterrate = pto->m_tx_relay->minFeeFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOCK(pto->cs_filter);
|
LOCK(pto->m_tx_relay->cs_filter);
|
||||||
|
|
||||||
for (const auto& txinfo : vtxinfo) {
|
for (const auto& txinfo : vtxinfo) {
|
||||||
const uint256& hash = txinfo.tx->GetHash();
|
const uint256& hash = txinfo.tx->GetHash();
|
||||||
CInv inv(MSG_TX, hash);
|
CInv inv(MSG_TX, hash);
|
||||||
pto->setInventoryTxToSend.erase(hash);
|
pto->m_tx_relay->setInventoryTxToSend.erase(hash);
|
||||||
if (filterrate) {
|
if (filterrate) {
|
||||||
if (txinfo.feeRate.GetFeePerK() < filterrate)
|
if (txinfo.feeRate.GetFeePerK() < filterrate)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (pto->pfilter) {
|
if (pto->m_tx_relay->pfilter) {
|
||||||
if (!pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
|
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);
|
vInv.push_back(inv);
|
||||||
if (vInv.size() == MAX_INV_SZ) {
|
if (vInv.size() == MAX_INV_SZ) {
|
||||||
connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
|
connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
|
||||||
vInv.clear();
|
vInv.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pto->timeLastMempoolReq = GetTime();
|
pto->m_tx_relay->timeLastMempoolReq = GetTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine transactions to relay
|
// Determine transactions to relay
|
||||||
if (fSendTrickle) {
|
if (fSendTrickle) {
|
||||||
// Produce a vector with all candidates for sending
|
// Produce a vector with all candidates for sending
|
||||||
std::vector<std::set<uint256>::iterator> vInvTx;
|
std::vector<std::set<uint256>::iterator> vInvTx;
|
||||||
vInvTx.reserve(pto->setInventoryTxToSend.size());
|
vInvTx.reserve(pto->m_tx_relay->setInventoryTxToSend.size());
|
||||||
for (std::set<uint256>::iterator it = pto->setInventoryTxToSend.begin(); it != pto->setInventoryTxToSend.end(); it++) {
|
for (std::set<uint256>::iterator it = pto->m_tx_relay->setInventoryTxToSend.begin(); it != pto->m_tx_relay->setInventoryTxToSend.end(); it++) {
|
||||||
vInvTx.push_back(it);
|
vInvTx.push_back(it);
|
||||||
}
|
}
|
||||||
CAmount filterrate = 0;
|
CAmount filterrate = 0;
|
||||||
{
|
{
|
||||||
LOCK(pto->cs_feeFilter);
|
LOCK(pto->m_tx_relay->cs_feeFilter);
|
||||||
filterrate = pto->minFeeFilter;
|
filterrate = pto->m_tx_relay->minFeeFilter;
|
||||||
}
|
}
|
||||||
// Topologically and fee-rate sort the inventory we send for privacy and priority reasons.
|
// 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.
|
// 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,
|
// 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.
|
// especially since we have many peers and some will draw much shorter delays.
|
||||||
unsigned int nRelayedTransactions = 0;
|
unsigned int nRelayedTransactions = 0;
|
||||||
LOCK(pto->cs_filter);
|
LOCK(pto->m_tx_relay->cs_filter);
|
||||||
while (!vInvTx.empty() && nRelayedTransactions < INVENTORY_BROADCAST_MAX) {
|
while (!vInvTx.empty() && nRelayedTransactions < INVENTORY_BROADCAST_MAX) {
|
||||||
// Fetch the top element from the heap
|
// Fetch the top element from the heap
|
||||||
std::pop_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder);
|
std::pop_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder);
|
||||||
@ -3871,9 +3901,9 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
|
|||||||
vInvTx.pop_back();
|
vInvTx.pop_back();
|
||||||
uint256 hash = *it;
|
uint256 hash = *it;
|
||||||
// Remove it from the to-be-sent set
|
// 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
|
// Check if not in the filter already
|
||||||
if (pto->filterInventoryKnown.contains(hash)) {
|
if (pto->m_tx_relay->filterInventoryKnown.contains(hash)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Not in the mempool anymore? don't bother sending it.
|
// 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) {
|
if (filterrate && txinfo.feeRate.GetFeePerK() < filterrate) {
|
||||||
continue;
|
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
|
// Send
|
||||||
vInv.push_back(CInv(MSG_TX, hash));
|
vInv.push_back(CInv(MSG_TX, hash));
|
||||||
nRelayedTransactions++;
|
nRelayedTransactions++;
|
||||||
@ -3905,7 +3935,8 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
|
|||||||
connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
|
connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
|
||||||
vInv.clear();
|
vInv.clear();
|
||||||
}
|
}
|
||||||
pto->filterInventoryKnown.insert(hash);
|
pto->m_tx_relay->filterInventoryKnown.insert(hash);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4066,27 +4097,27 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
|
|||||||
// Message: feefilter
|
// Message: feefilter
|
||||||
//
|
//
|
||||||
// We don't want white listed peers to filter txs to us if we have -whitelistforcerelay
|
// 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)) {
|
!pto->HasPermission(PF_FORCERELAY)) {
|
||||||
CAmount currentFilter = mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
|
CAmount currentFilter = mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
|
||||||
int64_t timeNow = GetTimeMicros();
|
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 CFeeRate default_feerate(DEFAULT_MIN_RELAY_TX_FEE);
|
||||||
static FeeFilterRounder filterRounder(default_feerate);
|
static FeeFilterRounder filterRounder(default_feerate);
|
||||||
CAmount filterToSend = filterRounder.round(currentFilter);
|
CAmount filterToSend = filterRounder.round(currentFilter);
|
||||||
// We always have a fee filter of at least minRelayTxFee
|
// We always have a fee filter of at least minRelayTxFee
|
||||||
filterToSend = std::max(filterToSend, ::minRelayTxFee.GetFeePerK());
|
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));
|
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
|
// 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.
|
// until scheduled broadcast, then move the broadcast to within MAX_FEEFILTER_CHANGE_DELAY.
|
||||||
else if (timeNow + MAX_FEEFILTER_CHANGE_DELAY * 1000000 < pto->nextSendTimeFeeFilter &&
|
else if (timeNow + MAX_FEEFILTER_CHANGE_DELAY * 1000000 < pto->m_tx_relay->nextSendTimeFeeFilter &&
|
||||||
(currentFilter < 3 * pto->lastSentFeeFilter / 4 || currentFilter > 4 * pto->lastSentFeeFilter / 3)) {
|
(currentFilter < 3 * pto->m_tx_relay->lastSentFeeFilter / 4 || currentFilter > 4 * pto->m_tx_relay->lastSentFeeFilter / 3)) {
|
||||||
pto->nextSendTimeFeeFilter = timeNow + GetRandInt(MAX_FEEFILTER_CHANGE_DELAY) * 1000000;
|
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);
|
auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, scheduler, false);
|
||||||
|
|
||||||
const Consensus::Params& consensusParams = Params().GetConsensus();
|
const Consensus::Params& consensusParams = Params().GetConsensus();
|
||||||
constexpr int nMaxOutbound = 8;
|
constexpr int max_outbound_full_relay = 8;
|
||||||
CConnman::Options options;
|
CConnman::Options options;
|
||||||
options.nMaxConnections = 125;
|
options.nMaxConnections = 125;
|
||||||
options.nMaxOutbound = nMaxOutbound;
|
options.m_max_outbound_full_relay = max_outbound_full_relay;
|
||||||
options.nMaxFeeler = 1;
|
options.nMaxFeeler = 1;
|
||||||
|
|
||||||
connman->Init(options);
|
connman->Init(options);
|
||||||
std::vector<CNode *> vNodes;
|
std::vector<CNode *> vNodes;
|
||||||
|
|
||||||
// Mock some outbound peers
|
// 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());
|
AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +190,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
|
|||||||
AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
|
AddRandomOutboundPeer(vNodes, *peerLogic, connman.get());
|
||||||
|
|
||||||
peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
|
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);
|
BOOST_CHECK(vNodes[i]->fDisconnect == false);
|
||||||
}
|
}
|
||||||
// Last added node should get marked for eviction
|
// 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());
|
UpdateLastBlockAnnounceTime(vNodes.back()->GetId(), GetTime());
|
||||||
|
|
||||||
peerLogic->CheckForStaleTipAndEvictPeers(consensusParams);
|
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[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);
|
BOOST_CHECK(vNodes.back()->fDisconnect == false);
|
||||||
|
|
||||||
bool dummy;
|
bool dummy;
|
||||||
|
@ -19,7 +19,7 @@ class P2PBlocksOnly(BitcoinTestFramework):
|
|||||||
def run_test(self):
|
def run_test(self):
|
||||||
self.nodes[0].add_p2p_connection(P2PInterface())
|
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]
|
prevtx = self.nodes[0].getblock(self.nodes[0].getblockhash(1), 2)['tx'][0]
|
||||||
rawtx = self.nodes[0].createrawtransaction(
|
rawtx = self.nodes[0].createrawtransaction(
|
||||||
inputs=[{
|
inputs=[{
|
||||||
@ -42,13 +42,17 @@ class P2PBlocksOnly(BitcoinTestFramework):
|
|||||||
assert_equal(self.nodes[0].getnetworkinfo()['localrelay'], False)
|
assert_equal(self.nodes[0].getnetworkinfo()['localrelay'], False)
|
||||||
with self.nodes[0].assert_debug_log(['transaction sent in violation of protocol peer=0']):
|
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.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)
|
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')
|
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)
|
assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], True)
|
||||||
txid = self.nodes[0].testmempoolaccept([sigtx])[0]['txid']
|
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].sendrawtransaction(sigtx)
|
||||||
self.nodes[0].p2p.wait_for_tx(txid)
|
self.nodes[0].p2p.wait_for_tx(txid)
|
||||||
assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
|
assert_equal(self.nodes[0].getmempoolinfo()['size'], 1)
|
||||||
|
Loading…
Reference in New Issue
Block a user