diff options
Diffstat (limited to 'src/net_processing.cpp')
| -rw-r--r-- | src/net_processing.cpp | 151 |
1 files changed, 132 insertions, 19 deletions
diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 50a8a8a88..1df1fab59 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -8,9 +8,11 @@ #include <addrman.h> #include <banman.h> #include <blockencodings.h> +#include <blockfilter.h> #include <chainparams.h> #include <consensus/validation.h> #include <hash.h> +#include <index/blockfilterindex.h> #include <validation.h> #include <merkleblock.h> #include <netmessagemaker.h> @@ -127,6 +129,8 @@ static constexpr unsigned int INVENTORY_BROADCAST_MAX = 7 * INVENTORY_BROADCAST_ static constexpr unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60; /** Maximum feefilter broadcast delay after significant change. */ static constexpr unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60; +/** Interval between compact filter checkpoints. See BIP 157. */ +static constexpr int CFCHECKPT_INTERVAL = 1000; struct COrphanTx { // When modifying, adapt the copy of this definition in tests/DoS_tests. @@ -1612,26 +1616,32 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm std::vector<CInv> vNotFound; const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); - // Note that if we receive a getdata for a MSG_TX or MSG_WITNESS_TX from a - // block-relay-only outbound peer, we will stop processing further getdata - // messages from this peer (likely resulting in our peer eventually - // disconnecting us). - if (pfrom->m_tx_relay != nullptr) { - // mempool entries added before this time have likely expired from mapRelay - const std::chrono::seconds longlived_mempool_time = GetTime<std::chrono::seconds>() - RELAY_TX_CACHE_TIME; - const std::chrono::seconds mempool_req = pfrom->m_tx_relay->m_last_mempool_req.load(); + // mempool entries added before this time have likely expired from mapRelay + const std::chrono::seconds longlived_mempool_time = GetTime<std::chrono::seconds>() - RELAY_TX_CACHE_TIME; + // Get last mempool request time + const std::chrono::seconds mempool_req = pfrom->m_tx_relay != nullptr ? pfrom->m_tx_relay->m_last_mempool_req.load() + : std::chrono::seconds::min(); + { LOCK(cs_main); + // Process as many TX items from the front of the getdata queue as + // possible, since they're common and it's efficient to batch process + // them. while (it != pfrom->vRecvGetData.end() && (it->type == MSG_TX || it->type == MSG_WITNESS_TX)) { if (interruptMsgProc) return; - // Don't bother if send buffer is too full to respond anyway + // The send buffer provides backpressure. If there's no space in + // the buffer, pause processing until the next call. if (pfrom->fPauseSend) break; - const CInv &inv = *it; - it++; + const CInv &inv = *it++; + + if (pfrom->m_tx_relay == nullptr) { + // Ignore GETDATA requests for transactions from blocks-only peers. + continue; + } // Send stream from relay memory bool push = false; @@ -1665,19 +1675,17 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm } } // release cs_main + // Only process one BLOCK item per call, since they're uncommon and can be + // expensive to process. if (it != pfrom->vRecvGetData.end() && !pfrom->fPauseSend) { - const CInv &inv = *it; + const CInv &inv = *it++; if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK) { - it++; ProcessGetBlockData(pfrom, chainparams, inv, connman); } + // else: If the first item on the queue is an unknown type, we erase it + // and continue processing the queue on the next call. } - // Unknown types in the GetData stay in vRecvGetData and block any future - // message from this peer, see vRecvGetData check in ProcessMessages(). - // Depending on future p2p changes, we might either drop unknown getdata on - // the floor or disconnect the peer. - pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it); if (!vNotFound.empty()) { @@ -1965,6 +1973,107 @@ void static ProcessOrphanTx(CConnman* connman, CTxMemPool& mempool, std::set<uin } } +/** + * Validation logic for compact filters request handling. + * + * May disconnect from the peer in the case of a bad request. + * + * @param[in] pfrom The peer that we received the request from + * @param[in] chain_params Chain parameters + * @param[in] filter_type The filter type the request is for. Must be basic filters. + * @param[in] stop_hash The stop_hash for the request + * @param[out] stop_index The CBlockIndex for the stop_hash block, if the request can be serviced. + * @param[out] filter_index The filter index, if the request can be serviced. + * @return True if the request can be serviced. + */ +static bool PrepareBlockFilterRequest(CNode* pfrom, const CChainParams& chain_params, + BlockFilterType filter_type, + const uint256& stop_hash, + const CBlockIndex*& stop_index, + const BlockFilterIndex*& filter_index) +{ + const bool supported_filter_type = + (filter_type == BlockFilterType::BASIC && + gArgs.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS)); + if (!supported_filter_type) { + LogPrint(BCLog::NET, "peer %d requested unsupported block filter type: %d\n", + pfrom->GetId(), static_cast<uint8_t>(filter_type)); + pfrom->fDisconnect = true; + return false; + } + + { + LOCK(cs_main); + stop_index = LookupBlockIndex(stop_hash); + + // Check that the stop block exists and the peer would be allowed to fetch it. + if (!stop_index || !BlockRequestAllowed(stop_index, chain_params.GetConsensus())) { + LogPrint(BCLog::NET, "peer %d requested invalid block hash: %s\n", + pfrom->GetId(), stop_hash.ToString()); + pfrom->fDisconnect = true; + return false; + } + } + + filter_index = GetBlockFilterIndex(filter_type); + if (!filter_index) { + LogPrint(BCLog::NET, "Filter index for supported type %s not found\n", BlockFilterTypeName(filter_type)); + return false; + } + + return true; +} + +/** + * Handle a getcfcheckpt request. + * + * May disconnect from the peer in the case of a bad request. + * + * @param[in] pfrom The peer that we received the request from + * @param[in] vRecv The raw message received + * @param[in] chain_params Chain parameters + * @param[in] connman Pointer to the connection manager + */ +static void ProcessGetCFCheckPt(CNode* pfrom, CDataStream& vRecv, const CChainParams& chain_params, + CConnman* connman) +{ + uint8_t filter_type_ser; + uint256 stop_hash; + + vRecv >> filter_type_ser >> stop_hash; + + const BlockFilterType filter_type = static_cast<BlockFilterType>(filter_type_ser); + + const CBlockIndex* stop_index; + const BlockFilterIndex* filter_index; + if (!PrepareBlockFilterRequest(pfrom, chain_params, filter_type, stop_hash, + stop_index, filter_index)) { + return; + } + + std::vector<uint256> headers(stop_index->nHeight / CFCHECKPT_INTERVAL); + + // Populate headers. + const CBlockIndex* block_index = stop_index; + for (int i = headers.size() - 1; i >= 0; i--) { + int height = (i + 1) * CFCHECKPT_INTERVAL; + block_index = block_index->GetAncestor(height); + + if (!filter_index->LookupFilterHeader(block_index, headers[i])) { + LogPrint(BCLog::NET, "Failed to find block filter header in index: filter_type=%s, block_hash=%s\n", + BlockFilterTypeName(filter_type), block_index->GetBlockHash().ToString()); + return; + } + } + + CSerializedNetMsg msg = CNetMsgMaker(pfrom->GetSendVersion()) + .Make(NetMsgType::CFCHECKPT, + filter_type_ser, + stop_index->GetBlockHash(), + headers); + connman->PushMessage(pfrom, std::move(msg)); +} + bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CTxMemPool& mempool, CConnman* connman, BanMan* banman, const std::atomic<bool>& interruptMsgProc) { LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(msg_type), vRecv.size(), pfrom->GetId()); @@ -3216,7 +3325,6 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec { LOCK(pfrom->m_tx_relay->cs_filter); pfrom->m_tx_relay->pfilter.reset(new CBloomFilter(filter)); - pfrom->m_tx_relay->pfilter->UpdateEmptyFull(); pfrom->m_tx_relay->fRelayTxes = true; } return true; @@ -3271,6 +3379,11 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec return true; } + if (msg_type == NetMsgType::GETCFCHECKPT) { + ProcessGetCFCheckPt(pfrom, vRecv, chainparams, connman); + return true; + } + if (msg_type == NetMsgType::NOTFOUND) { // Remove the NOTFOUND transactions from the peer LOCK(cs_main); |