From 678df6318ede69a2ed619138dc4023661884df38 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 16 Jun 2022 01:04:17 +0300 Subject: [PATCH] fix(net): Extend blocks-relay-only to also ignore some Dash-specific messages/invs --- src/llmq/instantsend.cpp | 2 +- src/masternode/sync.cpp | 2 +- src/net.cpp | 38 ++++++---- src/net.h | 75 +++++++++---------- src/net_processing.cpp | 118 ++++++++++++++++-------------- src/protocol.cpp | 33 +++++++++ src/protocol.h | 3 + test/functional/p2p_blocksonly.py | 2 +- 8 files changed, 159 insertions(+), 114 deletions(-) diff --git a/src/llmq/instantsend.cpp b/src/llmq/instantsend.cpp index 99a62581aa..ef4b7623c6 100644 --- a/src/llmq/instantsend.cpp +++ b/src/llmq/instantsend.cpp @@ -1526,7 +1526,7 @@ void CInstantSendManager::AskNodesForLockedTx(const uint256& txid, const CConnma if (nodesToAskFor.size() >= 4) { return; } - if (pnode->m_tx_relay != nullptr) { + if (!pnode->m_block_relay_only_peer) { LOCK(pnode->m_tx_relay->cs_tx_inventory); if (pnode->m_tx_relay->filterInventoryKnown.contains(txid)) { pnode->AddRef(); diff --git a/src/masternode/sync.cpp b/src/masternode/sync.cpp index 4f06f4230c..a239fb8d78 100644 --- a/src/masternode/sync.cpp +++ b/src/masternode/sync.cpp @@ -200,7 +200,7 @@ void CMasternodeSync::ProcessTick(CConnman& connman) // Now that the blockchain is synced request the mempool from the connected outbound nodes if possible for (auto pNodeTmp : vNodesCopy) { bool fRequestedEarlier = netfulfilledman.HasFulfilledRequest(pNodeTmp->addr, "mempool-sync"); - if (pNodeTmp->nVersion >= 70216 && !pNodeTmp->fInbound && !fRequestedEarlier) { + if (pNodeTmp->nVersion >= 70216 && !pNodeTmp->fInbound && !fRequestedEarlier && !pNodeTmp->IsBlockRelayOnly()) { netfulfilledman.AddFulfilledRequest(pNodeTmp->addr, "mempool-sync"); connman.PushMessage(pNodeTmp, msgMaker.Make(NetMsgType::MEMPOOL)); LogPrint(BCLog::MNSYNC, "CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d -- syncing mempool from peer=%d\n", nTick, nCurrentAsset, pNodeTmp->GetId()); diff --git a/src/net.cpp b/src/net.cpp index 139b780518..124b7f7282 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -566,7 +566,7 @@ void CNode::copyStats(CNodeStats &stats, const std::vector &m_asmap) X(addr); X(addrBind); stats.m_mapped_as = addr.GetMappedAS(m_asmap); - if (m_tx_relay != nullptr) { + if (!m_block_relay_only_peer) { LOCK(m_tx_relay->cs_filter); stats.fRelayTxes = m_tx_relay->fRelayTxes; } else { @@ -947,7 +947,7 @@ bool CConnman::AttemptToEvictConnection() bool peer_relay_txes = false; bool peer_filter_not_null = false; - if (node->m_tx_relay != nullptr) { + if (!node->m_block_relay_only_peer) { 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; @@ -2159,7 +2159,7 @@ void CConnman::ThreadOpenConnections(const std::vector 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(addrman.m_asmap)); - if (pnode->m_tx_relay == nullptr) { + if (pnode->m_block_relay_only_peer) { nOutboundBlockRelay++; } else if (!pnode->fFeeler) { nOutboundFullRelay++; @@ -3480,12 +3480,17 @@ void CConnman::RelayInvFiltered(CInv &inv, const CTransaction& relatedTx, const { LOCK(cs_vNodes); for (const auto& pnode : vNodes) { - if (pnode->nVersion < minProtoVersion || !pnode->CanRelay()) + if (pnode->nVersion < minProtoVersion || !pnode->CanRelay() || pnode->m_block_relay_only_peer) { continue; - if (pnode->m_tx_relay != nullptr) { + } + { LOCK(pnode->m_tx_relay->cs_filter); - if(pnode->m_tx_relay->pfilter && !pnode->m_tx_relay->pfilter->IsRelevantAndUpdate(relatedTx)) + if (!pnode->m_tx_relay->fRelayTxes) { continue; + } + if (pnode->m_tx_relay->pfilter && !pnode->m_tx_relay->pfilter->IsRelevantAndUpdate(relatedTx)) { + continue; + } } pnode->PushInventory(inv); } @@ -3495,11 +3500,17 @@ void CConnman::RelayInvFiltered(CInv &inv, const uint256& relatedTxHash, const i { LOCK(cs_vNodes); for (const auto& pnode : vNodes) { - if (pnode->nVersion < minProtoVersion || !pnode->CanRelay()) + if (pnode->nVersion < minProtoVersion || !pnode->CanRelay() || pnode->m_block_relay_only_peer) { continue; - if (pnode->m_tx_relay != nullptr) { + } + { LOCK(pnode->m_tx_relay->cs_filter); - if(pnode->m_tx_relay->pfilter && !pnode->m_tx_relay->pfilter->contains(relatedTxHash)) continue; + if (!pnode->m_tx_relay->fRelayTxes) { + continue; + } + if (pnode->m_tx_relay->pfilter && !pnode->m_tx_relay->pfilter->contains(relatedTxHash)) { + continue; + } } pnode->PushInventory(inv); } @@ -3641,10 +3652,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn fInbound(fInboundIn), nKeyedNetGroup(nKeyedNetGroupIn), addrKnown(5000, 0.001), - // 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), + m_block_relay_only_peer(block_relay_only), id(idIn), nLocalHostNonce(nLocalHostNonceIn), nLocalServices(nLocalServicesIn), @@ -3653,9 +3661,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn hSocket = hSocketIn; addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; hashContinue = uint256(); - if (!block_relay_only) { - m_tx_relay = MakeUnique(); - } + m_tx_relay = MakeUnique(); for (const std::string &msg : getAllNetMessageTypes()) mapRecvBytesPerMsgCmd[msg] = 0; diff --git a/src/net.h b/src/net.h index 2da1ba19e9..20ddcc65b7 100644 --- a/src/net.h +++ b/src/net.h @@ -1028,15 +1028,25 @@ public: int64_t nNextAddrSend GUARDED_BY(cs_sendProcessing){0}; int64_t nNextLocalAddrSend GUARDED_BY(cs_sendProcessing){0}; - const bool m_addr_relay_peer; - bool IsAddrRelayPeer() const { return m_addr_relay_peer; } + const bool m_block_relay_only_peer; + + // 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). + bool IsAddrRelayPeer() const { return !m_block_relay_only_peer; } + + bool IsBlockRelayOnly() const + { + // Stop processing non-block data early if + // 1) We are in blocks only mode and peer has no relay permission + // 2) This peer is a block-relay-only peer + return (!g_relay_txes && !HasPermission(PF_RELAY)) || m_block_relay_only_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 vInventoryBlockToSend GUARDED_BY(cs_inventory); - // List of non-tx/non-block inventory items - std::vector vInventoryOtherToSend; CCriticalSection cs_inventory; /** UNIX epoch time of the last block received from this peer that we had * not yet seen (e.g. not already received from another peer), that passed @@ -1066,7 +1076,9 @@ public: 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 setInventoryTxToSend; + std::set setInventoryTxToSend GUARDED_BY(cs_tx_inventory); + // List of non-tx/non-block inventory items + std::vector vInventoryOtherToSend GUARDED_BY(cs_tx_inventory); // Used for BIP35 mempool sending, also protected by cs_tx_inventory bool fSendMempool GUARDED_BY(cs_tx_inventory){false}; // Last time a "MEMPOOL" request was serviced. @@ -1074,7 +1086,8 @@ public: std::chrono::microseconds nNextInvSend{0}; }; - // m_tx_relay == nullptr if we're not relaying transactions with this peer + // in bitcoin: m_tx_relay == nullptr if we're not relaying transactions with this peer + // in dash: m_tx_relay should never be nullptr, use m_block_relay_only_peer == true instead std::unique_ptr m_tx_relay; // Used for headers announcements - unfiltered blocks to relay @@ -1234,49 +1247,29 @@ public: void AddInventoryKnown(const uint256& hash) { - if (m_tx_relay != nullptr) { - LOCK(m_tx_relay->cs_tx_inventory); - m_tx_relay->filterInventoryKnown.insert(hash); - } + LOCK(m_tx_relay->cs_tx_inventory); + m_tx_relay->filterInventoryKnown.insert(hash); } void PushInventory(const CInv& inv) { - if (inv.type == MSG_TX || inv.type == MSG_DSTX) { - if (m_tx_relay != nullptr) { - LOCK(m_tx_relay->cs_tx_inventory); - if (!m_tx_relay->filterInventoryKnown.contains(inv.hash)) { - LogPrint(BCLog::NET, "%s -- adding new inv: %s peer=%d\n", __func__, inv.ToString(), id); - LOCK(m_tx_relay->cs_filter); - m_tx_relay->setInventoryTxToSend.insert(inv.hash); - } else { - LogPrint(BCLog::NET, "%s -- skipping known inv: %s peer=%d\n", __func__, inv.ToString(), id); - } - } else { - LogPrint(BCLog::NET, "%s -- skipping unknown inv: %s peer=%d\n", __func__, inv.ToString(), id); - } - } else if (inv.type == MSG_BLOCK) { + if (inv.type == MSG_BLOCK) { LogPrint(BCLog::NET, "%s -- adding new inv: %s peer=%d\n", __func__, inv.ToString(), id); LOCK(cs_inventory); vInventoryBlockToSend.push_back(inv.hash); - } else { - LOCK(cs_inventory); - if (m_tx_relay != nullptr) { - LOCK(m_tx_relay->cs_tx_inventory); - if (!m_tx_relay->filterInventoryKnown.contains(inv.hash)) { - LogPrint(BCLog::NET, "%s -- adding new inv: %s peer=%d\n", __func__, inv.ToString(), id); - vInventoryOtherToSend.push_back(inv); - } else { - LogPrint(BCLog::NET, "%s -- skipping known inv: %s peer=%d\n", __func__, inv.ToString(), id); - } - } else { - // TODO KNST for this case we don't use inventory - // accordingly #2292 it can increase size of transmitted data. - // Should be added dedicated bloom filter - LogPrint(BCLog::NET, "%s -- adding new inv: %s peer=%d\n", __func__, inv.ToString(), id); - vInventoryOtherToSend.push_back(inv); - } + return; } + LOCK(m_tx_relay->cs_tx_inventory); + if (m_tx_relay->filterInventoryKnown.contains(inv.hash)) { + LogPrint(BCLog::NET, "%s -- skipping known inv: %s peer=%d\n", __func__, inv.ToString(), id); + return; + } + LogPrint(BCLog::NET, "%s -- adding new inv: %s peer=%d\n", __func__, inv.ToString(), id); + if (inv.type == MSG_TX || inv.type == MSG_DSTX) { + m_tx_relay->setInventoryTxToSend.insert(inv.hash); + return; + } + m_tx_relay->vInventoryOtherToSend.push_back(inv); } void PushBlockHash(const uint256 &hash) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 4d0680575b..17ef95b847 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -489,7 +489,7 @@ static void PushNodeVersion(CNode *pnode, CConnman* connman, int64_t nTime) } connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, nProtocolVersion, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe, - nonce, strSubVersion, nNodeStartingHeight, ::g_relay_txes && pnode->m_tx_relay != nullptr, mnauthChallenge, pnode->m_masternode_connection.load())); + nonce, strSubVersion, nNodeStartingHeight, ::g_relay_txes && !pnode->m_block_relay_only_peer, mnauthChallenge, pnode->m_masternode_connection.load())); if (fLogIPs) { LogPrint(BCLog::NET, "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", nProtocolVersion, nNodeStartingHeight, addrMe.ToString(), addrYou.ToString(), nodeid); @@ -1688,7 +1688,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c else if (inv.type == MSG_FILTERED_BLOCK) { bool sendMerkleBlock = false; CMerkleBlock merkleBlock; - if (pfrom->m_tx_relay != nullptr) { + if (!pfrom->m_block_relay_only_peer) { LOCK(pfrom->m_tx_relay->cs_filter); if (pfrom->m_tx_relay->pfilter) { sendMerkleBlock = true; @@ -1757,11 +1757,7 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm std::vector 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->IsKnownType()) { @@ -1775,6 +1771,13 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK) { break; } + if (pfrom->m_block_relay_only_peer && NetMessageViolatesBlocksOnly(inv.GetCommand())) { + // Note that if we receive a getdata for non-block messages + // from a block-relay-only outbound peer that violate the policy, + // we will stop processing further getdata messages from this peer + // (likely resulting in our peer eventually disconnecting us). + break; + } it++; // Send stream from relay memory @@ -2141,7 +2144,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, ChainstateMan } } - if (!pfrom->fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr && pfrom->m_tx_relay != nullptr) { + if (!pfrom->fDisconnect && IsOutboundDisconnectionCandidate(pfrom) && nodestate->pindexBestKnownBlock != nullptr && !pfrom->m_block_relay_only_peer) { // 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 @@ -2693,7 +2696,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec // 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) { + if (!pfrom->m_block_relay_only_peer) { LOCK(pfrom->m_tx_relay->cs_filter); pfrom->m_tx_relay->fRelayTxes = fRelay; // set to true after we get the first filter* message } @@ -2766,6 +2769,8 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec // At this point, the outgoing message serialization version can't change. const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); + bool fBlocksOnly = pfrom->IsBlockRelayOnly(); + if (msg_type == NetMsgType::VERACK) { pfrom->SetRecvVersion(std::min(pfrom->nVersion.load(), PROTOCOL_VERSION)); @@ -2777,7 +2782,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec 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"); + pfrom->m_block_relay_only_peer ? "block-relay" : "full-relay"); } if (!pfrom->m_masternode_probe_connection) { @@ -2802,10 +2807,11 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec } // Tell our peer that he should send us CoinJoin queue messages - connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDDSQUEUE, true)); - - if (llmq::CLLMQUtils::IsWatchQuorumsEnabled() && !pfrom->m_masternode_connection) { - connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::QWATCH)); + if (!fBlocksOnly) { + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDDSQUEUE, true)); + if (llmq::CLLMQUtils::IsWatchQuorumsEnabled() && !pfrom->m_masternode_connection) { + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::QWATCH)); + } } pfrom->fSuccessfullyConnected = true; @@ -2847,6 +2853,13 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec } } + // Stop processing non-block data early in blocks only mode and for block-relay-only peers + if (fBlocksOnly && NetMessageViolatesBlocksOnly(msg_type)) { + LogPrint(BCLog::NET, "%s sent in violation of protocol peer=%d\n", msg_type, pfrom->GetId()); + pfrom->fDisconnect = true; + return false; + } + if (msg_type == NetMsgType::ADDR || msg_type == NetMsgType::ADDRV2) { int stream_version = vRecv.GetVersion(); if (msg_type == NetMsgType::ADDRV2) { @@ -2964,14 +2977,6 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec return false; } - // 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)) - fBlocksOnly = false; - LOCK(cs_main); const auto current_time = GetTime(); @@ -3027,11 +3032,22 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec }; pfrom->AddInventoryKnown(inv); - if (fBlocksOnly) { - LogPrint(BCLog::NET, "transaction (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.hash.ToString(), pfrom->GetId()); + if (fBlocksOnly && NetMessageViolatesBlocksOnly(inv.GetCommand())) { + LogPrint(BCLog::NET, "%s (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.GetCommand(), inv.hash.ToString(), pfrom->GetId()); pfrom->fDisconnect = true; return true; } else if (!fAlreadyHave) { + if (fBlocksOnly && (inv.type == MSG_ISLOCK || inv.type == MSG_ISDLOCK)) { + if (pfrom->GetRecvVersion() <= ADDRV2_PROTO_VERSION) { + // It's ok to receive these invs, we just ignore them + // and do not request corresponding objects. + continue; + } + // Peers with newer versions should never send us these invs when we are in blocks-relay-only mode + LogPrint(BCLog::NET, "%s (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.GetCommand(), inv.hash.ToString(), pfrom->GetId()); + pfrom->fDisconnect = true; + return true; + } bool allowWhileInIBD = allowWhileInIBDObjs.count(inv.type); if (allowWhileInIBD || !chainman.ActiveChainstate().IsInitialBlockDownload()) { RequestObject(State(pfrom->GetId()), inv, current_time); @@ -3266,16 +3282,6 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec } if (msg_type == NetMsgType::TX || msg_type == NetMsgType::DSTX || msg_type == NetMsgType::LEGACYTXLOCKREQUEST) { - // Stop processing the transaction early if - // 1) We are in blocks only mode and peer has no relay permission - // 2) This peer is 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; - } - CTransactionRef ptx; CCoinJoinBroadcastTx dstx; int nInvType = MSG_TX; @@ -3862,7 +3868,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec return true; } - if (pfrom->m_tx_relay != nullptr) { + if (!pfrom->m_block_relay_only_peer) { LOCK(pfrom->m_tx_relay->cs_tx_inventory); pfrom->m_tx_relay->fSendMempool = true; } @@ -3953,7 +3959,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); } - else if (pfrom->m_tx_relay != nullptr) + else if (!pfrom->m_block_relay_only_peer) { LOCK(pfrom->m_tx_relay->cs_filter); pfrom->m_tx_relay->pfilter.reset(new CBloomFilter(filter)); @@ -3972,7 +3978,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec bool bad = false; if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) { bad = true; - } else if (pfrom->m_tx_relay != nullptr) { + } else if (!pfrom->m_block_relay_only_peer) { LOCK(pfrom->m_tx_relay->cs_filter); if (pfrom->m_tx_relay->pfilter) { pfrom->m_tx_relay->pfilter->insert(vData); @@ -3988,7 +3994,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec } if (msg_type == NetMsgType::FILTERCLEAR) { - if (pfrom->m_tx_relay == nullptr) { + if (pfrom->m_block_relay_only_peer) { return true; } LOCK(pfrom->m_tx_relay->cs_filter); @@ -4365,7 +4371,7 @@ void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds) // 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 (pnode->m_block_relay_only_peer) 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; @@ -4724,8 +4730,8 @@ bool PeerLogicValidation::SendMessages(CNode* pto) LOCK2(m_mempool.cs, pto->cs_inventory); size_t reserve = INVENTORY_BROADCAST_MAX_PER_1MB_BLOCK * MaxBlockSize() / 1000000; - if (pto->m_tx_relay != nullptr) { - LOCK(pto->m_tx_relay->cs_filter); + if (!pto->m_block_relay_only_peer) { + LOCK(pto->m_tx_relay->cs_tx_inventory); reserve = std::min(pto->m_tx_relay->setInventoryTxToSend.size(), reserve); } reserve = std::max(reserve, pto->vInventoryBlockToSend.size()); @@ -4743,10 +4749,8 @@ bool PeerLogicValidation::SendMessages(CNode* pto) pto->vInventoryBlockToSend.clear(); auto queueAndMaybePushInv = [this, pto, &vInv, &msgMaker](const CInv& invIn) { - if (pto->m_tx_relay != nullptr) { - AssertLockHeld(pto->m_tx_relay->cs_tx_inventory); - pto->m_tx_relay->filterInventoryKnown.insert(invIn.hash); - } + AssertLockHeld(pto->m_tx_relay->cs_tx_inventory); + pto->m_tx_relay->filterInventoryKnown.insert(invIn.hash); LogPrint(BCLog::NET, "SendMessages -- queued inv: %s index=%d peer=%d\n", invIn.ToString(), vInv.size(), pto->GetId()); vInv.push_back(invIn); if (vInv.size() == MAX_INV_SZ) { @@ -4756,7 +4760,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto) } }; - if (pto->m_tx_relay != nullptr) { + if (!pto->m_block_relay_only_peer) { LOCK(pto->m_tx_relay->cs_tx_inventory); // Check whether periodic sends should happen // Note: If this node is running in a Masternode mode, it makes no sense to delay outgoing txes @@ -4865,21 +4869,27 @@ bool PeerLogicValidation::SendMessages(CNode* pto) queueAndMaybePushInv(CInv(nInvType, hash)); } } + } + { // Send non-tx/non-block inventory items - for (const auto& inv : pto->vInventoryOtherToSend) { + LOCK2(pto->m_tx_relay->cs_tx_inventory, pto->m_tx_relay->cs_filter); + + bool fSendIS = pto->m_tx_relay->fRelayTxes && !pto->IsBlockRelayOnly(); + + for (const auto& inv : pto->m_tx_relay->vInventoryOtherToSend) { + if (!pto->m_tx_relay->fRelayTxes && NetMessageViolatesBlocksOnly(inv.GetCommand())) { + continue; + } if (pto->m_tx_relay->filterInventoryKnown.contains(inv.hash)) { continue; } + if (!fSendIS && (inv.type == MSG_ISLOCK || inv.type == MSG_ISDLOCK)) { + continue; + } queueAndMaybePushInv(inv); } - pto->vInventoryOtherToSend.clear(); - } else { // m_tx_relay is nullptr but we still need to send items from `vInventoryOtherToSend` - // Send non-tx/non-block inventory items - for (const auto& inv : pto->vInventoryOtherToSend) { - queueAndMaybePushInv(inv); - } - pto->vInventoryOtherToSend.clear(); + pto->m_tx_relay->vInventoryOtherToSend.clear(); } } if (!vInv.empty()) diff --git a/src/protocol.cpp b/src/protocol.cpp index df1171475e..45f9782ff8 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -174,6 +174,34 @@ const static std::string allNetMessageTypes[] = { NetMsgType::HEADERS2}; const static std::vector allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes)); +const static std::string netMessageTypesViolateBlocksOnly[] = { + NetMsgType::DSACCEPT, + NetMsgType::DSCOMPLETE, + NetMsgType::DSFINALTX, + NetMsgType::DSQUEUE, + NetMsgType::DSSIGNFINALTX, + NetMsgType::DSSTATUSUPDATE, + NetMsgType::DSTX, + NetMsgType::DSVIN, + NetMsgType::LEGACYTXLOCKREQUEST, + NetMsgType::QBSIGSHARES, + NetMsgType::QCOMPLAINT, + NetMsgType::QCONTRIB, + NetMsgType::QDATA, + NetMsgType::QGETDATA, + NetMsgType::QGETSIGSHARES, + NetMsgType::QJUSTIFICATION, + NetMsgType::QPCOMMITMENT, + NetMsgType::QSENDRECSIGS, + NetMsgType::QSIGREC, + NetMsgType::QSIGSESANN, + NetMsgType::QSIGSHARE, + NetMsgType::QSIGSHARESINV, + NetMsgType::QWATCH, + NetMsgType::TX, +}; +const static std::set netMessageTypesViolateBlocksOnlySet(netMessageTypesViolateBlocksOnly, netMessageTypesViolateBlocksOnly+ARRAYLEN(netMessageTypesViolateBlocksOnly)); + CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn) { memcpy(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE); @@ -337,6 +365,11 @@ const std::vector &getAllNetMessageTypes() return allNetMessageTypesVec; } +bool NetMessageViolatesBlocksOnly(const std::string& msg_type) +{ + return netMessageTypesViolateBlocksOnlySet.find(msg_type) != netMessageTypesViolateBlocksOnlySet.end(); +} + /** * Convert a service flag (NODE_*) to a human readable string. * It supports unknown service flags which will be returned as "UNKNOWN[...]". diff --git a/src/protocol.h b/src/protocol.h index afa9c3df3a..fe7220422c 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -306,6 +306,9 @@ extern const char *QUORUMROTATIONINFO; /* Get a vector of all valid message types (see above) */ const std::vector &getAllNetMessageTypes(); +/* Whether the message type violates blocks-relay-only policy */ +bool NetMessageViolatesBlocksOnly(const std::string& msg_type); + /** nServices flags */ enum ServiceFlags : uint64_t { // NOTE: When adding here, be sure to update serviceFlagToStr too diff --git a/test/functional/p2p_blocksonly.py b/test/functional/p2p_blocksonly.py index 3c2a56ad35..b0cc091b1d 100755 --- a/test/functional/p2p_blocksonly.py +++ b/test/functional/p2p_blocksonly.py @@ -40,7 +40,7 @@ class P2PBlocksOnly(BitcoinTestFramework): }], )['hex'] 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(['tx sent in violation of protocol peer=0']): self.nodes[0].p2p.send_message(msg_tx(FromHex(CTransaction(), sigtx))) self.nodes[0].p2p.wait_for_disconnect() assert_equal(self.nodes[0].getmempoolinfo()['size'], 0)