/* * This file is part of the Flowee project * Copyright (c) 2009-2010 Satoshi Nakamoto * Copyright (c) 2009-2015 The Bitcoin Core developers * Copyright (C) 2017-2024 Tom Zander * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "main.h" #include "addrman.h" #include "blocklocator.h" #include "chainparams.h" #include "consensus/consensus.h" #include "merkleblock.h" #include "consensus/validation.h" #include "DoubleSpendProof.h" #include "DoubleSpendProofStorage.h" #include "UiInterface.h" #include "init.h" #include "serverutil.h" #include "timedata.h" #include "txmempool.h" #include "txorphancache.h" #include "thinblock.h" #include "validation/Engine.h" #include #include #include #include #include #include #include #include "validationinterface.h" #include #include #include #include // for Blocks::BlockFileInfo #include #include #include /** * Global state */ CCriticalSection cs_main; CChain chainActive; CBlockIndex *pindexBestHeader = nullptr; CWaitableCriticalSection csBestBlock; CConditionVariable cvBlockChange; bool fIsBareMultisigStd = Settings::DefaultPermitBareMultisig; bool fRequireStandard = true; bool fCheckpointsEnabled = Settings::DefaultCheckpointsEnabled; /** Fees smaller than this (in satoshi) are considered zero fee (for relaying, mining and transaction creation) */ CFeeRate minRelayTxFee = CFeeRate(Settings::DefaultMinRelayTxFee); CTxMemPool mempool; const std::string strMessageMagic = "Bitcoin Signed Message:\n"; CCriticalSection cs_LastBlockFile; std::vector vinfoBlockFile; int nLastBlockFile = 0; /** Dirty block file entries. */ std::set setDirtyFileInfo; // Internal stuff namespace { struct CBlockIndexWorkComparator { bool operator()(CBlockIndex *pa, CBlockIndex *pb) const { // First sort by most total work, ... if (pa->nChainWork > pb->nChainWork) return false; if (pa->nChainWork < pb->nChainWork) return true; // ... then by earliest time received, ... if (pa->nSequenceId < pb->nSequenceId) return false; if (pa->nSequenceId > pb->nSequenceId) return true; // Use pointer address as tie breaker (should only happen with blocks // loaded from disk, as those all have id 0). if (pa < pb) return false; if (pa > pb) return true; // Identical blocks. return false; } }; /** Number of nodes with fSyncStarted. */ int nSyncStarted = 0; /** * Every received block is assigned a unique and increasing identifier, so we * know which one to give priority in case of a fork. */ CCriticalSection cs_nBlockSequenceId; /** Blocks loaded from disk are assigned id 0, so start the counter at 1. */ uint32_t nBlockSequenceId = 1; /** * Sources of received blocks, saved to be able to send them reject * messages or ban them when processing happens afterwards. Protected by * cs_main. */ std::map mapBlockSource; /** * Filter for transactions that were recently rejected by * AcceptToMemoryPool. These are not rerequested until the chain tip * changes, at which point the entire filter is reset. Protected by * cs_main. * * Without this filter we'd be re-requesting txs from each of our peers, * increasing bandwidth consumption considerably. For instance, with 100 * peers, half of which relay a tx we don't accept, that might be a 50x * bandwidth increase. A flooding attacker attempting to roll-over the * filter using minimum-sized, 60byte, transactions might manage to send * 1000/sec if we have fast peers, so we pick 120,000 to give our peers a * two minute window to send invs to us. * * Decreasing the false positive rate is fairly cheap, so we pick one in a * million to make it highly unlikely for users to have issues with this * filter. * * Memory used: 1.7MB */ boost::scoped_ptr recentRejects; uint256 hashRecentRejectsChainTip; /** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */ struct QueuedBlock { uint256 hash; CBlockIndex* pindex; //!< Optional. bool fValidatedHeaders; //!< Whether this block has validated headers at the time of request. }; std::map::iterator> > mapBlocksInFlight; /** Number of preferable block download peers. */ int nPreferredDownload = 0; /** Dirty block index entries. The block index instances not yet persisted to (index) DB */ std::set setDirtyBlockIndex; /** Number of peers from which we're downloading blocks. */ int nPeersWithValidatedDownloads = 0; } // anon namespace ////////////////////////////////////////////////////////////////////////////// // // Registration of network node signals. // namespace { struct CBlockReject { unsigned char chRejectCode; std::string strRejectReason; uint256 hashBlock; }; /** * Maintain validation-specific state about nodes, protected by cs_main, instead * by CNode's own locks. This simplifies asynchronous operation, where * processing of incoming data is done after the ProcessMessage call returns, * and we're no longer holding the node's locks. */ struct CNodeState { //! The peer's address CService address; //! Whether we have a fully established connection. bool fCurrentlyConnected; //! Accumulated misbehaviour score for this peer. int nMisbehavior; //! Whether this peer should be disconnected and banned (unless whitelisted). bool fShouldBan; //! List of asynchronously-determined block rejections to notify this peer about. std::vector rejects; //! The best known block we know this peer has announced. CBlockIndex *pindexBestKnownBlock; //! The hash of the last unknown block this peer has announced. uint256 hashLastUnknownBlock; //! The last full block we both have. CBlockIndex *pindexLastCommonBlock; //! The best header we have sent our peer. CBlockIndex *pindexBestHeaderSent; //! Whether we've started headers synchronization with this peer. bool fSyncStarted; //! Since when we're stalling block download progress (in microseconds), or 0. int64_t nStallingSince; std::list vBlocksInFlight; //! When the first entry in vBlocksInFlight started downloading. Don't care when vBlocksInFlight is empty. int64_t nDownloadingSince; int nBlocksInFlight; int nBlocksInFlightValidHeaders; //! Whether we consider this a preferred download peer. bool fPreferredDownload; //! Whether this peer wants invs or headers (when possible) for block announcements. bool fPreferHeaders; CNodeState() { fCurrentlyConnected = false; nMisbehavior = 0; fShouldBan = false; pindexBestKnownBlock = nullptr; hashLastUnknownBlock.SetNull(); pindexLastCommonBlock = nullptr; pindexBestHeaderSent = nullptr; ResetASERTAnchorBlockCache(); fSyncStarted = false; nStallingSince = 0; nDownloadingSince = 0; nBlocksInFlight = 0; nBlocksInFlightValidHeaders = 0; fPreferredDownload = false; fPreferHeaders = false; } }; /** Map maintaining per-node state. Requires cs_main. */ std::map mapNodeState; // Requires cs_main. CNodeState *State(NodeId pnode) { std::map::iterator it = mapNodeState.find(pnode); if (it == mapNodeState.end()) return nullptr; return &it->second; } int GetHeight() { LOCK(cs_main); return chainActive.Height(); } void UpdatePreferredDownload(CNode* node, CNodeState* state) { nPreferredDownload -= state->fPreferredDownload; // Whether this node should be marked as a preferred download node. state->fPreferredDownload = (!node->fInbound || node->fWhitelisted) && !node->fOneShot && !node->fClient; nPreferredDownload += state->fPreferredDownload; } void InitializeNode(NodeId nodeid, const CNode *pnode) { LOCK(cs_main); CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second; state.address = pnode->addr; } void FinalizeNode(NodeId nodeid) { LOCK(cs_main); CNodeState *state = State(nodeid); assert(state); if (state->fSyncStarted) nSyncStarted--; if (state->nMisbehavior == 0 && state->fCurrentlyConnected) { AddressCurrentlyConnected(state->address); } for (const QueuedBlock& entry : state->vBlocksInFlight) { mapBlocksInFlight.erase(entry.hash); } nPreferredDownload -= state->fPreferredDownload; nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0); assert(nPeersWithValidatedDownloads >= 0); mapNodeState.erase(nodeid); if (mapNodeState.empty()) { // Do a consistency check after the last peer is removed. assert(mapBlocksInFlight.empty()); assert(nPreferredDownload == 0); assert(nPeersWithValidatedDownloads == 0); } } } // Requires cs_main. // Returns a bool indicating whether we requested this block. bool MarkBlockAsReceived(const uint256& hash) { std::map::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); if (itInFlight != mapBlocksInFlight.end()) { CNodeState *state = State(itInFlight->second.first); state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders; if (state->nBlocksInFlightValidHeaders == 0 && itInFlight->second.second->fValidatedHeaders) { // Last validated block on the queue was received. nPeersWithValidatedDownloads--; } if (state->vBlocksInFlight.begin() == itInFlight->second.second) { // First block on the queue was received, update the start download time for the next one state->nDownloadingSince = std::max(state->nDownloadingSince, GetTimeMicros()); } state->vBlocksInFlight.erase(itInFlight->second.second); state->nBlocksInFlight--; state->nStallingSince = 0; mapBlocksInFlight.erase(itInFlight); return true; } return false; } bool IsBlockInFlight(const uint256 &hash) { return mapBlocksInFlight.count(hash) > 0; } namespace { // Requires cs_main. void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params&, CBlockIndex *pindex = nullptr) { CNodeState *state = State(nodeid); assert(state != nullptr); // Make sure it's not listed somewhere already. MarkBlockAsReceived(hash); QueuedBlock newentry = {hash, pindex, pindex != nullptr}; std::list::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); state->nBlocksInFlight++; state->nBlocksInFlightValidHeaders += newentry.fValidatedHeaders; if (state->nBlocksInFlight == 1) { // We're starting a block download (batch) from this peer. state->nDownloadingSince = GetTimeMicros(); } if (state->nBlocksInFlightValidHeaders == 1 && pindex != nullptr) { nPeersWithValidatedDownloads++; } mapBlocksInFlight[hash] = std::make_pair(nodeid, it); } /** Check whether the last unknown block a peer advertised is not yet known. */ void ProcessBlockAvailability(NodeId nodeid) { CNodeState *state = State(nodeid); assert(state != nullptr); if (!state->hashLastUnknownBlock.IsNull()) { auto bi = Blocks::Index::get(state->hashLastUnknownBlock); if (bi && bi->nChainWork > 0) { if (state->pindexBestKnownBlock == nullptr || bi->nChainWork >= state->pindexBestKnownBlock->nChainWork) state->pindexBestKnownBlock = bi; state->hashLastUnknownBlock.SetNull(); } } } /** Update tracking information about which blocks a peer is assumed to have. */ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { CNodeState *state = State(nodeid); assert(state != nullptr); ProcessBlockAvailability(nodeid); auto bi = Blocks::Index::get(hash); if (bi && bi->nChainWork > 0) { // An actually better block was announced. if (state->pindexBestKnownBlock == nullptr || bi->nChainWork >= state->pindexBestKnownBlock->nChainWork) state->pindexBestKnownBlock = bi; } else { // An unknown block was announced; just assume that the latest one is the best one. state->hashLastUnknownBlock = hash; } } // Requires cs_main /// returns true if the Hub has as 'tip' a chain that is close in time to 'now'. bool CanDirectFetch(const Consensus::Params &consensusParams) { return chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - consensusParams.nPowTargetSpacing * 20; } // Requires cs_main bool PeerHasHeader(CNodeState *state, CBlockIndex *pindex) { if (state->pindexBestKnownBlock && pindex == state->pindexBestKnownBlock->GetAncestor(pindex->nHeight)) return true; if (state->pindexBestHeaderSent && pindex == state->pindexBestHeaderSent->GetAncestor(pindex->nHeight)) return true; return false; } /** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has * at most count entries. */ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector& vBlocks, NodeId& nodeStaller) { if (count == 0) return; vBlocks.reserve(vBlocks.size() + count); CNodeState *state = State(nodeid); assert(state != nullptr); // Make sure pindexBestKnownBlock is up to date, we'll need it. ProcessBlockAvailability(nodeid); if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork) { // This peer has nothing interesting. return; } if (state->pindexLastCommonBlock == nullptr) { // Bootstrap quickly by guessing a parent of our best tip is the forking point. // Guessing wrong in either direction is not a problem. state->pindexLastCommonBlock = chainActive[std::min(state->pindexBestKnownBlock->nHeight, chainActive.Height())]; } // If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor // of its current tip anymore. Go back enough to fix that. state->pindexLastCommonBlock = Blocks::Index::lastCommonAncestor(state->pindexLastCommonBlock, state->pindexBestKnownBlock); if (state->pindexLastCommonBlock == state->pindexBestKnownBlock) return; std::vector vToFetch; CBlockIndex *pindexWalk = state->pindexLastCommonBlock; // Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last // linked block we have in common with this peer. The +1 is so we can detect stalling, namely if we would be able to // download that next block if the window were 1 larger. int nWindowEnd = state->pindexLastCommonBlock->nHeight + BLOCK_DOWNLOAD_WINDOW; int nMaxHeight = std::min(state->pindexBestKnownBlock->nHeight, nWindowEnd + 1); NodeId waitingfor = -1; while (pindexWalk->nHeight < nMaxHeight) { // Read up to 128 (or more, if more blocks than that are needed) successors of pindexWalk (towards // pindexBestKnownBlock) into vToFetch. We fetch 128, because CBlockIndex::GetAncestor may be as expensive // as iterating over ~100 CBlockIndex* entries anyway. int nToFetch = std::min(nMaxHeight - pindexWalk->nHeight, std::max(count - vBlocks.size(), 128)); vToFetch.resize(nToFetch); pindexWalk = state->pindexBestKnownBlock->GetAncestor(pindexWalk->nHeight + nToFetch); vToFetch[nToFetch - 1] = pindexWalk; for (unsigned int i = nToFetch - 1; i > 0; i--) { vToFetch[i - 1] = vToFetch[i]->pprev; } // Iterate over those blocks in vToFetch (in forward direction), adding the ones that // are not yet downloaded and not in flight to vBlocks. In the mean time, update // pindexLastCommonBlock as long as all ancestors are already downloaded, or if it's // already part of our chain (and therefore don't need it even if pruned). for (CBlockIndex* pindex : vToFetch) { if (!pindex->IsValid(BLOCK_VALID_TREE)) { // We consider the chain that this peer is on invalid. return; } if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) { if (pindex->nChainTx) state->pindexLastCommonBlock = pindex; } else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) { // The block is not already downloaded, and not yet in flight. if (pindex->nHeight > nWindowEnd) { // We reached the end of the window. if (vBlocks.size() == 0 && waitingfor != nodeid) { // We aren't able to fetch anything, but we would be if the download window was one larger. nodeStaller = waitingfor; } return; } vBlocks.push_back(pindex); if (vBlocks.size() == count) { return; } } else if (waitingfor == -1) { // This is the first already-in-flight block. waitingfor = mapBlocksInFlight[pindex->GetBlockHash()].first; } } } } } // anon namespace bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { LOCK(cs_main); CNodeState *state = State(nodeid); if (state == nullptr) return false; stats.nMisbehavior = state->nMisbehavior; stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1; stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1; for (const QueuedBlock& queue : state->vBlocksInFlight) { if (queue.pindex) stats.vHeightInFlight.push_back(queue.pindex->nHeight); } return true; } void RegisterNodeSignals(CNodeSignals& nodeSignals) { nodeSignals.GetHeight.connect(&GetHeight); nodeSignals.ProcessMessages.connect(&ProcessMessages); nodeSignals.SendMessages.connect(&SendMessages); nodeSignals.InitializeNode.connect(&InitializeNode); nodeSignals.FinalizeNode.connect(&FinalizeNode); } void UnregisterNodeSignals(CNodeSignals& nodeSignals) { nodeSignals.GetHeight.disconnect(&GetHeight); nodeSignals.ProcessMessages.disconnect(&ProcessMessages); nodeSignals.SendMessages.disconnect(&SendMessages); nodeSignals.InitializeNode.disconnect(&InitializeNode); nodeSignals.FinalizeNode.disconnect(&FinalizeNode); } CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) { // Find the first block the caller has in the main chain for (const uint256& hash : locator.vHave) { auto pindex = Blocks::Index::get(hash); if (pindex) { if (chain.Contains(pindex)) return pindex; } } return chain.Genesis(); } bool IsFinalTx(const Tx &tx, int nBlockHeight, int64_t nBlockTime) { Tx::Iterator iter(tx); bool sequenceFailed = false; while (iter.next(Tx::LockTime | Tx::Sequence) != Tx::End) { if (iter.tag() == Tx::LockTime) { auto lockTime = iter.uintData(); if (lockTime == 0) return true; if ((int64_t)lockTime < ((int64_t)lockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) return true; } else if (!sequenceFailed && iter.tag() == Tx::Sequence) { auto sequence = iter.uintData(); if (sequence != CTxIn::SEQUENCE_FINAL) sequenceFailed = true; } } return !sequenceFailed; } bool CheckFinalTx(const Tx &tx, int flags) { AssertLockHeld(cs_main); // By convention a negative value for flags indicates that the // current network-enforced consensus rules should be used. In // a future soft-fork scenario that would mean checking which // rules would be enforced for the next block and setting the // appropriate flags. flags = std::max(flags, 0); // CheckFinalTx() uses chainActive.Height()+1 to evaluate // nLockTime because when IsFinalTx() is called within // CBlock::AcceptBlock(), the height of the block *being* // evaluated is what is used. Thus if we want to know if a // transaction can be part of the *next* block, we need to call // IsFinalTx() with one more than chainActive.Height(). const int nBlockHeight = chainActive.Height() + 1; // BIP113 will require that time-locked transactions have nLockTime set to // less than the median time of the previous block they're contained in. // When the next block is created its previous block will be the current // chain tip, so we use that to calculate the median time passed to // IsFinalTx() if LOCKTIME_MEDIAN_TIME_PAST is set. const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST) ? chainActive.Tip()->GetMedianTimePast() : GetAdjustedTime(); return IsFinalTx(tx, nBlockHeight, nBlockTime); } /** * Calculates the block height and previous block's median time past at * which the transaction will be considered final in the context of BIP 68. * Also removes from the vector of input heights any entries which did not * correspond to sequence locked inputs as they do not affect the calculation. */ static std::pair CalculateSequenceLocks(const CTransaction &tx, int flags, std::vector* prevHeights, const CBlockIndex& block) { assert(prevHeights->size() == tx.vin.size()); // Will be set to the equivalent height- and time-based nLockTime // values that would be necessary to satisfy all relative lock- // time constraints given our view of block chain history. // The semantics of nLockTime are the last invalid height/time, so // use -1 to have the effect of any height or time being valid. int nMinHeight = -1; int64_t nMinTime = -1; // tx.nVersion is signed integer so requires cast to unsigned otherwise // we would be doing a signed comparison and half the range of nVersion // wouldn't support BIP 68. bool fEnforceBIP68 = static_cast(tx.nVersion) >= 2 && flags & LOCKTIME_VERIFY_SEQUENCE; // Do not enforce sequence numbers as a relative lock time // unless we have been instructed to if (!fEnforceBIP68) { return std::make_pair(nMinHeight, nMinTime); } for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) { const CTxIn& txin = tx.vin[txinIndex]; // Sequence numbers with the most significant bit set are not // treated as relative lock-times, nor are they given any // consensus-enforced meaning at this point. if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) { // The height of this input is not relevant for sequence locks (*prevHeights)[txinIndex] = 0; continue; } int nCoinHeight = (*prevHeights)[txinIndex]; if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) { int64_t nCoinTime = block.GetAncestor(std::max(nCoinHeight-1, 0))->GetMedianTimePast(); // NOTE: Subtract 1 to maintain nLockTime semantics // BIP 68 relative lock times have the semantics of calculating // the first block or time at which the transaction would be // valid. When calculating the effective block time or height // for the entire transaction, we switch to using the // semantics of nLockTime which is the last invalid block // time or height. Thus we subtract 1 from the calculated // time or height. // Time-based relative lock-times are measured from the // smallest allowed timestamp of the block containing the // txout being spent, which is the median time past of the // block prior. nMinTime = std::max(nMinTime, nCoinTime + (int64_t)((txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_MASK) << CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) - 1); } else { nMinHeight = std::max(nMinHeight, nCoinHeight + (int)(txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_MASK) - 1); } } return std::make_pair(nMinHeight, nMinTime); } static bool EvaluateSequenceLocks(const CBlockIndex& block, std::pair lockPair) { assert(block.pprev); int64_t nBlockTime = block.pprev->GetMedianTimePast(); if (lockPair.first >= block.nHeight || lockPair.second >= nBlockTime) return false; return true; } bool SequenceLocks(const CTransaction &tx, int flags, std::vector* prevHeights, const CBlockIndex& block) { return EvaluateSequenceLocks(block, CalculateSequenceLocks(tx, flags, prevHeights, block)); } bool TestLockPointValidity(const LockPoints* lp) { AssertLockHeld(cs_main); assert(lp); // If there are relative lock times then the maxInputBlock will be set // If there are no relative lock times, the LockPoints don't depend on the chain if (lp->maxInputBlock) { // Check whether chainActive is an extension of the block at which the LockPoints // calculation was valid. If not LockPoints are no longer valid if (!chainActive.Contains(lp->maxInputBlock)) { return false; } } // LockPoints still valid return true; } bool CheckSequenceLocks(CTxMemPool &mp, const CTransaction &tx, int flags, LockPoints* lp, bool useExistingLockPoints, CBlockIndex *tip) { if (!tip) { AssertLockHeld(cs_main); tip = chainActive.Tip(); } CBlockIndex index; index.pprev = tip; // CheckSequenceLocks() uses chainActive.Height()+1 to evaluate // height based locks because when SequenceLocks() is called within // ConnectBlock(), the height of the block *being* // evaluated is what is used. // Thus if we want to know if a transaction can be part of the // *next* block, we need to use one more than chainActive.Height() index.nHeight = tip->nHeight + 1; std::pair lockPair; if (useExistingLockPoints) { assert(lp); lockPair.first = lp->height; lockPair.second = lp->time; } else { std::vector prevheights; prevheights.resize(tx.vin.size()); for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) { const CTxIn& txin = tx.vin[txinIndex]; Tx prevTx; if (mp.lookup(txin.prevout.hash, prevTx)) { // Assume all mempool transaction confirm in the next block prevheights[txinIndex] = tip->nHeight + 1; } else { // try UTXO UnspentOutput output = g_utxo->find(txin.prevout.hash, txin.prevout.n); if (!output.isValid()) { logCritical(Log::BlockValidation) << __func__ << "Missing input"; return false; } prevheights[txinIndex] = output.blockHeight(); } } lockPair = CalculateSequenceLocks(tx, flags, &prevheights, index); if (lp) { lp->height = lockPair.first; lp->time = lockPair.second; // Also store the hash of the block with the highest height of // all the blocks which have sequence locked prevouts. // This hash needs to still be on the chain // for these LockPoint calculations to be valid // Note: It is impossible to correctly calculate a maxInputBlock // if any of the sequence locked inputs depend on unconfirmed txs, // except in the special case where the relative lock time/height // is 0, which is equivalent to no sequence lock. Since we assume // input height of tip+1 for mempool txs and test the resulting // lockPair from CalculateSequenceLocks against tip+1. We know // EvaluateSequenceLocks will fail if there was a non-zero sequence // lock on a mempool input, so we can use the return value of // CheckSequenceLocks to indicate the LockPoints validity int maxInputHeight = 0; for (int height : prevheights) { // Can ignore mempool inputs since we'll fail if they had non-zero locks if (height != tip->nHeight+1) { maxInputHeight = std::max(maxInputHeight, height); } } lp->maxInputBlock = tip->GetAncestor(maxInputHeight); } } return EvaluateSequenceLocks(index, lockPair); } void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) { int expired = pool.Expire(GetTime() - age); if (expired != 0) LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired); pool.TrimToSize(limit, nullptr); } ////////////////////////////////////////////////////////////////////////////// // // CBlock and CBlockIndex // bool ReadBlockFromDisk(MutableBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams) { block.setNull(); // Open history file to read Block fb = Blocks::DB::instance()->loadBlock(pos); if (fb.size() == 0) { LogPrintf("ReadBlockFromDisk: Unable to open file %d\n", pos.nFile); return false; } // Read block try { block = fb.createOldBlock(); } catch (const std::exception& e) { return error("%s: Deserialize or I/O error - %s at %s", __func__, e.what(), pos.ToString()); } // Check the header if (!CheckProofOfWork(block.createHash(), block.nBits, consensusParams)) return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString()); return true; } bool ReadBlockFromDisk(MutableBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams) { if (!ReadBlockFromDisk(block, pindex->GetBlockPos(), consensusParams)) return false; if (block.createHash() != pindex->GetBlockHash()) return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s", pindex->ToString(), pindex->GetBlockPos().ToString()); return true; } int64_t GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) { int halvings = nHeight / consensusParams.nSubsidyHalvingInterval; // Force block reward to zero when right shift is undefined. if (halvings >= 64) return 0; int64_t nSubsidy = 50 * COIN; // Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years. nSubsidy >>= halvings; return nSubsidy; } bool IsInitialBlockDownload() { if (Blocks::DB::instance()->isReindexing()) return true; return Blocks::DB::instance()->headerChain().Height() - chainActive.Height() > 1000; } void AlertNotify(const std::string& strMessage, bool fThread) { uiInterface.NotifyAlertChanged(); std::string strCmd = GetArg("-alertnotify", ""); if (strCmd.empty()) return; // Alert text should be plain ascii coming from a trusted source, but to // be safe we first strip anything not in safeChars, then add single quotes around // the whole string before passing it to the shell: std::string singleQuote("'"); std::string safeStatus = SanitizeString(strMessage); safeStatus = singleQuote+safeStatus+singleQuote; boost::replace_all(strCmd, "%s", safeStatus); if (fThread) boost::thread t(runCommand, strCmd); // thread runs free else runCommand(strCmd); } // Requires cs_main. void Misbehaving(NodeId nodeId, int howmuch) { if (howmuch == 0) return; CNodeState *state = State(nodeId); if (state == nullptr) return; state->nMisbehavior += howmuch; int banscore = GetArg("-banscore", Settings::DefaultBanscoreThreshold); if (!state->fShouldBan && state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore) { logCritical(Log::Net) << "Id:" << nodeId << state->nMisbehavior-howmuch << "=>" << state->nMisbehavior << "Ban threshold exceeded"; state->fShouldBan = true; addrman.increaseUselessness(state->address, 2); Application::instance()->validation()->nodeBanned(nodeId); } else { logWarning(Log::Net) << "Misbehaving" << "Id:" << nodeId << state->nMisbehavior-howmuch << "=>" << state->nMisbehavior; } } void queueRejectMessage(int peerId, const uint256 &blockHash, uint8_t rejectCode, const std::string &rejectReason) { LOCK(cs_main); auto state = State(peerId); if (state) { CBlockReject reject = {rejectCode, rejectReason.substr(0, MAX_REJECT_MESSAGE_LENGTH), blockHash}; state->rejects.push_back(reject); } } /** Abort with a message */ bool AbortNode(const std::string& strMessage, const std::string& userMessage="") { strMiscWarning = strMessage; LogPrintf("*** %s\n", strMessage); uiInterface.ThreadSafeMessageBox( userMessage.empty() ? _("Error: A fatal internal error occurred, see hub.log for details") : userMessage, "", CClientUIInterface::MSG_ERROR); StartShutdown(); return false; } bool AbortNode(CValidationState& state, const std::string& strMessage, const std::string& userMessage="") { AbortNode(strMessage, userMessage); return state.Error(strMessage); } bool FlushStateToDisk(CValidationState &state, FlushStateMode mode) { LOCK2(cs_main, cs_LastBlockFile); static int64_t nLastWrite = 0; static int64_t nLastFlush = 0; static int64_t nLastSetChain = 0; try { int64_t nNow = GetTimeMicros(); // Avoid writing/flushing immediately after startup. if (nLastWrite == 0) { nLastWrite = nNow; } if (nLastFlush == 0) { nLastFlush = nNow; } if (nLastSetChain == 0) { nLastSetChain = nNow; } // It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash. bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000; // It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage. bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000; // Combine all conditions that result in a full cache flush. bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fPeriodicFlush; // Write blocks and block index to disk. if (fDoFullFlush || fPeriodicWrite) { // Depend on nMinDiskSpace to ensure we can write block index if (!CheckDiskSpace(0)) return state.Error("out of disk space"); // First make sure all block and undo data is flushed to disk. // Then update all block file information (which may refer to block and undo files). { std::vector > vFiles; vFiles.reserve(setDirtyFileInfo.size()); for (std::set::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) { vFiles.push_back(std::make_pair(*it, &vinfoBlockFile[*it])); setDirtyFileInfo.erase(it++); } std::vector vBlocks; vBlocks.reserve(setDirtyBlockIndex.size()); for (std::set::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) { vBlocks.push_back(*it); setDirtyBlockIndex.erase(it++); } if (Blocks::DB::instance()) { // only when we actually finished init if (!Blocks::DB::instance()->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) return AbortNode(state, "Files to write to block index database"); } } nLastWrite = nNow; } // Flush best chain related state. This can only be done if the blocks / block index write was also done. if (fDoFullFlush) { if (!CheckDiskSpace(50000000)) return state.Error("out of disk space"); nLastFlush = nNow; } if (fDoFullFlush || ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000)) { // Update best block in wallet (so we can detect restored wallets). ValidationNotifier().setBestChain(chainActive.GetLocator()); nLastSetChain = nNow; } } catch (const std::runtime_error& e) { return AbortNode(state, std::string("System error while flushing: ") + e.what()); } return true; } void FlushStateToDisk() { CValidationState state; FlushStateToDisk(state, FLUSH_STATE_ALWAYS); } bool CheckBlockHeader(const BlockHeader &block, CValidationState& state, bool fCheckPOW) { // Check proof of work matches claimed amount if (fCheckPOW && !CheckProofOfWork(block.createHash(), block.nBits, Params().GetConsensus())) return state.DoS(50, error("CheckBlockHeader(): proof of work failed"), REJECT_INVALID, "high-hash"); // Check timestamp if (block.blockTime() > GetAdjustedTime() + 2 * 60 * 60) return state.Invalid(error("CheckBlockHeader(): block timestamp too far in the future"), REJECT_INVALID, "time-too-new"); return true; } bool CheckDiskSpace(uint64_t nAdditionalBytes) { uint64_t nFreeBytesAvailable = boost::filesystem::space(GetDataDir()).available; // Check for nMinDiskSpace bytes (currently 50MB) if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) return AbortNode("Disk space is low!", _("Error: Disk space is low!")); return true; } bool LoadBlockIndexDB(const UnspentOutputDatabase *utxo) { if (!Blocks::DB::instance()->CacheAllBlockInfos(utxo)) return false; // Load block file info if (Blocks::DB::instance()->ReadLastBlockFile(nLastBlockFile)) { vinfoBlockFile.resize(nLastBlockFile + 1); logInfo(Log::DB) << "last block file:" << nLastBlockFile; for (int nFile = 0; nFile <= nLastBlockFile; nFile++) { Blocks::DB::instance()->ReadBlockFileInfo(nFile, vinfoBlockFile[nFile]); } logInfo(Log::DB) << "last block file info:" << vinfoBlockFile[nLastBlockFile].ToString(); for (int nFile = nLastBlockFile + 1; true; nFile++) { Blocks::BlockFileInfo info; if (Blocks::DB::instance()->ReadBlockFileInfo(nFile, info)) { vinfoBlockFile.push_back(info); } else { break; } } } // Check presence of blk files logInfo(Log::DB) << "Checking all blk files are present..."; std::set setBlkDataFiles = Blocks::Index::fileIndexes(); int count = 0; auto blocksDb = Blocks::DB::instance(); for (std::set::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++) { Streaming::ConstBuffer dataFile = blocksDb->loadBlockFile(*it); if (!dataFile.isValid()) return false; // garbage collect every so many times to avoid hitting FD limits. if ((++count % 100) == 0) blocksDb->closeFiles(); } return true; } void UnloadBlockIndex() { LOCK(cs_main); chainActive.SetTip(nullptr); pindexBestHeader = nullptr; mempool.clear(); CTxOrphanCache::clear(); nSyncStarted = 0; vinfoBlockFile.clear(); nLastBlockFile = 0; nBlockSequenceId = 1; mapBlockSource.clear(); mapBlocksInFlight.clear(); nPreferredDownload = 0; setDirtyBlockIndex.clear(); setDirtyFileInfo.clear(); mapNodeState.clear(); recentRejects.reset(nullptr); Blocks::Index::unload(); } bool InitBlockIndex(const CChainParams& chainparams) { // Initialize global variables that cannot be constructed at startup. recentRejects.reset(new CRollingBloomFilter(120000, 0.000001)); // Check whether we're already initialized if (chainActive.Genesis() != nullptr) return true; logCritical(Log::Bitcoin) << "Initializing databases..."; // Only add the genesis block if not reindexing (in which case we reuse the one already on disk) if (!Blocks::DB::instance()->isReindexing()) { auto *bv = Application::instance()->validation(); auto future = bv->addBlock(chainparams.GenesisBlock(), Validation::SaveGoodToDisk).start(); future.waitUntilFinished(); if (!future.error().empty()) { logCritical(Log::Bitcoin) << "Failed to add the genesisblock due to:" << future.error(); return false; } // Force a chainstate write so that when we VerifyDB in a moment, it doesn't check stale data CValidationState state; return FlushStateToDisk(state, FLUSH_STATE_ALWAYS); } return true; } std::string GetWarnings(const std::string& strFor) { std::string strStatusBar; std::string strRPC; std::string strGUI; if (!CLIENT_VERSION_IS_RELEASE) { strStatusBar = "This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"; strGUI = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"); } if (GetBoolArg("-testsafemode", Settings::DefaultTestSafeMode)) strStatusBar = strRPC = strGUI = "testsafemode enabled"; // Misc warnings like out of disk space and clock is wrong if (strMiscWarning != "") { strStatusBar = strGUI = strMiscWarning; } if (strFor == "gui") return strGUI; else if (strFor == "statusbar") return strStatusBar; else if (strFor == "rpc") return strRPC; assert(!"GetWarnings(): invalid parameter"); return "error"; } ////////////////////////////////////////////////////////////////////////////// // // Messages // bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { switch (inv.type) { case MSG_TX: { auto validation = flApp->validation(); if (validation->isRecentlyRejectedTransaction(inv.hash)) return true; return validation->mempool()->exists(inv.hash); } case MSG_BLOCK: return Blocks::Index::exists(inv.hash); case MSG_DOUBLESPENDPROOF: return mempool.doubleSpendProofStorage()->exists(inv.hash) || mempool.doubleSpendProofStorage()->isRecentlyRejectedProof(inv.hash); } // Don't know what it is, just say we already got one return true; } void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams) { logDebug(106) << pfrom->vRecvGetData.size(); std::deque::iterator it = pfrom->vRecvGetData.begin(); std::vector vNotFound; LOCK(cs_main); while (it != pfrom->vRecvGetData.end()) { // Don't bother if send buffer is too full to respond anyway if (pfrom->nSendSize >= SendBufferSize()) break; const CInv &inv = *it; logDebug(106) << " + handling" << inv.ToString(); { boost::this_thread::interruption_point(); it++; if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_THINBLOCK || inv.type == MSG_XTHINBLOCK) { bool send = false; auto mi = Blocks::Index::get(inv.hash); if (mi) { if (chainActive.Contains(mi)) { send = true; } else { static const int nOneMonth = 30 * 24 * 60 * 60; // To prevent fingerprinting attacks, only send blocks outside of the active // chain if they are valid, and no more than a month older (both in time, and in // best equivalent proof of work) than the best header chain we know about. send = mi->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - mi->GetBlockTime() < nOneMonth) && (GetBlockProofEquivalentTime(*pindexBestHeader, *mi, *pindexBestHeader, consensusParams) < nOneMonth); if (!send) { logDebug(Log::Net) << "ProcessGetData ignoring request from peer" << pfrom->GetId() << "for old block that isn't in the main chain"; } } } // disconnect node in case we have reached the outbound limit for serving historical blocks // never disconnect whitelisted nodes static const int nOneWeek = 7 * 24 * 60 * 60; // assume > 1 week = historical if (send && CNode::OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - mi->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted) { logCritical(Log::Net) << "historical block serving limit reached, disconnect peer" << pfrom->GetId(); //disconnect node pfrom->fDisconnect = true; send = false; } // Pruned nodes may have deleted the block, so check whether // it's available before trying to send. if (send && (mi->nStatus & BLOCK_HAVE_DATA)) { logDebug(107) << " requested block available"; // Send block from disk MutableBlock block; if (!ReadBlockFromDisk(block, mi, consensusParams)) assert(!"cannot load block from disk"); bool sendFullBlock = true; if (inv.type == MSG_XTHINBLOCK) { CXThinBlock xThinBlock(block, pfrom->pThinBlockFilter); if (!xThinBlock.collision) { const int nSizeBlock = ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION); // Only send a thinblock if smaller than a regular block const int nSizeThinBlock = ::GetSerializeSize(xThinBlock, SER_NETWORK, PROTOCOL_VERSION); if (nSizeThinBlock < nSizeBlock) { pfrom->PushMessage(NetMsgType::XTHINBLOCK, xThinBlock); sendFullBlock = false; logInfo(Log::ThinBlocks) << "Sent xthinblock - size:" << nSizeThinBlock << "vs block size:" << nSizeBlock << "=> tx hashes:" << xThinBlock.vTxHashes.size() << "transactions:" << xThinBlock.vMissingTx.size() << "peerid" << pfrom->id; } } } else if (inv.type == MSG_FILTERED_BLOCK) { LOCK(pfrom->cs_filter); if (pfrom->pfilter) { CMerkleBlock merkleBlock(block, *pfrom->pfilter); pfrom->PushMessage(NetMsgType::MERKLEBLOCK, merkleBlock); // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see // This avoids hurting performance by pointlessly requiring a round-trip // Note that there is currently no way for a node to request any single transactions we didn't send here - // they must either disconnect and retry or request the full block. // Thus, the protocol spec specified allows for us to provide duplicate txn here, // however we MUST always provide at least what the remote peer needs typedef std::pair PairType; for (PairType& pair : merkleBlock.vMatchedTxn) pfrom->PushMessage(NetMsgType::TX, block.vtx[pair.first]); sendFullBlock = false; } } if (sendFullBlock) // if none of the other methods were actually executed; pfrom->PushMessage(NetMsgType::BLOCK, block); // Trigger the peer node to send a getblocks request for the next batch of inventory if (inv.hash == pfrom->hashContinue) { // Bypass PushInventory, this must send even if redundant, // and we want it right after the last block so they don't // wait for other stuff first. std::vector vInv; vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); pfrom->PushMessage(NetMsgType::INV, vInv); pfrom->hashContinue.SetNull(); } } } else if (inv.IsKnownType()) { // Send stream from relay memory bool pushed = false; { LOCK(cs_mapRelay); std::map::iterator mi = mapRelay.find(inv); if (mi != mapRelay.end()) { pfrom->PushMessage(inv.GetCommand(), (*mi).second); pushed = true; } } if (!pushed && inv.type == MSG_TX) { CTransaction tx; if (mempool.lookup(inv.hash, tx)) { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss.reserve(1000); ss << tx; pfrom->PushMessage(NetMsgType::TX, ss); pushed = true; } } else if (inv.type == MSG_DOUBLESPENDPROOF) { DoubleSpendProof dsp = mempool.doubleSpendProofStorage()->lookup(inv.hash); if (!dsp.isEmpty()) { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss.reserve(600); ss << dsp; pfrom->PushMessage(NetMsgType::DSPROOF, ss); pushed = true; } } if (!pushed) vNotFound.push_back(inv); } // Track requests for our stuff. ValidationNotifier().inventory(inv.hash); if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_THINBLOCK || inv.type == MSG_XTHINBLOCK) break; } } pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it); if (!vNotFound.empty()) { // Let the peer know that we didn't find what it asked for, so it doesn't // have to wait around forever. Currently only SPV clients actually care // about this message: it's needed when they are recursively walking the // dependencies of relevant unconfirmed transactions. SPV clients want to // do that because they want to know about (and store and rebroadcast and // risk analyze) the dependencies of transactions relevant to them, without // having to download the entire memory pool. pfrom->PushMessage(NetMsgType::NOTFOUND, vNotFound); } } bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vRecv, int64_t nTimeReceived) { const CChainParams& chainparams = Params(); RandAddSeedPerfmon(); const bool fReindex = Blocks::DB::instance()->isReindexing(); logDebug(Log::Net) << "received:" << SanitizeString(strCommand) << "bytes:" << vRecv.size() << "peer:" << pfrom->id; if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); return true; } const bool xthinEnabled = IsThinBlocksEnabled(); if (strCommand == NetMsgType::VERSION) { // Each connection can only send one version message if (pfrom->nVersion != 0) { pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, std::string("Duplicate version message")); Misbehaving(pfrom->GetId(), 10); return false; } int64_t nTime; CAddress addrMe; CAddress addrFrom; uint64_t nNonce = 1; int nVersion; vRecv >> nVersion >> pfrom->nServices >> nTime >> addrMe; if (nVersion < MIN_PEER_PROTO_VERSION) { // disconnect from peers older than this proto version logWarning(Log::Net) << "peer:" << pfrom->id << "using obsolete version" << nVersion << "disconnecting"; pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION)); pfrom->fDisconnect = true; addrman.increaseUselessness(pfrom->addr, 2); return false; } if (nVersion == 10300) nVersion = 300; if (!vRecv.empty()) { vRecv >> addrFrom; vRecv >> nNonce; } if (!vRecv.empty()) { vRecv >> LIMITED_STRING(pfrom->strSubVer, MAX_SUBVERSION_LENGTH); pfrom->cleanSubVer = SanitizeString(pfrom->strSubVer); } if (!vRecv.empty()) vRecv >> pfrom->nStartingHeight; if (!vRecv.empty()) vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message else pfrom->fRelayTxes = true; // only approve connection as the whole version message parsed correctly. pfrom->nVersion = nVersion; // Disconnect if we connected to ourself if (nNonce == nLocalHostNonce && nNonce > 1) { logCritical(Log::Net) << "connected to self at" << pfrom->addr << "disconnecting"; pfrom->fDisconnect = true; return true; } pfrom->addrLocal = addrMe; if (pfrom->fInbound && addrMe.IsRoutable()) { SeenLocal(addrMe); } // Be shy and don't send version until we hear if (pfrom->fInbound) pfrom->PushVersion(); pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); // Potentially mark this peer as a preferred download peer. UpdatePreferredDownload(pfrom, State(pfrom->GetId())); // Change version pfrom->PushMessage(NetMsgType::VERACK); pfrom->ssSend.SetVersion(std::min(pfrom->nVersion, PROTOCOL_VERSION)); if (!pfrom->fInbound) { // Advertise our address if (fListen && !IsInitialBlockDownload()) { CAddress addr = GetLocalAddress(&pfrom->addr); if (addr.IsRoutable()) { logInfo(Log::Net) << "ProcessMessages: advertising address" << addr; pfrom->PushAddress(addr); } else if (IsPeerAddrLocalGood(pfrom)) { addr.SetIP(pfrom->addrLocal); logInfo(Log::Net) << "ProcessMessages: advertising address" << addr; pfrom->PushAddress(addr); } } // Get recent addresses if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000) { pfrom->PushMessage(NetMsgType::GETADDR); pfrom->fGetAddr = true; } addrman.Good(pfrom->addr); } else { if (((CNetAddr)pfrom->addr) == (CNetAddr)addrFrom) { addrman.Add(addrFrom, addrFrom); addrman.Good(addrFrom); } } pfrom->fSuccessfullyConnected = true; logCritical(Log::Net) << "receive VERSION message:" << pfrom->cleanSubVer << "version:" << pfrom->nVersion << "blocks:" << pfrom->nStartingHeight << "id:" << pfrom->id << pfrom->addr; int64_t nTimeOffset = nTime - GetTime(); pfrom->nTimeOffset = nTimeOffset; AddTimeData(pfrom->addr, nTimeOffset); } else if (pfrom->nVersion == 0) { // Must have a version message before anything else pfrom->fDisconnect = true; return false; } else if (strCommand == NetMsgType::VERACK) { pfrom->SetRecvVersion(std::min(pfrom->nVersion, PROTOCOL_VERSION)); // Mark this node as currently connected, so we update its timestamp later. if (pfrom->fNetworkNode) { LOCK(cs_main); State(pfrom->GetId())->fCurrentlyConnected = true; } if (pfrom->nVersion >= SENDHEADERS_VERSION) { // Tell our peer we prefer to receive headers rather than inv's // We send this to non-NODE NETWORK peers as well, because even // non-NODE NETWORK peers can announce blocks (such as pruning // nodes) // BUIP010 Extreme Thinblocks: We only do inv/getdata for xthinblocks and so we must have headersfirst turned off if (!xthinEnabled) pfrom->PushMessage(NetMsgType::SENDHEADERS); } } else if (strCommand == NetMsgType::ADDR && (pfrom->nServices & NODE_BITCOIN_CASH)) { std::vector vAddr; vRecv >> vAddr; // Don't want addr from older versions unless seeding if (pfrom->nVersion < CADDR_TIME_VERSION && addrman.size() > 1000) return true; if (vAddr.size() > 1000) { Misbehaving(pfrom->GetId(), 20); return error("message addr size() = %u", vAddr.size()); } // Store the new addresses std::vector vAddrOk; int64_t nNow = GetAdjustedTime(); int64_t nSince = nNow - 10 * 60; for (CAddress& addr : vAddr) { boost::this_thread::interruption_point(); if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) addr.nTime = nNow - 5 * 24 * 60 * 60; pfrom->AddAddressKnown(addr); bool fReachable = IsReachable(addr); if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable()) { // Relay to a limited number of other nodes { LOCK(cs_vNodes); // Use deterministic randomness to send to the same nodes for 24 hours // at a time so the addrKnowns of the chosen nodes prevent repeats static uint256 hashSalt; if (hashSalt.IsNull()) hashSalt = GetRandHash(); uint64_t hashAddr = addr.GetHash(); uint256 hashRand = ArithToUint256(UintToArith256(hashSalt) ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60))); hashRand = Hash(BEGIN(hashRand), END(hashRand)); std::multimap mapMix; for (CNode* pnode : vNodes) { if (pnode->nVersion < CADDR_TIME_VERSION) continue; unsigned int nPointer; memcpy(&nPointer, &pnode, sizeof(nPointer)); uint256 hashKey = ArithToUint256(UintToArith256(hashRand) ^ nPointer); hashKey = Hash(BEGIN(hashKey), END(hashKey)); mapMix.insert(std::make_pair(hashKey, pnode)); } int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s) for (std::multimap::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) ((*mi).second)->PushAddress(addr); } } // Do not store addresses outside our network if (fReachable) vAddrOk.push_back(addr); } addrman.Add(vAddrOk, pfrom->addr, 2 * 60 * 60); if (vAddr.size() < 1000) pfrom->fGetAddr = false; if (pfrom->fOneShot) pfrom->fDisconnect = true; } else if (strCommand == NetMsgType::SENDHEADERS) { LOCK(cs_main); // BUIP010 Xtreme Thinblocks: We only do inv/getdata for xthinblocks and so we must have headersfirst turned off if (xthinEnabled) State(pfrom->GetId())->fPreferHeaders = false; else State(pfrom->GetId())->fPreferHeaders = true; } else if (strCommand == NetMsgType::INV) { std::vector vInv; vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { Misbehaving(pfrom->GetId(), 20); return error("message inv size() = %u", vInv.size()); } bool fBlocksOnly = GetBoolArg("-blocksonly", Settings::DefaultBlocksOnly); // When catching up, avoid accepting transactions before we reach the tip, since they could get blacklisted. if (Blocks::DB::instance()->headerChain().Height() - chainActive.Height() > 6) fBlocksOnly = true; // Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true else if (pfrom->fWhitelisted && GetBoolArg("-whitelistrelay", Settings::DefaultWhitelistRelay)) fBlocksOnly = false; LOCK(cs_main); std::vector vToFetch; for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { const CInv &inv = vInv[nInv]; pfrom->AddInventoryKnown(inv); bool fAlreadyHave = AlreadyHave(inv); logDebug(Log::Net) << "got inv:" << inv << (fAlreadyHave ? "have." : "new.") << "Peer:" << pfrom->id; if (inv.type == MSG_BLOCK) { UpdateBlockAvailability(pfrom->GetId(), inv.hash); if (!fAlreadyHave && !fReindex && !mapBlocksInFlight.count(inv.hash)) { // First request the headers preceding the announced block. In the normal fully-synced // case where a new block is announced that succeeds the current tip (no reorganization), // there are no such headers. // Secondly, and only when we are close to being synced, we request the announced block directly, // to avoid an extra round-trip. Note that we must *first* ask for the headers, so by the // time the block arrives, the header chain leading up to it is already validated. Not // doing this will result in the received block being rejected as an orphan in case it is // not a direct successor. pfrom->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash); CNodeState *nodestate = State(pfrom->GetId()); if (CanDirectFetch(chainparams.GetConsensus()) && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { // BUIP010 Xtreme Thinblocks: begin section CInv inv2(inv); CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); if (xthinEnabled && IsChainNearlySyncd()) { if (HaveThinblockNodes() && CheckThinblockTimer(inv.hash)) { // Must download a block from a ThinBlock peer if (pfrom->mapThinBlocksInFlight.size() < 1 && pfrom->ThinBlockCapable()) { // We can only send one thinblock per peer at a time pfrom->mapThinBlocksInFlight[inv2.hash] = GetTime(); inv2.type = MSG_XTHINBLOCK; CBloomFilter filterMemPool = createSeededBloomFilter(CTxOrphanCache::instance()->fetchTransactionIds()); ss << inv2; ss << filterMemPool; pfrom->PushMessage(NetMsgType::GET_XTHIN, ss); MarkBlockAsInFlight(pfrom->GetId(), inv.hash, chainparams.GetConsensus()); LogPrint("thin", "Requesting Thinblock %s from peer %s (%d)\n", inv2.hash.ToString(), pfrom->addrName.c_str(),pfrom->id); } } else { // Try to download a thinblock if possible otherwise just download a regular block if (pfrom->mapThinBlocksInFlight.size() < 1 && pfrom->ThinBlockCapable()) { // We can only send one thinblock per peer at a time pfrom->mapThinBlocksInFlight[inv2.hash] = GetTime(); inv2.type = MSG_XTHINBLOCK; CBloomFilter filterMemPool = createSeededBloomFilter(CTxOrphanCache::instance()->fetchTransactionIds()); ss << inv2; ss << filterMemPool; pfrom->PushMessage(NetMsgType::GET_XTHIN, ss); LogPrint("thin", "Requesting Thinblock %s from peer %s (%d)\n", inv2.hash.ToString(), pfrom->addrName.c_str(),pfrom->id); } else { LogPrint("thin", "Requesting Regular Block %s from peer %s (%d)\n", inv2.hash.ToString(), pfrom->addrName.c_str(),pfrom->id); vToFetch.push_back(inv2); } MarkBlockAsInFlight(pfrom->GetId(), inv.hash, chainparams.GetConsensus()); } } else { vToFetch.push_back(inv2); MarkBlockAsInFlight(pfrom->GetId(), inv.hash, chainparams.GetConsensus()); LogPrint("thin", "Requesting Regular Block %s from peer %s (%d)\n", inv2.hash.ToString(), pfrom->addrName.c_str(),pfrom->id); } // BUIP010 Xtreme Thinblocks: end section } logDebug(Log::Net) << "getheaders" << pindexBestHeader->nHeight << inv.hash << " to peer:" << pfrom->id; } } else if ((inv.type == MSG_DOUBLESPENDPROOF || inv.type == MSG_TX) && !fBlocksOnly && !fAlreadyHave && !fReindex) { pfrom->AskFor(inv); } // Track requests for our stuff ValidationNotifier().inventory(inv.hash); if (pfrom->nSendSize > (SendBufferSize() * 2)) { Misbehaving(pfrom->GetId(), 50); return error("send buffer size() = %u", pfrom->nSendSize); } } if (!vToFetch.empty()) pfrom->PushMessage(NetMsgType::GETDATA, vToFetch); } else if (strCommand == NetMsgType::GETDATA) { std::vector vInv; vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { Misbehaving(pfrom->GetId(), 20); return error("message getdata size() = %u", vInv.size()); } if (vInv.size() != 1) logDebug(Log::Net) << "received getdata (" << vInv.size() << "invsz) peer:" << pfrom->id; if ((vInv.size() > 0) || (vInv.size() == 1)) logDebug(Log::Net) << "received getdata for:" << vInv[0].ToString() << "peer:" << pfrom->id; pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); ProcessGetData(pfrom, chainparams.GetConsensus()); } else if (strCommand == NetMsgType::GETBLOCKS) { CBlockLocator locator; uint256 hashStop; vRecv >> locator >> hashStop; LOCK(cs_main); // Find the last block the caller has in the main chain CBlockIndex* pindex = FindForkInGlobalIndex(chainActive, locator); // Send the rest of the chain if (pindex) pindex = chainActive.Next(pindex); int nLimit = 500; LogPrint("net", "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom->id); for (; pindex; pindex = chainActive.Next(pindex)) { if (pindex->GetBlockHash() == hashStop) { LogPrint("net", " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); break; } pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); if (--nLimit <= 0) { // When this block is requested, we'll send an inv that'll // trigger the peer to getblocks the next batch of inventory. LogPrint("net", " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); pfrom->hashContinue = pindex->GetBlockHash(); break; } } } else if (strCommand == NetMsgType::GETHEADERS) { CBlockLocator locator; uint256 hashStop; vRecv >> locator >> hashStop; LOCK(cs_main); if (IsInitialBlockDownload() && !pfrom->fWhitelisted) { logDebug(Log::Net) << "Ignoring getheaders from peer:" <id << "because node is in initial block download"; return true; } CNodeState *nodestate = State(pfrom->GetId()); CBlockIndex* pindex = nullptr; if (locator.IsNull()) { // If locator is null, return the hashStop block pindex = Blocks::Index::get(hashStop); if (!pindex) return true; } else { // Find the last block the caller has in the main chain pindex = FindForkInGlobalIndex(chainActive, locator); if (pindex) { if (pindex->nStatus & BLOCK_FAILED_MASK) { // his TIP is one we rejected. We don't like them. Misbehaving(pfrom->GetId(), 100); return error("peer follows a different chain."); } pindex = chainActive.Next(pindex); } } // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end std::vector vHeaders; int nLimit = MAX_HEADERS_RESULTS; logDebug(Log::Net) << "getheaders" << (pindex ? pindex->nHeight : -1) << "to" << hashStop << "from peer:" << pfrom->id; for (; pindex; pindex = chainActive.Next(pindex)) { vHeaders.push_back(pindex->GetBlockHeader()); if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) break; } // pindex can be nullptr either if we sent chainActive.Tip() OR // if our peer has chainActive.Tip() (and thus we are sending an empty // headers message). In both cases it's safe to update // pindexBestHeaderSent to be our tip. nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip(); pfrom->PushMessage(NetMsgType::HEADERS, vHeaders); } else if (strCommand == NetMsgType::TX) { // Stop processing the transaction early if // We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off if (GetBoolArg("-blocksonly", Settings::DefaultBlocksOnly) && (!pfrom->fWhitelisted || !GetBoolArg("-whitelistrelay", Settings::DefaultWhitelistRelay))) { LogPrint("net", "transaction sent in violation of protocol peer=%d\n", pfrom->id); return true; } CTransaction tx; vRecv >> tx; CInv inv(MSG_TX, tx.GetHash()); pfrom->AddInventoryKnown(inv); pfrom->setAskFor.erase(inv.hash); { LOCK(cs_main); mapAlreadyAskedFor.erase(inv.hash); } uint32_t opts = 0; if (!pfrom->fWhitelisted) opts += Validation::PunishBadNode + Validation::RateLimitFreeTx; flApp->validation()->addTransaction(Tx::fromOldTransaction(tx), opts, pfrom); CValidationState val; if (!FlushStateToDisk(val, FLUSH_STATE_PERIODIC)) AbortNode(val.GetRejectReason().c_str()); } else if (strCommand == NetMsgType::HEADERS && !fReindex) // Ignore headers received while importing { std::vector headers; // Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks. unsigned int nCount = ReadCompactSize(vRecv); if (nCount > MAX_HEADERS_RESULTS) { Misbehaving(pfrom->GetId(), 20); return error("headers message size = %u", nCount); } headers.resize(nCount); for (unsigned int n = 0; n < nCount; n++) { vRecv >> headers[n]; ReadCompactSize(vRecv); // ignore tx count; assume it is 0. } if (nCount == 0) { // Nothing interesting. Stop asking this peers for more headers. return true; } auto *engine = Application::instance()->validation(); Streaming::BufferPool pool(100 * nCount); std::list futures; for (const BlockHeaderFields& header : headers) { auto block = Block::fromOldBlock(header, &pool); futures.push_back(engine->addBlock(block, 0, pfrom).start()); } for (const Validation::Settings &future : futures) { future.waitHeaderFinished(); if (!future.error().empty()) { logWarning(Log::Net) << "HEADERS received from peer:" << pfrom->GetId() << "returned:" << future.error(); if (!pfrom->fWhitelisted) { LOCK(cs_main); Misbehaving(pfrom->GetId(), Settings::DefaultBanscoreThreshold); } return false; } } CBlockIndex *pindexLast = futures.back().blockIndex(); assert(pindexLast); UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); if (nCount == MAX_HEADERS_RESULTS) { // Headers message had its maximum size; the peer may have more headers. // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue // from there instead. logDebug(Log::Net).nospace() << "more getheaders (" << pindexLast->nHeight << ") to end to peer=" << pfrom->id << "(startheight:" << pfrom->nStartingHeight << ")"; pfrom->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256()); } LOCK(cs_main); bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus()); CNodeState *nodestate = State(pfrom->GetId()); // If this set of headers is valid and ends in a block with at least as // much work as our tip, download as much as possible. logDebug(106) << "canDirectFetch" << fCanDirectFetch << "tree" << pindexLast->IsValid(BLOCK_VALID_TREE) << "more chain work:" << (chainActive.Tip()->nChainWork <= pindexLast->nChainWork); if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && chainActive.Tip()->nChainWork <= pindexLast->nChainWork) { std::vector vToFetch; CBlockIndex *pindexWalk = pindexLast; // Calculate all the blocks we'd need to switch to pindexLast, up to a limit. while (pindexWalk && !chainActive.Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { logDebug(106) << "starting fetch" << pindexWalk->nHeight; if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) && !mapBlocksInFlight.count(pindexWalk->GetBlockHash())) { // We don't have this block, and it's not yet in flight. vToFetch.push_back(pindexWalk); } pindexWalk = pindexWalk->pprev; } logDebug(106) << " first block that has data;" << pindexWalk->nHeight; logDebug(106) << " fetch" << vToFetch; // If pindexWalk still isn't on our main chain, we're looking at a // very large reorg at a time we think we're close to caught up to // the main chain -- this shouldn't really happen. Bail out on the // direct fetch and rely on parallel download instead. if (!chainActive.Contains(pindexWalk)) { logWarning(Log::Net) << "Large reorg, won't direct fetch to" << pindexLast->GetBlockHash() << "at height:" << pindexLast->nHeight; } else if (!(xthinEnabled && pfrom->nServices & NODE_XTHIN)) { // xthin based downloads are done elsewhere std::vector vGetData; // Download as much as possible, from earliest to latest. BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vToFetch) { if (nodestate->nBlocksInFlight >= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { // Can't download any more from this peer break; } vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), chainparams.GetConsensus(), pindex); logDebug(Log::Net) << "Requesting block" << pindex->GetBlockHash() << "from peer:" << pfrom->id; } if (vGetData.size() > 1) { logDebug(Log::Net) << "Downloading blocks toward" << pindexLast->GetBlockHash() << "height:" << pindexLast->nHeight; } if (vGetData.size() > 0) { pfrom->PushMessage(NetMsgType::GETDATA, vGetData); } } } } // BUIP010 Xtreme Thinblocks: begin section else if (strCommand == NetMsgType::GET_XTHIN && !fReindex) // Ignore blocks received while importing { if (!xthinEnabled) { LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); return false; } CBloomFilter filterMemPool; CInv inv; vRecv >> inv >> filterMemPool; if (inv.type != MSG_XTHINBLOCK && inv.type != MSG_THINBLOCK) { LOCK(cs_main); Misbehaving(pfrom->GetId(), 20); return false; } LoadFilter(pfrom, &filterMemPool); pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), inv); ProcessGetData(pfrom, chainparams.GetConsensus()); } else if (strCommand == NetMsgType::XTHINBLOCK && !fReindex) // Ignore blocks received while importing { if (!xthinEnabled) { LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); return false; } CXThinBlock thinBlock; vRecv >> thinBlock; logDebug(106) << "received XThinBlock" << thinBlock.createHash(); // Send expedited ASAP CValidationState state; if (!CheckBlockHeader(thinBlock.header(), state, true)) { // block header is bad LogPrint("thin", "Thinblock %s received with bad header from peer %s (%d)\n", thinBlock.createHash().ToString(), pfrom->addrName.c_str(), pfrom->id); Misbehaving(pfrom->id, 20); return false; } else if (!IsRecentlyExpeditedAndStore(thinBlock.createHash())) SendExpeditedBlock(thinBlock, 0, pfrom); CInv inv(MSG_BLOCK, thinBlock.createHash()); bool fAlreadyHave = false; // An expedited block or re-requested xthin can arrive and beat the original thin block request/response if (!pfrom->mapThinBlocksInFlight.count(inv.hash)) { LogPrint("thin", "Thinblock %s from peer %s (%d) received but we already have it\n", inv.hash.ToString(), pfrom->addrName.c_str(), pfrom->id); LOCK(cs_main); fAlreadyHave = AlreadyHave(inv); // I'll still continue processing if we don't have an accepted block yet } if (!fAlreadyHave) { if (thinBlock.process(pfrom)) HandleBlockMessage(pfrom, strCommand, pfrom->thinBlock, thinBlock.GetInv()); // clears the thin block } else { logDebug(106) << " already have this xthin block"; } } else if (strCommand == NetMsgType::XBLOCKTX && !fReindex) // handle Re-requested thinblock transactions { if (!xthinEnabled) { LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); return false; } if (pfrom->xThinBlockHashes.size() != pfrom->thinBlock.vtx.size()) { // crappy, but fast solution. LogPrint("thin", "Inconsistent thin block data while processing xblock-tx\n"); return true; } CXThinBlockTx thinBlockTx; vRecv >> thinBlockTx; CInv inv(MSG_XTHINBLOCK, thinBlockTx.blockhash); logDebug(Log::Net) << "received blocktxs for" << inv.hash << "peer" << pfrom->id; if (!pfrom->mapThinBlocksInFlight.count(inv.hash)) { LogPrint("thin", "ThinblockTx received but it was either not requested or it was beaten by another block %s peer=%d\n", inv.hash.ToString(), pfrom->id); return true; } // Create the mapMissingTx from all the supplied tx's in the xthinblock std::map mapMissingTx; for (const CTransaction &tx : thinBlockTx.vMissingTx) { mapMissingTx[tx.GetHash().GetCheapHash()] = tx; } int count=0; for (size_t i = 0; i < pfrom->thinBlock.vtx.size(); ++i) { if (pfrom->thinBlock.vtx[i].IsNull()) { auto val = mapMissingTx.find(pfrom->xThinBlockHashes[i]); if (val != mapMissingTx.end()) { pfrom->thinBlock.vtx[i] = val->second; --pfrom->thinBlockWaitingForTxns; } count++; } } LogPrint("thin", "Got %d Re-requested txs, needed %d of them\n", thinBlockTx.vMissingTx.size(), count); if (pfrom->thinBlockWaitingForTxns == 0) { // We have all the transactions now that are in this block: try to reassemble and process. pfrom->thinBlockWaitingForTxns = -1; pfrom->AddInventoryKnown(inv); // For correctness sake, assume all came from the orphans cache std::vector orphans; orphans.reserve(pfrom->thinBlock.vtx.size()); for (unsigned int i = 0; i < pfrom->thinBlock.vtx.size(); i++) { orphans.push_back(pfrom->thinBlock.vtx[i].GetHash()); } HandleBlockMessage(pfrom, strCommand, pfrom->thinBlock, inv); CTxOrphanCache::instance()->eraseOrphans(orphans); } else { LogPrint("thin", "Failed to retrieve all transactions for block\n"); } } else if (strCommand == NetMsgType::GET_XBLOCKTX && !fReindex) // return Re-requested xthinblock transactions { if (!xthinEnabled) { LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); return false; } CXRequestThinBlockTx thinRequestBlockTx; vRecv >> thinRequestBlockTx; if (thinRequestBlockTx.setCheapHashesToRequest.empty()) { // empty request?? LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); return false; } // We use MSG_TX here even though we refer to blockhash because we need to track // how many xblocktx requests we make in case of DOS CInv inv(MSG_TX, thinRequestBlockTx.blockhash); LogPrint("thin", "received get_xblocktx for %s peer=%d\n", inv.hash.ToString(), pfrom->id); // Check for Misbehaving and DOS // If they make more than 20 requests in 10 minutes then disconnect them { const uint64_t nNow = GetTime(); if (pfrom->nGetXBlockTxLastTime <= 0) pfrom->nGetXBlockTxLastTime = nNow; pfrom->nGetXBlockTxCount *= pow(1.0 - 1.0/600.0, (double)(nNow - pfrom->nGetXBlockTxLastTime)); pfrom->nGetXBlockTxLastTime = nNow; pfrom->nGetXBlockTxCount += 1; LogPrint("thin", "nGetXBlockTxCount is %f\n", pfrom->nGetXBlockTxCount); if (pfrom->nGetXBlockTxCount >= 20) { LogPrintf("DOS: Misbehaving - requesting too many xblocktx: %s\n", inv.hash.ToString()); LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); // If they exceed the limit then disconnect them } } CBlockIndex *index = Blocks::Index::get(inv.hash); if (!index) { LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); return false; } if (index->nHeight + 100 < chainActive.Height()) { // a node that is behind should never use this method. LOCK(cs_main); Misbehaving(pfrom->GetId(), 10); return false; } if ((index->nStatus & BLOCK_HAVE_DATA) == 0) { LogPrintf("GET_XBLOCKTX requested block-data not available %s\n", inv.hash.ToString().c_str()); return false; } MutableBlock block; const Consensus::Params& consensusParams = chainparams.GetConsensus(); LOCK(cs_main); if (!ReadBlockFromDisk(block, index, consensusParams)) { LogPrintf("Internal error, file missing datafile %d (block: %d)\n", index->nFile, index->nHeight); return false; } std::vector vTx; int todo = thinRequestBlockTx.setCheapHashesToRequest.size(); for (size_t i = 1; i < block.vtx.size(); i++) { uint64_t cheapHash = block.vtx[i].GetHash().GetCheapHash(); if (thinRequestBlockTx.setCheapHashesToRequest.count(cheapHash)) { vTx.push_back(block.vtx[i]); if (--todo == 0) break; } } if (todo > 0) { // node send us a request for transactions which were not in the block. Misbehaving(pfrom->GetId(), 100); return false; } pfrom->AddInventoryKnown(inv); CXThinBlockTx thinBlockTx(thinRequestBlockTx.blockhash, vTx); pfrom->PushMessage(NetMsgType::XBLOCKTX, thinBlockTx); } // BUIP010 Xtreme Thinblocks: end section else if (strCommand == NetMsgType::BLOCK && !fReindex) // Ignore blocks received while importing { logDebug(106) << "Received a block"; MutableBlock block; try { vRecv >> block; } catch (std::exception &e) { LogPrint("net", "ProcessMessage/block failed to parse message and got error: %s\n", e.what()); pfrom->fDisconnect = true; return true; } logDebug(106) << "->" << block.createHash(); CInv inv(MSG_BLOCK, block.createHash()); logDebug(Log::Net) << "received" << inv << "peer" << pfrom->id; pfrom->AddInventoryKnown(inv); // BUIP010 Extreme Thinblocks: Handle Block Message HandleBlockMessage(pfrom, strCommand, block, inv); std::vector orphans; orphans.reserve(block.vtx.size()); for (unsigned int i = 0; i < block.vtx.size(); i++) { orphans.push_back(block.vtx[i].GetHash()); } CTxOrphanCache::instance()->eraseOrphans(orphans); } else if (strCommand == NetMsgType::GETADDR) { // This asymmetric behavior for inbound and outbound connections was introduced // to prevent a fingerprinting attack: an attacker can send specific fake addresses // to users' AddrMan and later request them by sending getaddr messages. // Making nodes which are behind NAT and can only make outgoing connections ignore // the getaddr message mitigates the attack. if (!pfrom->fInbound) { LogPrint("net", "Ignoring \"getaddr\" from outbound connection. peer=%d\n", pfrom->id); return true; } // Only send one GetAddr response per connection to reduce resource waste // and discourage addr stamping of INV announcements. if (pfrom->fSentAddr) { LogPrint("net", "Ignoring repeated \"getaddr\". peer=%d\n", pfrom->id); return true; } pfrom->fSentAddr = true; pfrom->vAddrToSend.clear(); std::vector vAddr = addrman.GetAddr(); for (const CAddress &addr : vAddr) pfrom->PushAddress(addr); } else if (strCommand == NetMsgType::MEMPOOL) { if (CNode::OutboundTargetReached(false) && !pfrom->fWhitelisted) { LogPrint("net", "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId()); pfrom->fDisconnect = true; return true; } LOCK2(cs_main, pfrom->cs_filter); std::vector vtxid; mempool.queryHashes(vtxid); std::vector vInv; for (uint256& hash : vtxid) { CInv inv(MSG_TX, hash); if (pfrom->pfilter) { CTransaction tx; bool fInMemPool = mempool.lookup(hash, tx); if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... if (!pfrom->pfilter->isRelevantAndUpdate(tx)) continue; } vInv.push_back(inv); if (vInv.size() == MAX_INV_SZ) { pfrom->PushMessage(NetMsgType::INV, vInv); vInv.clear(); } } if (vInv.size() > 0) pfrom->PushMessage(NetMsgType::INV, vInv); } else if (strCommand == NetMsgType::PING) { if (pfrom->nVersion > BIP0031_VERSION) { uint64_t nonce = 0; vRecv >> nonce; // Echo the message back with the nonce. This allows for two useful features: // // 1) A remote node can quickly check if the connection is operational // 2) Remote nodes can measure the latency of the network thread. If this node // is overloaded it won't respond to pings quickly and the remote node can // avoid sending us more work, like chain download requests. // // The nonce stops the remote getting confused between different pings: without // it, if the remote node sends a ping once per second and this node takes 5 // seconds to respond to each, the 5th ping the remote sends would appear to // return very quickly. pfrom->PushMessage(NetMsgType::PONG, nonce); } } else if (strCommand == NetMsgType::PONG) { int64_t pingUsecEnd = nTimeReceived; uint64_t nonce = 0; size_t nAvail = vRecv.in_avail(); bool bPingFinished = false; std::string sProblem; if (nAvail >= sizeof(nonce)) { vRecv >> nonce; // Only process pong message if there is an outstanding ping (old ping without nonce should never pong) if (pfrom->nPingNonceSent != 0) { if (nonce == pfrom->nPingNonceSent) { // Matching pong received, this ping is no longer outstanding bPingFinished = true; int64_t pingUsecTime = pingUsecEnd - pfrom->nPingUsecStart; if (pingUsecTime > 0) { // Successful ping time measurement, replace previous pfrom->nPingUsecTime = pingUsecTime; pfrom->nMinPingUsecTime = std::min(pfrom->nMinPingUsecTime, pingUsecTime); } else { // This should never happen sProblem = "Timing mishap"; } } else { // Nonce mismatches are normal when pings are overlapping sProblem = "Nonce mismatch"; if (nonce == 0) { // This is most likely a bug in another implementation somewhere; cancel this ping bPingFinished = true; sProblem = "Nonce zero"; } } } else { sProblem = "Unsolicited pong without ping"; } } else { // This is most likely a bug in another implementation somewhere; cancel this ping bPingFinished = true; sProblem = "Short payload"; } if (!(sProblem.empty())) { LogPrint("net", "pong peer=%d: %s, %x expected, %x received, %u bytes\n", pfrom->id, sProblem, pfrom->nPingNonceSent, nonce, nAvail); } if (bPingFinished) { pfrom->nPingNonceSent = 0; } } else if (strCommand == NetMsgType::FILTERLOAD) { if (!GetBoolArg("-peerbloomfilters", true)) { LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); return false; } CBloomFilter filter; vRecv >> filter; if (!filter.isWithinSizeConstraints()) { // There is no excuse for sending a too-large filter LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); return false; } else { LOCK(pfrom->cs_filter); delete pfrom->pfilter; pfrom->pfilter = new CBloomFilter(filter); pfrom->pfilter->updateEmptyFull(); } pfrom->fRelayTxes = true; } else if (strCommand == NetMsgType::FILTERADD) { if (!GetBoolArg("-peerbloomfilters", true)) { LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); return false; } std::vector vData; vRecv >> vData; // Nodes must NEVER send a data item > 520 bytes (the max size for a script data object, // and thus, the maximum size any matched object can have) in a filteradd message if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) { LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); return false; } else { LOCK(pfrom->cs_filter); if (pfrom->pfilter) pfrom->pfilter->insert(vData); else Misbehaving(pfrom->GetId(), 100); } } else if (strCommand == NetMsgType::FILTERCLEAR) { if (!GetBoolArg("-peerbloomfilters", true)) { LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); return false; } LOCK(pfrom->cs_filter); delete pfrom->pfilter; pfrom->pfilter = new CBloomFilter(); pfrom->fRelayTxes = true; } else if (strCommand == NetMsgType::DSPROOF) { logInfo(Log::DSProof) << "Got DSPROOF message"; uint256 hash; try { DoubleSpendProof dsp; vRecv >> dsp; if (dsp.isEmpty()) throw std::runtime_error("DSP empty"); hash = dsp.createHash(); const CInv inv(MSG_DOUBLESPENDPROOF, hash); logDebug(Log::DSProof) << " DSP" << inv; pfrom->setAskFor.erase(inv.hash); { LOCK(cs_main); mapAlreadyAskedFor.erase(hash); } switch (dsp.validate(mempool)) { case DoubleSpendProof::Valid: { const auto tx = mempool.addDoubleSpendProof(dsp); if (tx.size() > 0) { // added to mempool correctly, then forward to nodes. logDebug(Log::DSProof) << " Good DSP, broadcasting an INV"; ValidationNotifier().doubleSpendFound(tx, dsp); CTransaction dspTx = tx.createOldTransaction(); LOCK(cs_vNodes); for (CNode* pnode : vNodes) { if (!pnode->fRelayTxes || pnode == pfrom) continue; LOCK(pnode->cs_filter); if (pnode->pfilter) { // For nodes that we sent this Tx before, send a proof. if (pnode->pfilter->isRelevantAndUpdate(dspTx)) pnode->PushInventory(inv); } else { pnode->PushInventory(inv); } } } else logDebug(Log::DSProof) << " valid, not propagating"; break; } case DoubleSpendProof::MissingUTXO: case DoubleSpendProof::MissingTransaction: logInfo(Log::DSProof) << "DoubleSpend Proof postponed: is orphan"; mempool.doubleSpendProofStorage()->addOrphan(dsp, pfrom->fWhitelisted ? -1 : pfrom->id); break; case DoubleSpendProof::Invalid: throw std::runtime_error("DSProof didn't validate"); default: assert(false); return false; } } catch (const std::exception &e) { logWarning(Log::DSProof) << "Failure handling double spend proof. Peer:" << pfrom->GetId() << "Reason:" << e; if (!hash.IsNull()) mempool.doubleSpendProofStorage()->markProofRejected(hash); LOCK(cs_main); Misbehaving(pfrom->GetId(), 15); return false; } } else if (strCommand == NetMsgType::REJECT) { #ifndef NDEBUG try { std::string strMsg; unsigned char ccode; std::string strReason; vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, MAX_REJECT_MESSAGE_LENGTH); std::ostringstream ss; ss << strMsg << " code " << itostr(ccode) << ": " << strReason; if (strMsg == NetMsgType::BLOCK || strMsg == NetMsgType::TX) { uint256 hash; vRecv >> hash; ss << ": hash " << hash.ToString(); } LogPrint("net", "Reject %s\n", SanitizeString(ss.str())); } catch (const std::ios_base::failure&) { // Avoid feedback loops by preventing reject messages from triggering a new reject message. LogPrint("net", "Unparseable reject message received\n"); } #endif } else { // Ignore unknown commands for extensibility logDebug(Log::Net) << "Unknown command" << SanitizeString(strCommand) << "from peer:" << pfrom->id; } return true; } // requires LOCK(cs_vRecvMsg) bool ProcessMessages(CNode* pfrom) { const CChainParams& chainparams = Params(); //if (fDebug) // LogPrintf("%s(%u messages)\n", __func__, pfrom->vRecvMsg.size()); // // Message format // (4) message start // (12) command // (4) size // (4) checksum // (x) data // bool fOk = true; if (!pfrom->vRecvGetData.empty()) ProcessGetData(pfrom, chainparams.GetConsensus()); // this maintains the order of responses if (!pfrom->vRecvGetData.empty()) return fOk; std::deque::iterator it = pfrom->vRecvMsg.begin(); while (!pfrom->fDisconnect && it != pfrom->vRecvMsg.end()) { // Don't bother if send buffer is too full to respond anyway if (pfrom->nSendSize >= SendBufferSize()) break; // get next message CNetMessage& msg = *it; //if (fDebug) // LogPrintf("%s(message %u msgsz, %u bytes, complete:%s)\n", __func__, // msg.hdr.nMessageSize, msg.vRecv.size(), // msg.complete() ? "Y" : "N"); // end, if an incomplete message is found if (!msg.complete()) break; // at this point, any failure means we can delete the current message it++; // Scan for message start if (pfrom->nVersion == 0) { // uninitialized. if (!pfrom->fInbound // we already set isCashNode bool to right value. && memcmp(msg.hdr.pchMessageStart, chainparams.magic(), MESSAGE_START_SIZE) != 0) { addrman.increaseUselessness(pfrom->addr); fOk = false; break; } if (memcmp(msg.hdr.pchMessageStart, chainparams.magic(), MESSAGE_START_SIZE) != 0) { logWarning(Log::Net) << "ProcessMessage: handshake invalid messageStart" << SanitizeString(msg.hdr.GetCommand()) << "peer:" << pfrom->id << pfrom->addr.ToString(); addrman.increaseUselessness(pfrom->addr); fOk = false; break; } assert (memcmp(msg.hdr.pchMessageStart, Params().magic(), MESSAGE_START_SIZE) == 0); addrman.increaseUselessness(pfrom->addr, -1); } // Read header CMessageHeader& hdr = msg.hdr; if (!hdr.IsValid(Params().magic())) { logWarning(Log::Net) << "PROCESSMESSAGE: ERRORS IN HEADER" << SanitizeString(msg.hdr.GetCommand()) << "peer:" << pfrom->id; LOCK(cs_main); Misbehaving(pfrom->id, 5); continue; } std::string strCommand = hdr.GetCommand(); // Message size unsigned int nMessageSize = hdr.nMessageSize; // Checksum CDataStream& vRecv = msg.vRecv; uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); unsigned int nChecksum = ReadLE32((unsigned char*)&hash); if (nChecksum != hdr.nChecksum) { LogPrintf("%s(%s, %u bytes): CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", __func__, SanitizeString(strCommand), nMessageSize, nChecksum, hdr.nChecksum); continue; } // Process message bool fRet = false; try { fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime); boost::this_thread::interruption_point(); } catch (const std::ios_base::failure& e) { pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message")); if (strstr(e.what(), "end of data")) { // Allow exceptions from under-length message on vRecv LogPrintf("%s(%s, %u bytes): Exception '%s' caught, normally caused by a message being shorter than its stated length\n", __func__, SanitizeString(strCommand), nMessageSize, e.what()); } else if (strstr(e.what(), "size too large")) { // Allow exceptions from over-long size LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what()); } else { PrintExceptionContinue(&e, "ProcessMessages()"); } } catch (const boost::thread_interrupted&) { throw; } catch (const std::exception& e) { PrintExceptionContinue(&e, "ProcessMessages()"); } catch (...) { PrintExceptionContinue(nullptr, "ProcessMessages()"); } if (!fRet) LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->id); break; } // In case the connection got shut down, its receive buffer was wiped if (!pfrom->fDisconnect) pfrom->vRecvMsg.erase(pfrom->vRecvMsg.begin(), it); return fOk; } bool SendMessages(CNode* pto) { const bool fReindex = Blocks::DB::instance()->isReindexing(); const Consensus::Params& consensusParams = Params().GetConsensus(); { // Don't send anything until we get its version message if (pto->nVersion == 0) return true; // // Message: ping // bool pingSend = false; if (pto->fPingQueued) { // RPC ping request by user pingSend = true; } if (pto->nPingNonceSent == 0 && pto->nPingUsecStart + PING_INTERVAL * 1000000 < GetTimeMicros()) { // Ping automatically sent as a latency probe & keepalive. pingSend = true; } if (pingSend) { uint64_t nonce = 0; while (nonce == 0) { GetRandBytes((unsigned char*)&nonce, sizeof(nonce)); } pto->fPingQueued = false; pto->nPingUsecStart = GetTimeMicros(); if (pto->nVersion > BIP0031_VERSION) { pto->nPingNonceSent = nonce; pto->PushMessage(NetMsgType::PING, nonce); } else { // Peer is too old to support ping command with nonce, pong will never arrive. pto->nPingNonceSent = 0; pto->PushMessage(NetMsgType::PING); } } TRY_LOCK(cs_main, lockMain); // Acquire cs_main for IsInitialBlockDownload() and CNodeState() if (!lockMain) return true; // Address refresh broadcast if (pindexBestHeader == nullptr) pindexBestHeader = chainActive.Tip(); int64_t nNow = GetTimeMicros(); if (!IsInitialBlockDownload() && pto->nNextLocalAddrSend < nNow) { AdvertiseLocal(pto); pto->nNextLocalAddrSend = nNow + AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL * 1000000 + rand() % 500000000; } // // Message: addr // if (pto->nNextAddrSend < nNow) { pto->nNextAddrSend = PoissonNextSend(nNow, AVG_ADDRESS_BROADCAST_INTERVAL); std::vector vAddr; vAddr.reserve(pto->vAddrToSend.size()); for (const CAddress& addr : pto->vAddrToSend) { if (!pto->addrKnown.contains(addr.GetKey())) { pto->addrKnown.insert(addr.GetKey()); vAddr.push_back(addr); // receiver rejects addr messages larger than 1000 if (vAddr.size() >= 1000) { pto->PushMessage(NetMsgType::ADDR, vAddr); vAddr.clear(); } } } pto->vAddrToSend.clear(); if (!vAddr.empty()) pto->PushMessage(NetMsgType::ADDR, vAddr); } CNodeState &state = *State(pto->GetId()); if (state.fShouldBan) { if (pto->fWhitelisted) LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString()); else { pto->fDisconnect = true; if (pto->addr.IsLocal()) LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString()); else { CNode::Ban(pto->addr, BanReasonNodeMisbehaving); } } state.fShouldBan = false; } for (const CBlockReject& reject : state.rejects) pto->PushMessage(NetMsgType::REJECT, (std::string)NetMsgType::BLOCK, reject.chRejectCode, reject.strRejectReason, reject.hashBlock); state.rejects.clear(); // Start block sync bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do. if (!state.fSyncStarted && !pto->fClient && !fReindex) { // Only actively request headers from small number of peers, unless we're close to today. if (nSyncStarted < 5 || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) { state.fSyncStarted = true; nSyncStarted++; const CBlockIndex *pindexStart = pindexBestHeader; /* If possible, start at the block preceding the currently best known header. This ensures that we always get a non-empty list of headers back as long as the peer is up-to-date. With a non-empty response, we can initialise the peer's known best block. This wouldn't be possible if we requested starting at pindexBestHeader and got back an empty response. */ if (pindexStart->pprev) pindexStart = pindexStart->pprev; logDebug(Log::Net) << "initial getheaders" << pindexStart->nHeight << "to peer:" << pto->id <<"startheight:" << pto->nStartingHeight; pto->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256()); } } // Resend wallet transactions that haven't gotten in a block yet // Except during reindex, importing and IBD, when old wallet // transactions become unconfirmed and spams other nodes. if (!fReindex && !IsInitialBlockDownload()) { ValidationNotifier().resendWalletTransactions(Blocks::DB::instance()->headerChain().Tip()->GetBlockTime()); } // // Try sending block announcements via headers // { // If we have less than MAX_BLOCKS_TO_ANNOUNCE in our // list of block hashes we're relaying, and our peer wants // headers announcements, then find the first header // not yet known to our peer but would connect, and send. // If no header would connect, or if we have too many // blocks, or if the peer doesn't want headers, just // add all to the inv queue. LOCK(pto->cs_inventory); std::vector vHeaders; bool fRevertToInv = (!state.fPreferHeaders || pto->vBlockHashesToAnnounce.size() > MAX_BLOCKS_TO_ANNOUNCE); CBlockIndex *pBestIndex = nullptr; // last header queued for delivery ProcessBlockAvailability(pto->id); // ensure pindexBestKnownBlock is up-to-date if (!fRevertToInv) { bool fFoundStartingHeader = false; // Try to find first header that our peer doesn't have, and // then send all headers past that one. If we come across any // headers that aren't on chainActive, give up. for (const uint256 &hash : pto->vBlockHashesToAnnounce) { CBlockIndex *pindex = Blocks::Index::get(hash); assert(pindex); if (chainActive[pindex->nHeight] != pindex) { // Bail out if we reorged away from this block fRevertToInv = true; break; } if (pBestIndex != nullptr && pindex->pprev != pBestIndex) { // This means that the list of blocks to announce don't // connect to each other. // This shouldn't really be possible to hit during // regular operation (because reorgs should take us to // a chain that has some block not on the prior chain, // which should be caught by the prior check), but one // way this could happen is by using invalidateblock / // reconsiderblock repeatedly on the tip, causing it to // be added multiple times to vBlockHashesToAnnounce. // Robustly deal with this rare situation by reverting // to an inv. fRevertToInv = true; break; } pBestIndex = pindex; if (fFoundStartingHeader) { // add this to the headers message vHeaders.push_back(pindex->GetBlockHeader()); } else if (PeerHasHeader(&state, pindex)) { continue; // keep looking for the first new block } else if (pindex->pprev == nullptr || PeerHasHeader(&state, pindex->pprev)) { // Peer doesn't have this header but they do have the prior one. // Start sending headers. fFoundStartingHeader = true; vHeaders.push_back(pindex->GetBlockHeader()); } else { // Peer doesn't have this header or the prior one -- nothing will // connect, so bail out. fRevertToInv = true; break; } } } if (fRevertToInv) { // If falling back to using an inv, just try to inv the tip. // The last entry in vBlockHashesToAnnounce was our tip at some point // in the past. if (!pto->vBlockHashesToAnnounce.empty()) { const uint256 &hashToAnnounce = pto->vBlockHashesToAnnounce.back(); CBlockIndex *pindex = Blocks::Index::get(hashToAnnounce); assert(pindex); // Warn if we're announcing a block that is not on the main chain. // This should be very rare and could be optimized out. // Just log for now. if (chainActive[pindex->nHeight] != pindex) { LogPrint("net", "Announcing block %s not on main chain (tip=%s)\n", hashToAnnounce.ToString(), chainActive.Tip()->GetBlockHash().ToString()); } // If the peer announced this block to us, don't inv it back. // (Since block announcements may not be via inv's, we can't solely rely on // setInventoryKnown to track this.) if (!PeerHasHeader(&state, pindex)) { pto->PushInventory(CInv(MSG_BLOCK, hashToAnnounce)); LogPrint("net", "%s: sending inv peer=%d hash=%s\n", __func__, pto->id, hashToAnnounce.ToString()); } } } else if (!vHeaders.empty()) { if (vHeaders.size() > 1) { LogPrint("net", "%s: %u headers, range (%s, %s), to peer=%d\n", __func__, vHeaders.size(), vHeaders.front().createHash().ToString(), vHeaders.back().createHash().ToString(), pto->id); } else { LogPrint("net", "%s: sending header %s to peer=%d\n", __func__, vHeaders.front().createHash().ToString(), pto->id); } pto->PushMessage(NetMsgType::HEADERS, vHeaders); state.pindexBestHeaderSent = pBestIndex; } pto->vBlockHashesToAnnounce.clear(); } // // Message: inventory // std::vector vInv; std::vector vInvWait; { bool fSendTrickle = pto->fWhitelisted; if (pto->nNextInvSend < nNow) { fSendTrickle = true; pto->nNextInvSend = PoissonNextSend(nNow, AVG_INVENTORY_BROADCAST_INTERVAL); } LOCK(pto->cs_inventory); vInv.reserve(std::min(1000, pto->vInventoryToSend.size())); vInvWait.reserve(pto->vInventoryToSend.size()); for (const CInv& inv : pto->vInventoryToSend) { if (inv.type == MSG_TX && pto->filterInventoryKnown.contains(inv.hash)) continue; // trickle out tx inv to protect privacy if (inv.type == MSG_TX && !fSendTrickle) { // 1/4 of tx invs blast to all immediately static uint256 hashSalt; if (hashSalt.IsNull()) hashSalt = GetRandHash(); uint256 hashRand = ArithToUint256(UintToArith256(inv.hash) ^ UintToArith256(hashSalt)); hashRand = Hash(BEGIN(hashRand), END(hashRand)); bool fTrickleWait = ((UintToArith256(hashRand) & 3) != 0); if (fTrickleWait) { vInvWait.push_back(inv); continue; } } pto->filterInventoryKnown.insert(inv.hash); vInv.push_back(inv); if (vInv.size() >= 1000) { pto->PushMessage(NetMsgType::INV, vInv); vInv.clear(); } } pto->vInventoryToSend = vInvWait; } if (!vInv.empty()) pto->PushMessage(NetMsgType::INV, vInv); // Detect whether we're stalling nNow = GetTimeMicros(); if (!pto->fDisconnect && state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) { // Stalling only triggers when the block download window cannot move. During normal steady state, // the download window should be much larger than the to-be-downloaded set of blocks, so disconnection // should only happen during initial block download. logCritical(Log::Net) << "Peer" << pto->id << "is stalling block download, disconnecting"; pto->fDisconnect = true; } // In case there is a block that has been in flight from this peer for 2 + 0.5 * N times the block interval // (with N the number of peers from which we're downloading validated blocks), disconnect due to timeout. // We compensate for other peers to prevent killing off peers due to our own downstream link // being saturated. We only count validated in-flight blocks so peers can't advertise non-existing block hashes // to unreasonably increase our timeout. if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0) { QueuedBlock &queuedBlock = state.vBlocksInFlight.front(); int nOtherPeersWithValidatedDownloads = nPeersWithValidatedDownloads - (state.nBlocksInFlightValidHeaders > 0); if (nNow > state.nDownloadingSince + consensusParams.nPowTargetSpacing * (BLOCK_DOWNLOAD_TIMEOUT_BASE + BLOCK_DOWNLOAD_TIMEOUT_PER_PEER * nOtherPeersWithValidatedDownloads)) { logCritical(Log::Net) << "Timeout downloading block" << queuedBlock.hash << "from peer" << pto->id << "disconnecting"; pto->fDisconnect = true; } } // // Message: getdata (blocks) // std::vector vGetData; if (!pto->fDisconnect && !pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { std::vector vToDownload; NodeId staller = -1; FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller); for (CBlockIndex *pindex : vToDownload) { // BUIP010 Xtreme Thinblocks: begin section if (IsThinBlocksEnabled() && IsChainNearlySyncd()) { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); if (HaveThinblockNodes() && CheckThinblockTimer(pindex->GetBlockHash())) { // Must download a block from a ThinBlock peer if (pto->mapThinBlocksInFlight.size() < 1 && pto->ThinBlockCapable()) { // We can only send one thinblock per peer at a time pto->mapThinBlocksInFlight[pindex->GetBlockHash()] = GetTime(); CBloomFilter filterMemPool = createSeededBloomFilter(CTxOrphanCache::instance()->fetchTransactionIds()); ss << CInv(MSG_XTHINBLOCK, pindex->GetBlockHash()); ss << filterMemPool; pto->PushMessage(NetMsgType::GET_XTHIN, ss); MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex); LogPrint("thin", "Requesting thinblock %s (%d) from peer %s (%d)\n", pindex->GetBlockHash().ToString(), pindex->nHeight, pto->addrName.c_str(), pto->id); } } else { // Try to download a thinblock if possible otherwise just download a regular block if (pto->mapThinBlocksInFlight.size() < 1 && pto->ThinBlockCapable()) { // We can only send one thinblock per peer at a time pto->mapThinBlocksInFlight[pindex->GetBlockHash()] = GetTime(); CBloomFilter filterMemPool = createSeededBloomFilter(CTxOrphanCache::instance()->fetchTransactionIds()); ss << CInv(MSG_XTHINBLOCK, pindex->GetBlockHash()); ss << filterMemPool; pto->PushMessage(NetMsgType::GET_XTHIN, ss); LogPrint("thin", "Requesting Thinblock %s (%d) from peer %s (%d)\n", pindex->GetBlockHash().ToString(), pindex->nHeight, pto->addrName.c_str(), pto->id); } else { vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); logDebug(Log::Net) << "Requesting block" << pindex->GetBlockHash() << pindex->nHeight << "from peer" << pto->addrName << pto->id; } MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex); } } else { vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex); logDebug(Log::Net) << "Requesting block" << pindex->GetBlockHash() << pindex->nHeight << "from peer" << pto->id; } // BUIP010 Xtreme Thinblocks: end section } if (state.nBlocksInFlight == 0 && staller != -1) { if (State(staller)->nStallingSince == 0) { State(staller)->nStallingSince = nNow; logDebug(Log::Net) << "Stall started peer" << staller; } } } // // Message: getdata (non-blocks) // while (!pto->fDisconnect && !pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) { const CInv& inv = (*pto->mapAskFor.begin()).second; if (!AlreadyHave(inv)) { logDebug(Log::Net) << "Requesting" << inv << "peer:" << pto->id; vGetData.push_back(inv); if (vGetData.size() >= 1000) { pto->PushMessage(NetMsgType::GETDATA, vGetData); vGetData.clear(); } } else { //If we're not going to ask, don't expect a response. pto->setAskFor.erase(inv.hash); } pto->mapAskFor.erase(pto->mapAskFor.begin()); } if (!vGetData.empty()) pto->PushMessage(NetMsgType::GETDATA, vGetData); } return true; } void MarkIndexUnsaved(CBlockIndex *index) { LOCK(cs_main); setDirtyBlockIndex.insert(index); } class CMainCleanup { public: CMainCleanup() {} ~CMainCleanup() { Blocks::Index::unload(); } } instance_of_cmaincleanup;