diff --git a/src/net.cpp b/src/net.cpp index 78b33954d..9514da880 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -499,9 +499,11 @@ void CNode::copyStats(CNodeStats &stats) X(nServices); X(addr); X(addrBind); - { + 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); - { + 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->m_tx_relay->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->m_tx_relay->fRelayTxes, node->m_tx_relay->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); } diff --git a/src/net.h b/src/net.h index d12fc3ae9..218dd67ba 100644 --- a/src/net.h +++ b/src/net.h @@ -848,7 +848,7 @@ public: void AddInventoryKnown(const CInv& inv) { - { + if (m_tx_relay != nullptr) { LOCK(m_tx_relay->cs_tx_inventory); m_tx_relay->filterInventoryKnown.insert(inv.hash); } @@ -856,7 +856,7 @@ public: void PushInventory(const CInv& inv) { - if (inv.type == MSG_TX) { + 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); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 303a060a9..0802cf424 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1448,7 +1448,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c { bool sendMerkleBlock = false; CMerkleBlock merkleBlock; - { + if (pfrom->m_tx_relay != nullptr) { LOCK(pfrom->m_tx_relay->cs_filter); if (pfrom->m_tx_relay->pfilter) { sendMerkleBlock = true; @@ -1512,7 +1512,7 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm std::deque::iterator it = pfrom->vRecvGetData.begin(); std::vector vNotFound; const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); - { + if (pfrom->m_tx_relay != nullptr) { LOCK(cs_main); while (it != pfrom->vRecvGetData.end() && (it->type == MSG_TX || it->type == MSG_WITNESS_TX)) { @@ -1995,7 +1995,7 @@ 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)); - { + 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 } @@ -3030,8 +3030,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr return true; } - LOCK(pfrom->m_tx_relay->cs_tx_inventory); - pfrom->m_tx_relay->fSendMempool = true; + if (pfrom->m_tx_relay != nullptr) { + LOCK(pfrom->m_tx_relay->cs_tx_inventory); + pfrom->m_tx_relay->fSendMempool = true; + } return true; } @@ -3122,7 +3124,7 @@ 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->m_tx_relay->cs_filter); pfrom->m_tx_relay->pfilter.reset(new CBloomFilter(filter)); @@ -3141,7 +3143,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr bool bad = false; if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) { bad = true; - } else { + } 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); @@ -3157,6 +3159,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr } if (strCommand == NetMsgType::FILTERCLEAR) { + if (pfrom->m_tx_relay == nullptr) { + return true; + } LOCK(pfrom->m_tx_relay->cs_filter); if (pfrom->GetLocalServices() & NODE_BLOOM) { pfrom->m_tx_relay->pfilter.reset(new CBloomFilter()); @@ -3169,7 +3174,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr CAmount newFeeFilter = 0; vRecv >> newFeeFilter; if (MoneyRange(newFeeFilter)) { - { + if (pfrom->m_tx_relay != nullptr) { LOCK(pfrom->m_tx_relay->cs_feeFilter); pfrom->m_tx_relay->minFeeFilter = newFeeFilter; } @@ -3791,121 +3796,123 @@ bool PeerLogicValidation::SendMessages(CNode* pto) } pto->vInventoryBlockToSend.clear(); - LOCK(pto->m_tx_relay->cs_tx_inventory); - // Check whether periodic sends should happen - bool fSendTrickle = pto->HasPermission(PF_NOBAN); - if (pto->m_tx_relay->nNextInvSend < nNow) { - fSendTrickle = true; - if (pto->fInbound) { - 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->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->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->m_tx_relay->fSendMempool) { - auto vtxinfo = mempool.infoAll(); - pto->m_tx_relay->fSendMempool = false; - CAmount filterrate = 0; - { - LOCK(pto->m_tx_relay->cs_feeFilter); - filterrate = pto->m_tx_relay->minFeeFilter; - } - - LOCK(pto->m_tx_relay->cs_filter); - - for (const auto& txinfo : vtxinfo) { - const uint256& hash = txinfo.tx->GetHash(); - CInv inv(MSG_TX, hash); - pto->m_tx_relay->setInventoryTxToSend.erase(hash); - if (filterrate) { - if (txinfo.feeRate.GetFeePerK() < filterrate) - continue; - } - if (pto->m_tx_relay->pfilter) { - if (!pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue; - } - 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(); + 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->m_tx_relay->nNextInvSend < nNow) { + fSendTrickle = true; + if (pto->fInbound) { + 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->m_tx_relay->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> 1); } } - pto->m_tx_relay->timeLastMempoolReq = GetTime(); - } - // Determine transactions to relay - if (fSendTrickle) { - // Produce a vector with all candidates for sending - std::vector::iterator> vInvTx; - vInvTx.reserve(pto->m_tx_relay->setInventoryTxToSend.size()); - for (std::set::iterator it = pto->m_tx_relay->setInventoryTxToSend.begin(); it != pto->m_tx_relay->setInventoryTxToSend.end(); it++) { - vInvTx.push_back(it); + // Time to send but the peer has requested we not relay transactions. + if (fSendTrickle) { + LOCK(pto->m_tx_relay->cs_filter); + if (!pto->m_tx_relay->fRelayTxes) pto->m_tx_relay->setInventoryTxToSend.clear(); } - CAmount filterrate = 0; - { - 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. - CompareInvMempoolOrder compareInvMempoolOrder(&mempool); - std::make_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder); - // 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->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); - std::set::iterator it = vInvTx.back(); - vInvTx.pop_back(); - uint256 hash = *it; - // Remove it from the to-be-sent set - pto->m_tx_relay->setInventoryTxToSend.erase(it); - // Check if not in the filter already - if (pto->m_tx_relay->filterInventoryKnown.contains(hash)) { - continue; - } - // Not in the mempool anymore? don't bother sending it. - auto txinfo = mempool.info(hash); - if (!txinfo.tx) { - continue; - } - if (filterrate && txinfo.feeRate.GetFeePerK() < filterrate) { - 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++; + + // Respond to BIP35 mempool requests + if (fSendTrickle && pto->m_tx_relay->fSendMempool) { + auto vtxinfo = mempool.infoAll(); + pto->m_tx_relay->fSendMempool = false; + CAmount filterrate = 0; { - // Expire old relay messages - while (!vRelayExpiration.empty() && vRelayExpiration.front().first < nNow) - { - mapRelay.erase(vRelayExpiration.front().second); - vRelayExpiration.pop_front(); - } + LOCK(pto->m_tx_relay->cs_feeFilter); + filterrate = pto->m_tx_relay->minFeeFilter; + } - auto ret = mapRelay.insert(std::make_pair(hash, std::move(txinfo.tx))); - if (ret.second) { - vRelayExpiration.push_back(std::make_pair(nNow + 15 * 60 * 1000000, ret.first)); + LOCK(pto->m_tx_relay->cs_filter); + + for (const auto& txinfo : vtxinfo) { + const uint256& hash = txinfo.tx->GetHash(); + CInv inv(MSG_TX, hash); + pto->m_tx_relay->setInventoryTxToSend.erase(hash); + if (filterrate) { + if (txinfo.feeRate.GetFeePerK() < filterrate) + continue; + } + if (pto->m_tx_relay->pfilter) { + if (!pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue; + } + 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(); } } - if (vInv.size() == MAX_INV_SZ) { - connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); - vInv.clear(); + pto->m_tx_relay->timeLastMempoolReq = GetTime(); + } + + // Determine transactions to relay + if (fSendTrickle) { + // Produce a vector with all candidates for sending + std::vector::iterator> vInvTx; + vInvTx.reserve(pto->m_tx_relay->setInventoryTxToSend.size()); + for (std::set::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->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. + CompareInvMempoolOrder compareInvMempoolOrder(&mempool); + std::make_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder); + // 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->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); + std::set::iterator it = vInvTx.back(); + vInvTx.pop_back(); + uint256 hash = *it; + // Remove it from the to-be-sent set + pto->m_tx_relay->setInventoryTxToSend.erase(it); + // Check if not in the filter already + if (pto->m_tx_relay->filterInventoryKnown.contains(hash)) { + continue; + } + // Not in the mempool anymore? don't bother sending it. + auto txinfo = mempool.info(hash); + if (!txinfo.tx) { + continue; + } + if (filterrate && txinfo.feeRate.GetFeePerK() < filterrate) { + 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++; + { + // Expire old relay messages + while (!vRelayExpiration.empty() && vRelayExpiration.front().first < nNow) + { + mapRelay.erase(vRelayExpiration.front().second); + vRelayExpiration.pop_front(); + } + + auto ret = mapRelay.insert(std::make_pair(hash, std::move(txinfo.tx))); + if (ret.second) { + vRelayExpiration.push_back(std::make_pair(nNow + 15 * 60 * 1000000, ret.first)); + } + } + if (vInv.size() == MAX_INV_SZ) { + connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); + vInv.clear(); + } + pto->m_tx_relay->filterInventoryKnown.insert(hash); } - pto->m_tx_relay->filterInventoryKnown.insert(hash); } } } @@ -4066,7 +4073,7 @@ 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();