diff options
| author | langerhans <[email protected]> | 2019-06-09 19:49:48 +0200 |
|---|---|---|
| committer | langerhans <[email protected]> | 2019-06-09 19:51:03 +0200 |
| commit | d278efaccdc45e7155147d2c86a50f193eafdc07 (patch) | |
| tree | 05cf92afa059fafff80e460c1619edd5bec231b3 /src/main.cpp | |
| parent | Revert "Change fPIE to fPIC (#1420)" (#1447) (diff) | |
| parent | Mark 1.14 ready for release (diff) | |
| download | discoin-d278efaccdc45e7155147d2c86a50f193eafdc07.tar.xz discoin-d278efaccdc45e7155147d2c86a50f193eafdc07.zip | |
Merge branch '1.14-branding'
Diffstat (limited to 'src/main.cpp')
| -rw-r--r-- | src/main.cpp | 5188 |
1 files changed, 0 insertions, 5188 deletions
diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index 9325a2fdb..000000000 --- a/src/main.cpp +++ /dev/null @@ -1,5188 +0,0 @@ -// Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2014 The Bitcoin Core developers -// Copyright (c) 2014-2015 The Dogecoin Core developers -// Copyright (c) 2014-2015 Daniel Kraft -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "main.h" - -#include "addrman.h" -#include "alert.h" -#include "arith_uint256.h" -#include "auxpow.h" -#include "chainparams.h" -#include "checkpoints.h" -#include "checkqueue.h" -#include "consensus/validation.h" -#include "dogecoin.h" -#include "init.h" -#include "merkleblock.h" -#include "net.h" -#include "pow.h" -#include "txdb.h" -#include "txmempool.h" -#include "ui_interface.h" -#include "undo.h" -#include "util.h" -#include "utilmoneystr.h" -#include "validationinterface.h" - -#include <sstream> - -#include <boost/algorithm/string/replace.hpp> -#include <boost/filesystem.hpp> -#include <boost/filesystem/fstream.hpp> -#include <boost/math/distributions/poisson.hpp> -#include <boost/thread.hpp> - -using namespace std; - -#if defined(NDEBUG) -# error "Dogecoin cannot be compiled without assertions." -#endif - -/** - * Global state - */ - -CCriticalSection cs_main; - -BlockMap mapBlockIndex; -CChain chainActive; -CBlockIndex *pindexBestHeader = NULL; -int64_t nTimeBestReceived = 0; -CWaitableCriticalSection csBestBlock; -CConditionVariable cvBlockChange; -int nScriptCheckThreads = 0; -bool fImporting = false; -bool fReindex = false; -bool fTxIndex = false; -bool fHavePruned = false; -bool fPruneMode = false; -bool fIsBareMultisigStd = true; -bool fCheckBlockIndex = false; -bool fCheckpointsEnabled = true; -size_t nCoinCacheUsage = 5000 * 300; -uint64_t nPruneTarget = 0; -bool fAlerts = DEFAULT_ALERTS; - -/** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */ -CFeeRate minRelayTxFee = CFeeRate(COIN); - -CTxMemPool mempool(::minRelayTxFee); - -struct COrphanTx { - CTransaction tx; - NodeId fromPeer; -}; -map<uint256, COrphanTx> mapOrphanTransactions; -map<uint256, set<uint256> > mapOrphanTransactionsByPrev; -void EraseOrphansFor(NodeId peer); - -/** - * Returns true if there are nRequired or more blocks of minVersion or above - * in the last Consensus::Params::nMajorityWindow blocks, starting at pstart and going backwards. - */ -static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned nRequired, const Consensus::Params& consensusParams); -static void CheckBlockIndex(); - -/** Constant stuff for coinbase transactions we create: */ -CScript COINBASE_FLAGS; - -const string strMessageMagic = "Dogecoin Signed Message:\n"; - -// 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; - } - }; - - CBlockIndex *pindexBestInvalid; - - /** - * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and - * as good as our current tip or better. Entries may be failed, though, and pruning nodes may be - * missing the data for the block. - */ - set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates; - /** Number of nodes with fSyncStarted. */ - int nSyncStarted = 0; - /** All pairs A->B, where A (or one if its ancestors) misses transactions, but B has transactions. - * Pruned nodes may have entries where B is missing data. - */ - multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked; - - CCriticalSection cs_LastBlockFile; - std::vector<CBlockFileInfo> vinfoBlockFile; - int nLastBlockFile = 0; - /** Global flag to indicate we should check to see if there are - * block/undo files that should be deleted. Set on startup - * or if we allocate more file space when we're in prune mode - */ - bool fCheckForPruning = false; - - /** - * 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. - */ - map<uint256, NodeId> mapBlockSource; - - /** 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. - int64_t nTime; //! Time of "getdata" request in microseconds. - bool fValidatedHeaders; //! Whether this block has validated headers at the time of request. - int64_t nTimeDisconnect; //! The timeout for this block request (for disconnecting a slow peer) - }; - map<uint256, pair<NodeId, list<QueuedBlock>::iterator> > mapBlocksInFlight; - - /** Number of blocks in flight with validated headers. */ - int nQueuedValidatedHeaders = 0; - - /** Number of preferable block download peers. */ - int nPreferredDownload = 0; - - /** Dirty block index entries. */ - set<CBlockIndex*> setDirtyBlockIndex; - - /** Dirty block file entries. */ - set<int> setDirtyFileInfo; -} // anon namespace - -////////////////////////////////////////////////////////////////////////////// -// -// Registration of network node signals. -// - -namespace { - -struct CBlockReject { - unsigned char chRejectCode; - 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; - //! String name of this peer (debugging/logging purposes). - std::string name; - //! List of asynchronously-determined block rejections to notify this peer about. - std::vector<CBlockReject> 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; - //! 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; - list<QueuedBlock> vBlocksInFlight; - int nBlocksInFlight; - int nBlocksInFlightValidHeaders; - //! Whether we consider this a preferred download peer. - bool fPreferredDownload; - - CNodeState() { - fCurrentlyConnected = false; - nMisbehavior = 0; - fShouldBan = false; - pindexBestKnownBlock = NULL; - hashLastUnknownBlock.SetNull(); - pindexLastCommonBlock = NULL; - fSyncStarted = false; - nStallingSince = 0; - nBlocksInFlight = 0; - nBlocksInFlightValidHeaders = 0; - fPreferredDownload = false; - } -}; - -/** Map maintaining per-node state. Requires cs_main. */ -map<NodeId, CNodeState> mapNodeState; - -// Requires cs_main. -CNodeState *State(NodeId pnode) { - map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode); - if (it == mapNodeState.end()) - return NULL; - 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; -} - -// Returns time at which to timeout block request (nTime in microseconds) -int64_t GetBlockTimeout(int64_t nTime, int nValidatedQueuedBefore, const Consensus::Params &consensusParams) -{ - return nTime + 500000 * consensusParams.nPowTargetSpacing * (4 + nValidatedQueuedBefore); -} - -void InitializeNode(NodeId nodeid, const CNode *pnode) { - LOCK(cs_main); - CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second; - state.name = pnode->addrName; - state.address = pnode->addr; -} - -void FinalizeNode(NodeId nodeid) { - LOCK(cs_main); - CNodeState *state = State(nodeid); - - if (state->fSyncStarted) - nSyncStarted--; - - if (state->nMisbehavior == 0 && state->fCurrentlyConnected) { - AddressCurrentlyConnected(state->address); - } - - BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) - mapBlocksInFlight.erase(entry.hash); - EraseOrphansFor(nodeid); - nPreferredDownload -= state->fPreferredDownload; - - mapNodeState.erase(nodeid); -} - -// Requires cs_main. -// Returns a bool indicating whether we requested this block. -bool MarkBlockAsReceived(const uint256& hash) { - map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); - if (itInFlight != mapBlocksInFlight.end()) { - CNodeState *state = State(itInFlight->second.first); - nQueuedValidatedHeaders -= itInFlight->second.second->fValidatedHeaders; - state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders; - state->vBlocksInFlight.erase(itInFlight->second.second); - state->nBlocksInFlight--; - state->nStallingSince = 0; - mapBlocksInFlight.erase(itInFlight); - return true; - } - return false; -} - -// Requires cs_main. -void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL) { - CNodeState *state = State(nodeid); - assert(state != NULL); - - // Make sure it's not listed somewhere already. - MarkBlockAsReceived(hash); - - int64_t nNow = GetTimeMicros(); - QueuedBlock newentry = {hash, pindex, nNow, pindex != NULL, GetBlockTimeout(nNow, nQueuedValidatedHeaders, consensusParams)}; - nQueuedValidatedHeaders += newentry.fValidatedHeaders; - list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry); - state->nBlocksInFlight++; - state->nBlocksInFlightValidHeaders += newentry.fValidatedHeaders; - mapBlocksInFlight[hash] = std::make_pair(nodeid, it); -} - -/** Check whether the last unknown block a peer advertized is not yet known. */ -void ProcessBlockAvailability(NodeId nodeid) { - CNodeState *state = State(nodeid); - assert(state != NULL); - - if (!state->hashLastUnknownBlock.IsNull()) { - BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock); - if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) { - if (state->pindexBestKnownBlock == NULL || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) - state->pindexBestKnownBlock = itOld->second; - 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 != NULL); - - ProcessBlockAvailability(nodeid); - - BlockMap::iterator it = mapBlockIndex.find(hash); - if (it != mapBlockIndex.end() && it->second->nChainWork > 0) { - // An actually better block was announced. - if (state->pindexBestKnownBlock == NULL || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) - state->pindexBestKnownBlock = it->second; - } else { - // An unknown block was announced; just assume that the latest one is the best one. - state->hashLastUnknownBlock = hash; - } -} - -/** Find the last common ancestor two blocks have. - * Both pa and pb must be non-NULL. */ -CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) { - if (pa->nHeight > pb->nHeight) { - pa = pa->GetAncestor(pb->nHeight); - } else if (pb->nHeight > pa->nHeight) { - pb = pb->GetAncestor(pa->nHeight); - } - - while (pa != pb && pa && pb) { - pa = pa->pprev; - pb = pb->pprev; - } - - // Eventually all chain branches meet at the genesis block. - assert(pa == pb); - return pa; -} - -/** 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<CBlockIndex*>& vBlocks, NodeId& nodeStaller) { - if (count == 0) - return; - - vBlocks.reserve(vBlocks.size() + count); - CNodeState *state = State(nodeid); - assert(state != NULL); - - // Make sure pindexBestKnownBlock is up to date, we'll need it. - ProcessBlockAvailability(nodeid); - - if (state->pindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork) { - // This peer has nothing interesting. - return; - } - - if (state->pindexLastCommonBlock == NULL) { - // 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 = LastCommonAncestor(state->pindexLastCommonBlock, state->pindexBestKnownBlock); - if (state->pindexLastCommonBlock == state->pindexBestKnownBlock) - return; - - std::vector<CBlockIndex*> 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<int>(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<int>(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). - BOOST_FOREACH(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 == NULL) - return false; - stats.nMisbehavior = state->nMisbehavior; - stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1; - stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1; - BOOST_FOREACH(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 - BOOST_FOREACH(const uint256& hash, locator.vHave) { - BlockMap::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) - { - CBlockIndex* pindex = (*mi).second; - if (chain.Contains(pindex)) - return pindex; - } - } - return chain.Genesis(); -} - -CCoinsViewCache *pcoinsTip = NULL; -CBlockTreeDB *pblocktree = NULL; - -////////////////////////////////////////////////////////////////////////////// -// -// mapOrphanTransactions -// - -bool AddOrphanTx(const CTransaction& tx, NodeId peer) -{ - uint256 hash = tx.GetHash(); - if (mapOrphanTransactions.count(hash)) - return false; - - // Ignore big transactions, to avoid a - // send-big-orphans memory exhaustion attack. If a peer has a legitimate - // large transaction with a missing parent then we assume - // it will rebroadcast it later, after the parent transaction(s) - // have been mined or received. - // 10,000 orphans, each of which is at most 5,000 bytes big is - // at most 500 megabytes of orphans: - unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); - if (sz > 5000) - { - LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString()); - return false; - } - - mapOrphanTransactions[hash].tx = tx; - mapOrphanTransactions[hash].fromPeer = peer; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash); - - LogPrint("mempool", "stored orphan tx %s (mapsz %u prevsz %u)\n", hash.ToString(), - mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size()); - return true; -} - -void static EraseOrphanTx(uint256 hash) -{ - map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.find(hash); - if (it == mapOrphanTransactions.end()) - return; - BOOST_FOREACH(const CTxIn& txin, it->second.tx.vin) - { - map<uint256, set<uint256> >::iterator itPrev = mapOrphanTransactionsByPrev.find(txin.prevout.hash); - if (itPrev == mapOrphanTransactionsByPrev.end()) - continue; - itPrev->second.erase(hash); - if (itPrev->second.empty()) - mapOrphanTransactionsByPrev.erase(itPrev); - } - mapOrphanTransactions.erase(it); -} - -void EraseOrphansFor(NodeId peer) -{ - int nErased = 0; - map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin(); - while (iter != mapOrphanTransactions.end()) - { - map<uint256, COrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid - if (maybeErase->second.fromPeer == peer) - { - EraseOrphanTx(maybeErase->second.tx.GetHash()); - ++nErased; - } - } - if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx from peer %d\n", nErased, peer); -} - - -unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) -{ - unsigned int nEvicted = 0; - while (mapOrphanTransactions.size() > nMaxOrphans) - { - // Evict a random orphan: - uint256 randomhash = GetRandHash(); - map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.lower_bound(randomhash); - if (it == mapOrphanTransactions.end()) - it = mapOrphanTransactions.begin(); - EraseOrphanTx(it->first); - ++nEvicted; - } - return nEvicted; -} - - - - - - - -bool IsStandardTx(const CTransaction& tx, string& reason) -{ - if (tx.nVersion > CTransaction::CURRENT_VERSION || tx.nVersion < 1) { - reason = "version"; - return false; - } - - // Extremely large transactions with lots of inputs can cost the network - // almost as much to process as they cost the sender in fees, because - // computing signature hashes is O(ninputs*txsize). Limiting transactions - // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks. - unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); - if (sz >= MAX_STANDARD_TX_SIZE) { - reason = "tx-size"; - return false; - } - - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - // Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed - // keys. (remember the 520 byte limit on redeemScript size) That works - // out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)+3=1627 - // bytes of scriptSig, which we round off to 1650 bytes for some minor - // future-proofing. That's also enough to spend a 20-of-20 - // CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not - // considered standard) - if (txin.scriptSig.size() > 1650) { - reason = "scriptsig-size"; - return false; - } - if (!txin.scriptSig.IsPushOnly()) { - reason = "scriptsig-not-pushonly"; - return false; - } - } - - unsigned int nDataOut = 0; - txnouttype whichType; - BOOST_FOREACH(const CTxOut& txout, tx.vout) { - if (!::IsStandard(txout.scriptPubKey, whichType)) { - reason = "scriptpubkey"; - return false; - } - - if (whichType == TX_NULL_DATA) - nDataOut++; - else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) { - reason = "bare-multisig"; - return false; - } else if (txout.IsDust(::minRelayTxFee)) { - reason = "dust"; - return false; - } - } - - // only one OP_RETURN txout is permitted - if (nDataOut > 1) { - reason = "multi-op-return"; - return false; - } - - return true; -} - -bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) -{ - if (tx.nLockTime == 0) - return true; - if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) - return true; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - if (!txin.IsFinal()) - return false; - return true; -} - -bool CheckFinalTx(const CTransaction &tx) -{ - AssertLockHeld(cs_main); - return IsFinalTx(tx, chainActive.Height() + 1, GetAdjustedTime()); -} - -/** - * Check transaction inputs to mitigate two - * potential denial-of-service attacks: - * - * 1. scriptSigs with extra data stuffed into them, - * not consumed by scriptPubKey (or P2SH script) - * 2. P2SH scripts with a crazy number of expensive - * CHECKSIG/CHECKMULTISIG operations - */ -bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) -{ - if (tx.IsCoinBase()) - return true; // Coinbases don't use vin normally - - for (unsigned int i = 0; i < tx.vin.size(); i++) - { - const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]); - - vector<vector<unsigned char> > vSolutions; - txnouttype whichType; - // get the scriptPubKey corresponding to this input: - const CScript& prevScript = prev.scriptPubKey; - if (!Solver(prevScript, whichType, vSolutions)) - return false; - int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions); - if (nArgsExpected < 0) - return false; - - // Transactions with extra stuff in their scriptSigs are - // non-standard. Note that this EvalScript() call will - // be quick, because if there are any operations - // beside "push data" in the scriptSig - // IsStandardTx() will have already returned false - // and this method isn't called. - vector<vector<unsigned char> > stack; - if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker())) - return false; - - if (whichType == TX_SCRIPTHASH) - { - if (stack.empty()) - return false; - CScript subscript(stack.back().begin(), stack.back().end()); - vector<vector<unsigned char> > vSolutions2; - txnouttype whichType2; - if (Solver(subscript, whichType2, vSolutions2)) - { - int tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2); - if (tmpExpected < 0) - return false; - nArgsExpected += tmpExpected; - } - else - { - // Any other Script with less than 15 sigops OK: - unsigned int sigops = subscript.GetSigOpCount(true); - // ... extra data left on the stack after execution is OK, too: - return (sigops <= MAX_P2SH_SIGOPS); - } - } - - if (stack.size() != (unsigned int)nArgsExpected) - return false; - } - - return true; -} - -unsigned int GetLegacySigOpCount(const CTransaction& tx) -{ - unsigned int nSigOps = 0; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - nSigOps += txin.scriptSig.GetSigOpCount(false); - } - BOOST_FOREACH(const CTxOut& txout, tx.vout) - { - nSigOps += txout.scriptPubKey.GetSigOpCount(false); - } - return nSigOps; -} - -unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& inputs) -{ - if (tx.IsCoinBase()) - return 0; - - unsigned int nSigOps = 0; - for (unsigned int i = 0; i < tx.vin.size(); i++) - { - const CTxOut &prevout = inputs.GetOutputFor(tx.vin[i]); - if (prevout.scriptPubKey.IsPayToScriptHash()) - nSigOps += prevout.scriptPubKey.GetSigOpCount(tx.vin[i].scriptSig); - } - return nSigOps; -} - - - - - - - - -bool CheckTransaction(const CTransaction& tx, CValidationState &state) -{ - // Basic checks that don't depend on any context - if (tx.vin.empty()) - return state.DoS(10, error("CheckTransaction(): vin empty"), - REJECT_INVALID, "bad-txns-vin-empty"); - if (tx.vout.empty()) - return state.DoS(10, error("CheckTransaction(): vout empty"), - REJECT_INVALID, "bad-txns-vout-empty"); - // Size limits - if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return state.DoS(100, error("CheckTransaction(): size limits failed"), - REJECT_INVALID, "bad-txns-oversize"); - - // Check for negative or overflow output values - CAmount nValueOut = 0; - BOOST_FOREACH(const CTxOut& txout, tx.vout) - { - if (txout.nValue < 0) - return state.DoS(100, error("CheckTransaction(): txout.nValue negative"), - REJECT_INVALID, "bad-txns-vout-negative"); - if (txout.nValue > MAX_MONEY) - return state.DoS(100, error("CheckTransaction(): txout.nValue too high"), - REJECT_INVALID, "bad-txns-vout-toolarge"); - nValueOut += txout.nValue; - if (!MoneyRange(nValueOut)) - return state.DoS(100, error("CheckTransaction(): txout total out of range"), - REJECT_INVALID, "bad-txns-txouttotal-toolarge"); - } - - // Check for duplicate inputs - set<COutPoint> vInOutPoints; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - if (vInOutPoints.count(txin.prevout)) - return state.DoS(100, error("CheckTransaction(): duplicate inputs"), - REJECT_INVALID, "bad-txns-inputs-duplicate"); - vInOutPoints.insert(txin.prevout); - } - - if (tx.IsCoinBase()) - { - if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) - return state.DoS(100, error("CheckTransaction(): coinbase script size"), - REJECT_INVALID, "bad-cb-length"); - } - else - { - BOOST_FOREACH(const CTxIn& txin, tx.vin) - if (txin.prevout.IsNull()) - return state.DoS(10, error("CheckTransaction(): prevout is null"), - REJECT_INVALID, "bad-txns-prevout-null"); - } - - return true; -} - -CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree) -{ - { - LOCK(mempool.cs); - uint256 hash = tx.GetHash(); - double dPriorityDelta = 0; - CAmount nFeeDelta = 0; - mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta); - if (dPriorityDelta > 0 || nFeeDelta > 0) - return 0; - } - - CAmount nMinFee = ::minRelayTxFee.GetFee(nBytes); - nMinFee += GetDogecoinDustFee(tx.vout, ::minRelayTxFee); - - if (fAllowFree) - { - // There is a free transaction area in blocks created by most miners, - // * If we are relaying we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 1000 - // to be considered to fall into this category. We don't want to encourage sending - // multiple transactions instead of one big transaction to avoid fees. - if (nBytes < (DEFAULT_BLOCK_PRIORITY_SIZE - 1000)) - nMinFee = 0; - } - - if (!MoneyRange(nMinFee)) - nMinFee = MAX_MONEY; - return nMinFee; -} - - -bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fRejectAbsurdFee) -{ - AssertLockHeld(cs_main); - if (pfMissingInputs) - *pfMissingInputs = false; - - if (!CheckTransaction(tx, state)) - return error("AcceptToMemoryPool: CheckTransaction failed"); - - // Coinbase is only valid in a block, not as a loose transaction - if (tx.IsCoinBase()) - return state.DoS(100, error("AcceptToMemoryPool: coinbase as individual tx"), - REJECT_INVALID, "coinbase"); - - // Rather not work on nonstandard transactions (unless -testnet/-regtest) - string reason; - if (Params().RequireStandard() && !IsStandardTx(tx, reason)) - return state.DoS(0, - error("AcceptToMemoryPool: nonstandard transaction: %s", reason), - REJECT_NONSTANDARD, reason); - - // Only accept nLockTime-using transactions that can be mined in the next - // block; we don't want our mempool filled up with transactions that can't - // be mined yet. - if (!CheckFinalTx(tx)) - return state.DoS(0, error("AcceptToMemoryPool: non-final"), - REJECT_NONSTANDARD, "non-final"); - - // is it already in the memory pool? - uint256 hash = tx.GetHash(); - if (pool.exists(hash)) - return false; - - // Check for conflicts with in-memory transactions - { - LOCK(pool.cs); // protect pool.mapNextTx - for (unsigned int i = 0; i < tx.vin.size(); i++) - { - COutPoint outpoint = tx.vin[i].prevout; - if (pool.mapNextTx.count(outpoint)) - { - // Disable replacement feature for now - return false; - } - } - } - - { - CCoinsView dummy; - CCoinsViewCache view(&dummy); - - CAmount nValueIn = 0; - { - LOCK(pool.cs); - CCoinsViewMemPool viewMemPool(pcoinsTip, pool); - view.SetBackend(viewMemPool); - - // do we already have it? - if (view.HaveCoins(hash)) - return false; - - // do all inputs exist? - // Note that this does not check for the presence of actual outputs (see the next check for that), - // and only helps with filling in pfMissingInputs (to determine missing vs spent). - BOOST_FOREACH(const CTxIn txin, tx.vin) { - if (!view.HaveCoins(txin.prevout.hash)) { - if (pfMissingInputs) - *pfMissingInputs = true; - return false; - } - } - - // are the actual inputs available? - if (!view.HaveInputs(tx)) - return state.Invalid(error("AcceptToMemoryPool: inputs already spent"), - REJECT_DUPLICATE, "bad-txns-inputs-spent"); - - // Bring the best block into scope - view.GetBestBlock(); - - nValueIn = view.GetValueIn(tx); - - // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool - view.SetBackend(dummy); - } - - // Check for non-standard pay-to-script-hash in inputs - if (Params().RequireStandard() && !AreInputsStandard(tx, view)) - return error("AcceptToMemoryPool: nonstandard transaction input"); - - // Check that the transaction doesn't have an excessive number of - // sigops, making it impossible to mine. Since the coinbase transaction - // itself can contain sigops MAX_STANDARD_TX_SIGOPS is less than - // MAX_BLOCK_SIGOPS; we still consider this an invalid rather than - // merely non-standard transaction. - unsigned int nSigOps = GetLegacySigOpCount(tx); - nSigOps += GetP2SHSigOpCount(tx, view); - if (nSigOps > MAX_STANDARD_TX_SIGOPS) - return state.DoS(0, - error("AcceptToMemoryPool: too many sigops %s, %d > %d", - hash.ToString(), nSigOps, MAX_STANDARD_TX_SIGOPS), - REJECT_NONSTANDARD, "bad-txns-too-many-sigops"); - - CAmount nValueOut = tx.GetValueOut(); - CAmount nFees = nValueIn-nValueOut; - double dPriority = view.GetPriority(tx, chainActive.Height()); - - CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), mempool.HasNoInputsOf(tx)); - unsigned int nSize = entry.GetTxSize(); - - // Don't accept it if it can't get into a block - CAmount txMinFee = GetMinRelayFee(tx, nSize, true); - if (fLimitFree && nFees < txMinFee) - return state.DoS(0, error("AcceptToMemoryPool: not enough fees %s, %d < %d", - hash.ToString(), nFees, txMinFee), - REJECT_INSUFFICIENTFEE, "insufficient fee"); - - // Require that free transactions have sufficient priority to be mined in the next block. - if (GetBoolArg("-relaypriority", true) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) { - return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); - } - - // Continuously rate-limit free (really, very-low-fee) transactions - // This mitigates 'penny-flooding' -- sending thousands of free transactions just to - // be annoying or make others' transactions take longer to confirm. - if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize)) - { - static CCriticalSection csFreeLimiter; - static double dFreeCount; - static int64_t nLastTime; - int64_t nNow = GetTime(); - - LOCK(csFreeLimiter); - - // Use an exponentially decaying ~10-minute window: - dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); - nLastTime = nNow; - // -limitfreerelay unit is thousand-bytes-per-minute - // At default rate it would take over a month to fill 1GB - if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000) - return state.DoS(0, error("AcceptToMemoryPool: free transaction rejected by rate limiter"), - REJECT_INSUFFICIENTFEE, "rate limited free transaction"); - LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); - dFreeCount += nSize; - } - - if (fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) - return error("AcceptToMemoryPool: absurdly high fees %s, %d > %d", - hash.ToString(), - nFees, ::minRelayTxFee.GetFee(nSize) * 10000); - - // Check against previous transactions - // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true)) - { - return error("AcceptToMemoryPool: ConnectInputs failed %s", hash.ToString()); - } - - // Check again against just the consensus-critical mandatory script - // verification flags, in case of bugs in the standard flags that cause - // transactions to pass as valid when they're actually invalid. For - // instance the STRICTENC flag was incorrectly allowing certain - // CHECKSIG NOT scripts to pass, even though they were invalid. - // - // There is a similar check in CreateNewBlock() to prevent creating - // invalid blocks, however allowing such transactions into the mempool - // can be exploited as a DoS attack. - if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true)) - { - return error("AcceptToMemoryPool: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s", hash.ToString()); - } - - // Store transaction in memory - pool.addUnchecked(hash, entry, !IsInitialBlockDownload()); - } - - SyncWithWallets(tx, NULL); - - return true; -} - -/** Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock */ -bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow) -{ - CBlockIndex *pindexSlow = NULL; - { - LOCK(cs_main); - { - if (mempool.lookup(hash, txOut)) - { - return true; - } - } - - if (fTxIndex) { - CDiskTxPos postx; - if (pblocktree->ReadTxIndex(hash, postx)) { - CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); - if (file.IsNull()) - return error("%s: OpenBlockFile failed", __func__); - CBlockHeader header; - try { - file >> header; - fseek(file.Get(), postx.nTxOffset, SEEK_CUR); - file >> txOut; - } catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); - } - hashBlock = header.GetHash(); - if (txOut.GetHash() != hash) - return error("%s: txid mismatch", __func__); - return true; - } - } - - if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it - int nHeight = -1; - { - CCoinsViewCache &view = *pcoinsTip; - const CCoins* coins = view.AccessCoins(hash); - if (coins) - nHeight = coins->nHeight; - } - if (nHeight > 0) - pindexSlow = chainActive[nHeight]; - } - } - - if (pindexSlow) { - CBlock block; - if (ReadBlockFromDisk(block, pindexSlow)) { - BOOST_FOREACH(const CTransaction &tx, block.vtx) { - if (tx.GetHash() == hash) { - txOut = tx; - hashBlock = pindexSlow->GetBlockHash(); - return true; - } - } - } - } - - return false; -} - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// CBlock and CBlockIndex -// - -bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart) -{ - // Open history file to append - CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); - if (fileout.IsNull()) - return error("WriteBlockToDisk: OpenBlockFile failed"); - - // Write index header - unsigned int nSize = fileout.GetSerializeSize(block); - fileout << FLATDATA(messageStart) << nSize; - - // Write block - long fileOutPos = ftell(fileout.Get()); - if (fileOutPos < 0) - return error("WriteBlockToDisk: ftell failed"); - pos.nPos = (unsigned int)fileOutPos; - fileout << block; - - return true; -} - -/* Generic implementation of block reading that can handle - both a block and its header. */ - -template<typename T> -static bool ReadBlockOrHeader(T& block, const CDiskBlockPos& pos) -{ - block.SetNull(); - - // Open history file to read - CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); - if (filein.IsNull()) - return error("ReadBlockFromDisk: OpenBlockFile failed for %s", pos.ToString()); - - // Read block - try { - filein >> block; - } - catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s at %s", __func__, e.what(), pos.ToString()); - } - - // Check the header - // Dogecoin: We don't necessarily have block height, so we depend on using the base parameters - if (!CheckAuxPowProofOfWork(block, Params().GetConsensus(0))) - return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString()); - - return true; -} - -template<typename T> -static bool ReadBlockOrHeader(T& block, const CBlockIndex* pindex) -{ - if (!ReadBlockOrHeader(block, pindex->GetBlockPos())) - return false; - if (block.GetHash() != pindex->GetBlockHash()) - return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s", - pindex->ToString(), pindex->GetBlockPos().ToString()); - return true; -} - -bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos) -{ - return ReadBlockOrHeader(block, pos); -} - -bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) -{ - return ReadBlockOrHeader(block, pindex); -} - -bool ReadBlockHeaderFromDisk(CBlockHeader& block, const CBlockIndex* pindex) -{ - return ReadBlockOrHeader(block, pindex); -} - -CAmount 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; - - CAmount 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() -{ - const CChainParams& chainParams = Params(); - LOCK(cs_main); - if (fImporting || fReindex) - return true; - if (fCheckpointsEnabled && chainActive.Height() < Checkpoints::GetTotalBlocksEstimate(chainParams.Checkpoints())) - return true; - static bool lockIBDState = false; - if (lockIBDState) - return false; - bool state = (chainActive.Height() < pindexBestHeader->nHeight - 24 * 6 || - pindexBestHeader->GetBlockTime() < GetTime() - 24 * 60 * 60); - if (!state) - lockIBDState = true; - return state; -} - -bool fLargeWorkForkFound = false; -bool fLargeWorkInvalidChainFound = false; -CBlockIndex *pindexBestForkTip = NULL, *pindexBestForkBase = NULL; - -void CheckForkWarningConditions() -{ - AssertLockHeld(cs_main); - // Before we get past initial download, we cannot reliably alert about forks - // (we assume we don't get stuck on a fork before the last checkpoint) - if (IsInitialBlockDownload()) - return; - - // If our best fork is no longer within 360 blocks (+/- 6 hours if no one mines it) - // of our head, drop it - if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 360) - pindexBestForkTip = NULL; - - if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > chainActive.Tip()->nChainWork + (GetBlockProof(*chainActive.Tip()) * 30))) - { - if (!fLargeWorkForkFound && pindexBestForkBase) - { - std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") + - pindexBestForkBase->phashBlock->ToString() + std::string("'"); - CAlert::Notify(warning, true); - } - if (pindexBestForkTip && pindexBestForkBase) - { - LogPrintf("%s: Warning: Large valid fork found\n forking the chain at height %d (%s)\n lasting to height %d (%s).\nChain state database corruption likely.\n", __func__, - pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString(), - pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString()); - fLargeWorkForkFound = true; - } - else - { - LogPrintf("%s: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n", __func__); - fLargeWorkInvalidChainFound = true; - } - } - else - { - fLargeWorkForkFound = false; - fLargeWorkInvalidChainFound = false; - } -} - -void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) -{ - AssertLockHeld(cs_main); - // If we are on a fork that is sufficiently large, set a warning flag - CBlockIndex* pfork = pindexNewForkTip; - CBlockIndex* plonger = chainActive.Tip(); - while (pfork && pfork != plonger) - { - while (plonger && plonger->nHeight > pfork->nHeight) - plonger = plonger->pprev; - if (pfork == plonger) - break; - pfork = pfork->pprev; - } - - // We define a condition where we should warn the user about as a fork of at least 7 blocks - // with a tip within 72 blocks (+/- 12 hours if no one mines it) of ours - // We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network - // hash rate operating on the fork. - // or a chain that is entirely longer than ours and invalid (note that this should be detected by both) - // We define it this way because it allows us to only store the highest fork tip (+ base) which meets - // the 7-block condition and from this always have the most-likely-to-cause-warning fork - if (pfork && (!pindexBestForkTip || (pindexBestForkTip && pindexNewForkTip->nHeight > pindexBestForkTip->nHeight)) && - pindexNewForkTip->nChainWork - pfork->nChainWork > (GetBlockProof(*pfork) * 7) && - chainActive.Height() - pindexNewForkTip->nHeight < 72) - { - pindexBestForkTip = pindexNewForkTip; - pindexBestForkBase = pfork; - } - - CheckForkWarningConditions(); -} - -// Requires cs_main. -void Misbehaving(NodeId pnode, int howmuch) -{ - if (howmuch == 0) - return; - - CNodeState *state = State(pnode); - if (state == NULL) - return; - - state->nMisbehavior += howmuch; - int banscore = GetArg("-banscore", 100); - if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore) - { - LogPrintf("%s: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", __func__, state->name, state->nMisbehavior-howmuch, state->nMisbehavior); - state->fShouldBan = true; - } else - LogPrintf("%s: %s (%d -> %d)\n", __func__, state->name, state->nMisbehavior-howmuch, state->nMisbehavior); -} - -void static InvalidChainFound(CBlockIndex* pindexNew) -{ - if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork) - pindexBestInvalid = pindexNew; - - LogPrintf("%s: invalid block=%s height=%d log2_work=%.8g date=%s\n", __func__, - pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, - log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", - pindexNew->GetBlockTime())); - LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", __func__, - chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime())); - CheckForkWarningConditions(); -} - -void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) { - int nDoS = 0; - if (state.IsInvalid(nDoS)) { - std::map<uint256, NodeId>::iterator it = mapBlockSource.find(pindex->GetBlockHash()); - if (it != mapBlockSource.end() && State(it->second)) { - CBlockReject reject = {state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash()}; - State(it->second)->rejects.push_back(reject); - if (nDoS > 0) - Misbehaving(it->second, nDoS); - } - } - if (!state.CorruptionPossible()) { - pindex->nStatus |= BLOCK_FAILED_VALID; - setDirtyBlockIndex.insert(pindex); - setBlockIndexCandidates.erase(pindex); - InvalidChainFound(pindex); - } -} - -void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight) -{ - // mark inputs spent - if (!tx.IsCoinBase()) { - txundo.vprevout.reserve(tx.vin.size()); - BOOST_FOREACH(const CTxIn &txin, tx.vin) { - CCoinsModifier coins = inputs.ModifyCoins(txin.prevout.hash); - unsigned nPos = txin.prevout.n; - - if (nPos >= coins->vout.size() || coins->vout[nPos].IsNull()) - assert(false); - // mark an outpoint spent, and construct undo information - txundo.vprevout.push_back(CTxInUndo(coins->vout[nPos])); - coins->Spend(nPos); - if (coins->vout.size() == 0) { - CTxInUndo& undo = txundo.vprevout.back(); - undo.nHeight = coins->nHeight; - undo.fCoinBase = coins->fCoinBase; - undo.nVersion = coins->nVersion; - } - } - } - - // add outputs - inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight); -} - -void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, int nHeight) -{ - CTxUndo txundo; - UpdateCoins(tx, state, inputs, txundo, nHeight); -} - -bool CScriptCheck::operator()() { - const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; - if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, cacheStore), &error)) { - return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error)); - } - return true; -} - -bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, std::vector<CScriptCheck> *pvChecks) -{ - if (!tx.IsCoinBase()) - { - if (pvChecks) - pvChecks->reserve(tx.vin.size()); - - // This doesn't trigger the DoS code on purpose; if it did, it would make it easier - // for an attacker to attempt to split the network. - if (!inputs.HaveInputs(tx)) - return state.Invalid(error("CheckInputs(): %s inputs unavailable", tx.GetHash().ToString())); - - // While checking, GetBestBlock() refers to the parent block. - // This is also true for mempool checks. - CBlockIndex *pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second; - int nSpendHeight = pindexPrev->nHeight + 1; - CAmount nValueIn = 0; - CAmount nFees = 0; - for (unsigned int i = 0; i < tx.vin.size(); i++) - { - const COutPoint &prevout = tx.vin[i].prevout; - const CCoins *coins = inputs.AccessCoins(prevout.hash); - assert(coins); - - // If prev is coinbase, check that it's matured - if (coins->IsCoinBase()) { - // Dogecoin: Switch maturity at depth 145,000 - int nCoinbaseMaturity = Params().GetConsensus(coins->nHeight).nCoinbaseMaturity; - if (nSpendHeight - coins->nHeight < nCoinbaseMaturity) - return state.Invalid( - error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight), - REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); - } - - // Check for negative or overflow input values - nValueIn += coins->vout[prevout.n].nValue; - if (!MoneyRange(coins->vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return state.DoS(100, error("CheckInputs(): txin values out of range"), - REJECT_INVALID, "bad-txns-inputvalues-outofrange"); - - } - - if (nValueIn < tx.GetValueOut()) - return state.DoS(100, error("CheckInputs(): %s value in (%s) < value out (%s)", - tx.GetHash().ToString(), FormatMoney(nValueIn), FormatMoney(tx.GetValueOut())), - REJECT_INVALID, "bad-txns-in-belowout"); - - // Tally transaction fees - CAmount nTxFee = nValueIn - tx.GetValueOut(); - if (nTxFee < 0) - return state.DoS(100, error("CheckInputs(): %s nTxFee < 0", tx.GetHash().ToString()), - REJECT_INVALID, "bad-txns-fee-negative"); - nFees += nTxFee; - if (!MoneyRange(nFees)) - return state.DoS(100, error("CheckInputs(): nFees out of range"), - REJECT_INVALID, "bad-txns-fee-outofrange"); - - // The first loop above does all the inexpensive checks. - // Only if ALL inputs pass do we perform expensive ECDSA signature checks. - // Helps prevent CPU exhaustion attacks. - - // Skip ECDSA signature verification when connecting blocks - // before the last block chain checkpoint. This is safe because block merkle hashes are - // still computed and checked, and any change will be caught at the next checkpoint. - if (fScriptChecks) { - for (unsigned int i = 0; i < tx.vin.size(); i++) { - const COutPoint &prevout = tx.vin[i].prevout; - const CCoins* coins = inputs.AccessCoins(prevout.hash); - assert(coins); - - // Verify signature - CScriptCheck check(*coins, tx, i, flags, cacheStore); - if (pvChecks) { - pvChecks->push_back(CScriptCheck()); - check.swap(pvChecks->back()); - } else if (!check()) { - if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) { - // Check whether the failure was caused by a - // non-mandatory script verification check, such as - // non-standard DER encodings or non-null dummy - // arguments; if so, don't trigger DoS protection to - // avoid splitting the network between upgraded and - // non-upgraded nodes. - CScriptCheck check(*coins, tx, i, - flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore); - if (check()) - return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); - } - // Failures of other flags indicate a transaction that is - // invalid in new blocks, e.g. a invalid P2SH. We DoS ban - // such nodes as they are not following the protocol. That - // said during an upgrade careful thought should be taken - // as to the correct behavior - we may want to continue - // peering with non-upgraded nodes even after a soft-fork - // super-majority vote has passed. - return state.DoS(100,false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError()))); - } - } - } - } - - return true; -} - -namespace { - -bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart) -{ - // Open history file to append - CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); - if (fileout.IsNull()) - return error("%s: OpenUndoFile failed", __func__); - - // Write index header - unsigned int nSize = fileout.GetSerializeSize(blockundo); - fileout << FLATDATA(messageStart) << nSize; - - // Write undo data - long fileOutPos = ftell(fileout.Get()); - if (fileOutPos < 0) - return error("%s: ftell failed", __func__); - pos.nPos = (unsigned int)fileOutPos; - fileout << blockundo; - - // calculate & write checksum - CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); - hasher << hashBlock; - hasher << blockundo; - fileout << hasher.GetHash(); - - return true; -} - -bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uint256& hashBlock) -{ - // Open history file to read - CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); - if (filein.IsNull()) - return error("%s: OpenBlockFile failed", __func__); - - // Read block - uint256 hashChecksum; - try { - filein >> blockundo; - filein >> hashChecksum; - } - catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); - } - - // Verify checksum - CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); - hasher << hashBlock; - hasher << blockundo; - if (hashChecksum != hasher.GetHash()) - return error("%s: Checksum mismatch", __func__); - - return true; -} - -/** 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 occured, see debug.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); -} - -} // anon namespace - -/** - * Apply the undo operation of a CTxInUndo to the given chain state. - * @param undo The undo object. - * @param view The coins view to which to apply the changes. - * @param out The out point that corresponds to the tx input. - * @return True on success. - */ -static bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const COutPoint& out) -{ - bool fClean = true; - - CCoinsModifier coins = view.ModifyCoins(out.hash); - if (undo.nHeight != 0) { - // undo data contains height: this is the last output of the prevout tx being spent - if (!coins->IsPruned()) - fClean = fClean && error("%s: undo data overwriting existing transaction", __func__); - coins->Clear(); - coins->fCoinBase = undo.fCoinBase; - coins->nHeight = undo.nHeight; - coins->nVersion = undo.nVersion; - } else { - if (coins->IsPruned()) - fClean = fClean && error("%s: undo data adding output to missing transaction", __func__); - } - if (coins->IsAvailable(out.n)) - fClean = fClean && error("%s: undo data overwriting existing output", __func__); - if (coins->vout.size() < out.n+1) - coins->vout.resize(out.n+1); - coins->vout[out.n] = undo.txout; - - return fClean; -} - -bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) -{ - assert(pindex->GetBlockHash() == view.GetBestBlock()); - - if (pfClean) - *pfClean = false; - - bool fClean = true; - - CBlockUndo blockUndo; - CDiskBlockPos pos = pindex->GetUndoPos(); - if (pos.IsNull()) - return error("DisconnectBlock(): no undo data available"); - if (!UndoReadFromDisk(blockUndo, pos, pindex->pprev->GetBlockHash())) - return error("DisconnectBlock(): failure reading undo data"); - - if (blockUndo.vtxundo.size() + 1 != block.vtx.size()) - return error("DisconnectBlock(): block and undo data inconsistent"); - - // undo transactions in reverse order - for (int i = block.vtx.size() - 1; i >= 0; i--) { - const CTransaction &tx = block.vtx[i]; - uint256 hash = tx.GetHash(); - - // Check that all outputs are available and match the outputs in the block itself - // exactly. - { - CCoinsModifier outs = view.ModifyCoins(hash); - outs->ClearUnspendable(); - - CCoins outsBlock(tx, pindex->nHeight); - // The CCoins serialization does not serialize negative numbers. - // No network rules currently depend on the version here, so an inconsistency is harmless - // but it must be corrected before txout nversion ever influences a network rule. - if (outsBlock.nVersion < 0) - outs->nVersion = outsBlock.nVersion; - if (*outs != outsBlock) - fClean = fClean && error("DisconnectBlock(): added transaction mismatch? database corrupted"); - - // remove outputs - outs->Clear(); - } - - // restore inputs - if (i > 0) { // not coinbases - const CTxUndo &txundo = blockUndo.vtxundo[i-1]; - if (txundo.vprevout.size() != tx.vin.size()) - return error("DisconnectBlock(): transaction and undo data inconsistent"); - for (unsigned int j = tx.vin.size(); j-- > 0;) { - const COutPoint &out = tx.vin[j].prevout; - const CTxInUndo &undo = txundo.vprevout[j]; - if (!ApplyTxInUndo(undo, view, out)) - fClean = false; - } - } - } - - // move best block pointer to prevout block - view.SetBestBlock(pindex->pprev->GetBlockHash()); - - if (pfClean) { - *pfClean = fClean; - return true; - } - - return fClean; -} - -void static FlushBlockFile(bool fFinalize = false) -{ - LOCK(cs_LastBlockFile); - - CDiskBlockPos posOld(nLastBlockFile, 0); - - FILE *fileOld = OpenBlockFile(posOld); - if (fileOld) { - if (fFinalize) - TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize); - FileCommit(fileOld); - fclose(fileOld); - } - - fileOld = OpenUndoFile(posOld); - if (fileOld) { - if (fFinalize) - TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize); - FileCommit(fileOld); - fclose(fileOld); - } -} - -bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize); - -static CCheckQueue<CScriptCheck> scriptcheckqueue(128); - -void ThreadScriptCheck() { - RenameThread("dogecoin-scriptch"); - scriptcheckqueue.Thread(); -} - -// -// Called periodically asynchronously; alerts if it smells like -// we're being fed a bad chain (blocks being generated much -// too slowly or too quickly). -// -void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const CBlockIndex *const &bestHeader, - int64_t nPowTargetSpacing) -{ - if (bestHeader == NULL || initialDownloadCheck()) return; - - static int64_t lastAlertTime = 0; - int64_t now = GetAdjustedTime(); - if (lastAlertTime > now-60*60*24) return; // Alert at most once per day - - const int SPAN_HOURS=4; - const int SPAN_SECONDS=SPAN_HOURS*60*60; - int BLOCKS_EXPECTED = SPAN_SECONDS / nPowTargetSpacing; - - boost::math::poisson_distribution<double> poisson(BLOCKS_EXPECTED); - - std::string strWarning; - int64_t startTime = GetAdjustedTime()-SPAN_SECONDS; - - LOCK(cs); - const CBlockIndex* i = bestHeader; - int nBlocks = 0; - while (i->GetBlockTime() >= startTime) { - ++nBlocks; - i = i->pprev; - if (i == NULL) return; // Ran out of chain, we must not be fully sync'ed - } - - // How likely is it to find that many by chance? - double p = boost::math::pdf(poisson, nBlocks); - - LogPrint("partitioncheck", "%s : Found %d blocks in the last %d hours\n", __func__, nBlocks, SPAN_HOURS); - LogPrint("partitioncheck", "%s : likelihood: %g\n", __func__, p); - - // Aim for one false-positive about every fifty years of normal running: - const int FIFTY_YEARS = 50*365*24*60*60; - double alertThreshold = 1.0 / (FIFTY_YEARS / SPAN_SECONDS); - - if (p <= alertThreshold && nBlocks < BLOCKS_EXPECTED) - { - // Many fewer blocks than expected: alert! - strWarning = strprintf(_("WARNING: check your network connection, %d blocks received in the last %d hours (%d expected)"), - nBlocks, SPAN_HOURS, BLOCKS_EXPECTED); - } - else if (p <= alertThreshold && nBlocks > BLOCKS_EXPECTED) - { - // Many more blocks than expected: alert! - strWarning = strprintf(_("WARNING: abnormally high number of blocks generated, %d blocks received in the last %d hours (%d expected)"), - nBlocks, SPAN_HOURS, BLOCKS_EXPECTED); - } - if (!strWarning.empty()) - { - strMiscWarning = strWarning; - CAlert::Notify(strWarning, true); - lastAlertTime = now; - } -} - -static int64_t nTimeVerify = 0; -static int64_t nTimeConnect = 0; -static int64_t nTimeIndex = 0; -static int64_t nTimeCallbacks = 0; -static int64_t nTimeTotal = 0; - -bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck) -{ - const CChainParams& chainparams = Params(); - const Consensus::Params consensus = chainparams.GetConsensus(pindex->nHeight); - AssertLockHeld(cs_main); - // Check it again in case a previous version let a bad block in - if (!CheckBlock(block, state, !fJustCheck, !fJustCheck)) - return false; - - // verify that the view's current state corresponds to the previous block - uint256 hashPrevBlock = pindex->pprev == NULL ? uint256() : pindex->pprev->GetBlockHash(); - assert(hashPrevBlock == view.GetBestBlock()); - - // Special case for the genesis block, skipping connection of its transactions - // (its coinbase is unspendable) - if (block.GetHash() == consensus.hashGenesisBlock) { - if (!fJustCheck) - view.SetBestBlock(pindex->GetBlockHash()); - return true; - } - - bool fScriptChecks = (!fCheckpointsEnabled || pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints())); - - // Do not allow blocks that contain transactions which 'overwrite' older transactions, - // unless those are already completely spent. - // If such overwrites are allowed, coinbases and transactions depending upon those - // can be duplicated to remove the ability to spend the first instance -- even after - // being sent to another address. - // See BIP30 and http://r6.ca/blog/20120206T005236Z.html for more information. - // This logic is not necessary for memory pool transactions, as AcceptToMemoryPool - // already refuses previously-known transaction ids entirely. - // This rule was originally applied to all blocks with a timestamp after March 15, 2012, 0:00 UTC. - // Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the - // two in the chain that violate it. This prevents exploiting the issue against nodes during their - // initial block download. - //bool fEnforceBIP30 = (!pindex->phashBlock) || // Enforce on CreateNewBlock invocations which don't have a hash. - // !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256S("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || - // (pindex->nHeight==91880 && pindex->GetBlockHash() == uint256S("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721"))); - //if (fEnforceBIP30) { - // Dogecoin: BIP30 has been active since inception - BOOST_FOREACH(const CTransaction& tx, block.vtx) { - const CCoins* coins = view.AccessCoins(tx.GetHash()); - if (coins && !coins->IsPruned()) - return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"), - REJECT_INVALID, "bad-txns-BIP30"); - } - //} - - // Dogecoin: BIP16 has been active since inception - unsigned int flags = SCRIPT_VERIFY_P2SH; - - // Start enforcing the DERSIG (BIP66) rules, for block.nVersion=3 blocks, when 75% of the network has upgraded: - if (block.nVersion >= 3 && IsSuperMajority(3, pindex->pprev, consensus.nMajorityEnforceBlockUpgrade, consensus)) { - flags |= SCRIPT_VERIFY_DERSIG; - } - - CBlockUndo blockundo; - - CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); - - int64_t nTimeStart = GetTimeMicros(); - CAmount nFees = 0; - int nInputs = 0; - unsigned int nSigOps = 0; - CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); - std::vector<std::pair<uint256, CDiskTxPos> > vPos; - vPos.reserve(block.vtx.size()); - blockundo.vtxundo.reserve(block.vtx.size() - 1); - for (unsigned int i = 0; i < block.vtx.size(); i++) - { - const CTransaction &tx = block.vtx[i]; - - nInputs += tx.vin.size(); - nSigOps += GetLegacySigOpCount(tx); - if (nSigOps > MAX_BLOCK_SIGOPS) - return state.DoS(100, error("ConnectBlock(): too many sigops"), - REJECT_INVALID, "bad-blk-sigops"); - - if (!tx.IsCoinBase()) - { - if (!view.HaveInputs(tx)) - return state.DoS(100, error("ConnectBlock(): inputs missing/spent"), - REJECT_INVALID, "bad-txns-inputs-missingorspent"); - - - // Add in sigops done by pay-to-script-hash inputs; - // this is to prevent a "rogue miner" from creating - // an incredibly-expensive-to-validate block. - // Dogecoin: BIP16 has been enabled since inception - nSigOps += GetP2SHSigOpCount(tx, view); - if (nSigOps > MAX_BLOCK_SIGOPS) - return state.DoS(100, error("ConnectBlock(): too many sigops"), - REJECT_INVALID, "bad-blk-sigops"); - - nFees += view.GetValueIn(tx)-tx.GetValueOut(); - - std::vector<CScriptCheck> vChecks; - if (!CheckInputs(tx, state, view, fScriptChecks, flags, false, nScriptCheckThreads ? &vChecks : NULL)) - return false; - control.Add(vChecks); - } - - CTxUndo undoDummy; - if (i > 0) { - blockundo.vtxundo.push_back(CTxUndo()); - } - UpdateCoins(tx, state, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight); - - vPos.push_back(std::make_pair(tx.GetHash(), pos)); - pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); - } - int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart; - LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs-1), nTimeConnect * 0.000001); - - CAmount blockReward = nFees + GetDogecoinBlockSubsidy(pindex->nHeight, consensus, hashPrevBlock); - if (block.vtx[0].GetValueOut() > blockReward) - return state.DoS(100, - error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)", - block.vtx[0].GetValueOut(), blockReward), - REJECT_INVALID, "bad-cb-amount"); - - if (!control.Wait()) - return state.DoS(100, false); - int64_t nTime2 = GetTimeMicros(); nTimeVerify += nTime2 - nTimeStart; - LogPrint("bench", " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs]\n", nInputs - 1, 0.001 * (nTime2 - nTimeStart), nInputs <= 1 ? 0 : 0.001 * (nTime2 - nTimeStart) / (nInputs-1), nTimeVerify * 0.000001); - - if (fJustCheck) - return true; - - // Write undo information to disk - if (pindex->GetUndoPos().IsNull() || !pindex->IsValid(BLOCK_VALID_SCRIPTS)) - { - if (pindex->GetUndoPos().IsNull()) { - CDiskBlockPos pos; - if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) - return error("ConnectBlock(): FindUndoPos failed"); - if (!UndoWriteToDisk(blockundo, pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart())) - return AbortNode(state, "Failed to write undo data"); - - // update nUndoPos in block index - pindex->nUndoPos = pos.nPos; - pindex->nStatus |= BLOCK_HAVE_UNDO; - } - - pindex->RaiseValidity(BLOCK_VALID_SCRIPTS); - setDirtyBlockIndex.insert(pindex); - } - - if (fTxIndex) - if (!pblocktree->WriteTxIndex(vPos)) - return AbortNode(state, "Failed to write transaction index"); - - // add this block to the view's block chain - view.SetBestBlock(pindex->GetBlockHash()); - - int64_t nTime3 = GetTimeMicros(); nTimeIndex += nTime3 - nTime2; - LogPrint("bench", " - Index writing: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeIndex * 0.000001); - - // Watch for changes to the previous coinbase transaction. - static uint256 hashPrevBestCoinBase; - GetMainSignals().UpdatedTransaction(hashPrevBestCoinBase); - hashPrevBestCoinBase = block.vtx[0].GetHash(); - - int64_t nTime4 = GetTimeMicros(); nTimeCallbacks += nTime4 - nTime3; - LogPrint("bench", " - Callbacks: %.2fms [%.2fs]\n", 0.001 * (nTime4 - nTime3), nTimeCallbacks * 0.000001); - - return true; -} - -enum FlushStateMode { - FLUSH_STATE_NONE, - FLUSH_STATE_IF_NEEDED, - FLUSH_STATE_PERIODIC, - FLUSH_STATE_ALWAYS -}; - -/** - * Update the on-disk chain state. - * The caches and indexes are flushed depending on the mode we're called with - * if they're too large, if it's been a while since the last write, - * or always and in all cases if we're in prune mode and are deleting files. - */ -bool static 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; - std::set<int> setFilesToPrune; - bool fFlushForPrune = false; - try { - if (fPruneMode && fCheckForPruning) { - FindFilesToPrune(setFilesToPrune); - fCheckForPruning = false; - if (!setFilesToPrune.empty()) { - fFlushForPrune = true; - if (!fHavePruned) { - pblocktree->WriteFlag("prunedblockfiles", true); - fHavePruned = true; - } - } - } - 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; - } - size_t cacheSize = pcoinsTip->DynamicMemoryUsage(); - // The cache is large and close to the limit, but we have time now (not in the middle of a block processing). - bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize * (10.0/9) > nCoinCacheUsage; - // The cache is over the limit, we have to write now. - bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nCoinCacheUsage; - // 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) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune; - // 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. - FlushBlockFile(); - // Then update all block file information (which may refer to block and undo files). - { - std::vector<std::pair<int, const CBlockFileInfo*> > vFiles; - vFiles.reserve(setDirtyFileInfo.size()); - for (set<int>::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) { - vFiles.push_back(make_pair(*it, &vinfoBlockFile[*it])); - setDirtyFileInfo.erase(it++); - } - std::vector<const CBlockIndex*> vBlocks; - vBlocks.reserve(setDirtyBlockIndex.size()); - for (set<CBlockIndex*>::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) { - vBlocks.push_back(*it); - setDirtyBlockIndex.erase(it++); - } - if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) { - return AbortNode(state, "Files to write to block index database"); - } - } - // Finally remove any pruned files - if (fFlushForPrune) - UnlinkPrunedFiles(setFilesToPrune); - nLastWrite = nNow; - } - // Flush best chain related state. This can only be done if the blocks / block index write was also done. - if (fDoFullFlush) { - // Typical CCoins structures on disk are around 128 bytes in size. - // Pushing a new one to the database can cause it to be written - // twice (once in the log, and once in the tables). This is already - // an overestimation, as most will delete an existing entry or - // overwrite one. Still, use a conservative safety factor of 2. - if (!CheckDiskSpace(128 * 2 * 2 * pcoinsTip->GetCacheSize())) - return state.Error("out of disk space"); - // Flush the chainstate (which may refer to block index entries). - if (!pcoinsTip->Flush()) - return AbortNode(state, "Failed to write to coin database"); - nLastFlush = nNow; - } - if ((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). - GetMainSignals().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); -} - -void PruneAndFlush() { - CValidationState state; - fCheckForPruning = true; - FlushStateToDisk(state, FLUSH_STATE_NONE); -} - -/** Update chainActive and related internal data structures. */ -void static UpdateTip(CBlockIndex *pindexNew) { - const CChainParams& chainParams = Params(); - chainActive.SetTip(pindexNew); - - // New best block - nTimeBestReceived = GetTime(); - mempool.AddTransactionsUpdated(1); - - LogPrintf("%s: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f cache=%.1fMiB(%utx)\n", __func__, - chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), - Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); - - cvBlockChange.notify_all(); - - // Check the version of the last 100 blocks to see if we need to upgrade: - static bool fWarned = false; - if (!IsInitialBlockDownload() && !fWarned) - { - int nUpgraded = 0; - const CBlockIndex* pindex = chainActive.Tip(); - for (int i = 0; i < 100 && pindex != NULL; i++) - { - if (pindex->nVersion > CBlock::CURRENT_VERSION) - ++nUpgraded; - pindex = pindex->pprev; - } - if (nUpgraded > 0) - LogPrintf("%s: %d of last 100 blocks above version %d\n", __func__, nUpgraded, (int)CBlock::CURRENT_VERSION); - if (nUpgraded > 100/2) - { - // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user: - strMiscWarning = _("Warning: This version is obsolete; upgrade required!"); - CAlert::Notify(strMiscWarning, true); - fWarned = true; - } - } -} - -/** Disconnect chainActive's tip. */ -bool static DisconnectTip(CValidationState &state) { - CBlockIndex *pindexDelete = chainActive.Tip(); - assert(pindexDelete); - mempool.check(pcoinsTip); - // Read block from disk. - CBlock block; - if (!ReadBlockFromDisk(block, pindexDelete)) - return AbortNode(state, "Failed to read block"); - // Apply the block atomically to the chain state. - int64_t nStart = GetTimeMicros(); - { - CCoinsViewCache view(pcoinsTip); - if (!DisconnectBlock(block, state, pindexDelete, view)) - return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString()); - assert(view.Flush()); - } - LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); - // Write the chain state to disk, if necessary. - if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) - return false; - // Resurrect mempool transactions from the disconnected block. - BOOST_FOREACH(const CTransaction &tx, block.vtx) { - // ignore validation errors in resurrected transactions - list<CTransaction> removed; - CValidationState stateDummy; - if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) - mempool.remove(tx, removed, true); - } - mempool.removeCoinbaseSpends(pcoinsTip, pindexDelete->nHeight); - mempool.check(pcoinsTip); - // Update chainActive and related variables. - UpdateTip(pindexDelete->pprev); - // Let wallets know transactions went from 1-confirmed to - // 0-confirmed or conflicted: - BOOST_FOREACH(const CTransaction &tx, block.vtx) { - SyncWithWallets(tx, NULL); - } - return true; -} - -static int64_t nTimeReadFromDisk = 0; -static int64_t nTimeConnectTotal = 0; -static int64_t nTimeFlush = 0; -static int64_t nTimeChainState = 0; -static int64_t nTimePostConnect = 0; - -/** - * Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock - * corresponding to pindexNew, to bypass loading it again from disk. - */ -bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *pblock) { - assert(pindexNew->pprev == chainActive.Tip()); - mempool.check(pcoinsTip); - // Read block from disk. - int64_t nTime1 = GetTimeMicros(); - CBlock block; - if (!pblock) { - if (!ReadBlockFromDisk(block, pindexNew)) - return AbortNode(state, "Failed to read block"); - pblock = █ - } - // Apply the block atomically to the chain state. - int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1; - int64_t nTime3; - LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); - { - CCoinsViewCache view(pcoinsTip); - CInv inv(MSG_BLOCK, pindexNew->GetBlockHash()); - bool rv = ConnectBlock(*pblock, state, pindexNew, view); - GetMainSignals().BlockChecked(*pblock, state); - if (!rv) { - if (state.IsInvalid()) - InvalidBlockFound(pindexNew, state); - return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); - } - mapBlockSource.erase(inv.hash); - nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2; - LogPrint("bench", " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001); - assert(view.Flush()); - } - int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3; - LogPrint("bench", " - Flush: %.2fms [%.2fs]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001); - // Write the chain state to disk, if necessary. - if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) - return false; - int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4; - LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001); - // Remove conflicting transactions from the mempool. - list<CTransaction> txConflicted; - mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload()); - mempool.check(pcoinsTip); - // Update chainActive & related variables. - UpdateTip(pindexNew); - // Tell wallet about transactions that went from mempool - // to conflicted: - BOOST_FOREACH(const CTransaction &tx, txConflicted) { - SyncWithWallets(tx, NULL); - } - // ... and about transactions that got confirmed: - BOOST_FOREACH(const CTransaction &tx, pblock->vtx) { - SyncWithWallets(tx, pblock); - } - - int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1; - LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001); - LogPrint("bench", "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001); - return true; -} - -/** - * Return the tip of the chain with the most work in it, that isn't - * known to be invalid (it's however far from certain to be valid). - */ -static CBlockIndex* FindMostWorkChain() { - do { - CBlockIndex *pindexNew = NULL; - - // Find the best candidate header. - { - std::set<CBlockIndex*, CBlockIndexWorkComparator>::reverse_iterator it = setBlockIndexCandidates.rbegin(); - if (it == setBlockIndexCandidates.rend()) - return NULL; - pindexNew = *it; - } - - // Check whether all blocks on the path between the currently active chain and the candidate are valid. - // Just going until the active chain is an optimization, as we know all blocks in it are valid already. - CBlockIndex *pindexTest = pindexNew; - bool fInvalidAncestor = false; - while (pindexTest && !chainActive.Contains(pindexTest)) { - assert(pindexTest->nChainTx || pindexTest->nHeight == 0); - - // Pruned nodes may have entries in setBlockIndexCandidates for - // which block files have been deleted. Remove those as candidates - // for the most work chain if we come across them; we can't switch - // to a chain unless we have all the non-active-chain parent blocks. - bool fFailedChain = pindexTest->nStatus & BLOCK_FAILED_MASK; - bool fMissingData = !(pindexTest->nStatus & BLOCK_HAVE_DATA); - if (fFailedChain || fMissingData) { - // Candidate chain is not usable (either invalid or missing data) - if (fFailedChain && (pindexBestInvalid == NULL || pindexNew->nChainWork > pindexBestInvalid->nChainWork)) - pindexBestInvalid = pindexNew; - CBlockIndex *pindexFailed = pindexNew; - // Remove the entire chain from the set. - while (pindexTest != pindexFailed) { - if (fFailedChain) { - pindexFailed->nStatus |= BLOCK_FAILED_CHILD; - } else if (fMissingData) { - // If we're missing data, then add back to mapBlocksUnlinked, - // so that if the block arrives in the future we can try adding - // to setBlockIndexCandidates again. - mapBlocksUnlinked.insert(std::make_pair(pindexFailed->pprev, pindexFailed)); - } - setBlockIndexCandidates.erase(pindexFailed); - pindexFailed = pindexFailed->pprev; - } - setBlockIndexCandidates.erase(pindexTest); - fInvalidAncestor = true; - break; - } - pindexTest = pindexTest->pprev; - } - if (!fInvalidAncestor) - return pindexNew; - } while(true); -} - -/** Delete all entries in setBlockIndexCandidates that are worse than the current tip. */ -static void PruneBlockIndexCandidates() { - // Note that we can't delete the current block itself, as we may need to return to it later in case a - // reorganization to a better block fails. - std::set<CBlockIndex*, CBlockIndexWorkComparator>::iterator it = setBlockIndexCandidates.begin(); - while (it != setBlockIndexCandidates.end() && setBlockIndexCandidates.value_comp()(*it, chainActive.Tip())) { - setBlockIndexCandidates.erase(it++); - } - // Either the current tip or a successor of it we're working towards is left in setBlockIndexCandidates. - assert(!setBlockIndexCandidates.empty()); -} - -/** - * Try to make some progress towards making pindexMostWork the active block. - * pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork. - */ -static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMostWork, CBlock *pblock) { - AssertLockHeld(cs_main); - bool fInvalidFound = false; - const CBlockIndex *pindexOldTip = chainActive.Tip(); - const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork); - - // Disconnect active blocks which are no longer in the best chain. - while (chainActive.Tip() && chainActive.Tip() != pindexFork) { - if (!DisconnectTip(state)) - return false; - } - - // Build list of new blocks to connect. - std::vector<CBlockIndex*> vpindexToConnect; - bool fContinue = true; - int nHeight = pindexFork ? pindexFork->nHeight : -1; - while (fContinue && nHeight != pindexMostWork->nHeight) { - // Don't iterate the entire list of potential improvements toward the best tip, as we likely only need - // a few blocks along the way. - int nTargetHeight = std::min(nHeight + 32, pindexMostWork->nHeight); - vpindexToConnect.clear(); - vpindexToConnect.reserve(nTargetHeight - nHeight); - CBlockIndex *pindexIter = pindexMostWork->GetAncestor(nTargetHeight); - while (pindexIter && pindexIter->nHeight != nHeight) { - vpindexToConnect.push_back(pindexIter); - pindexIter = pindexIter->pprev; - } - nHeight = nTargetHeight; - - // Connect new blocks. - BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) { - if (!ConnectTip(state, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL)) { - if (state.IsInvalid()) { - // The block violates a consensus rule. - if (!state.CorruptionPossible()) - InvalidChainFound(vpindexToConnect.back()); - state = CValidationState(); - fInvalidFound = true; - fContinue = false; - break; - } else { - // A system error occurred (disk space, database error, ...). - return false; - } - } else { - PruneBlockIndexCandidates(); - if (!pindexOldTip || chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) { - // We're in a better position than we were. Return temporarily to release the lock. - fContinue = false; - break; - } - } - } - } - - // Callbacks/notifications for a new best chain. - if (fInvalidFound) - CheckForkWarningConditionsOnNewFork(vpindexToConnect.back()); - else - CheckForkWarningConditions(); - - return true; -} - -/** - * Make the best chain active, in multiple steps. The result is either failure - * or an activated best chain. pblock is either NULL or a pointer to a block - * that is already loaded (to avoid loading it again from disk). - */ -bool ActivateBestChain(CValidationState &state, CBlock *pblock) { - CBlockIndex *pindexNewTip = NULL; - CBlockIndex *pindexMostWork = NULL; - const CChainParams& chainParams = Params(); - do { - boost::this_thread::interruption_point(); - - bool fInitialDownload; - { - LOCK(cs_main); - pindexMostWork = FindMostWorkChain(); - - // Whether we have anything to do at all. - if (pindexMostWork == NULL || pindexMostWork == chainActive.Tip()) - return true; - - if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL)) - return false; - - pindexNewTip = chainActive.Tip(); - fInitialDownload = IsInitialBlockDownload(); - } - // When we reach this point, we switched to a new tip (stored in pindexNewTip). - - // Notifications/callbacks that can run without cs_main - if (!fInitialDownload) { - uint256 hashNewTip = pindexNewTip->GetBlockHash(); - // Relay inventory, but don't relay old inventory during initial block download. - int nBlockEstimate = 0; - if (fCheckpointsEnabled) - nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(chainParams.Checkpoints()); - // Don't relay blocks if pruning -- could cause a peer to try to download, resulting - // in a stalled download if the block file is pruned before the request. - if (nLocalServices & NODE_NETWORK) { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) - pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip)); - } - // Notify external listeners about the new tip. - uiInterface.NotifyBlockTip(hashNewTip, pindexNewTip->nHeight); - } - } while(pindexMostWork != chainActive.Tip()); - CheckBlockIndex(); - - // Write changes periodically to disk, after relay. - if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) { - return false; - } - - return true; -} - -bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) { - AssertLockHeld(cs_main); - - // Mark the block itself as invalid. - pindex->nStatus |= BLOCK_FAILED_VALID; - setDirtyBlockIndex.insert(pindex); - setBlockIndexCandidates.erase(pindex); - - while (chainActive.Contains(pindex)) { - CBlockIndex *pindexWalk = chainActive.Tip(); - pindexWalk->nStatus |= BLOCK_FAILED_CHILD; - setDirtyBlockIndex.insert(pindexWalk); - setBlockIndexCandidates.erase(pindexWalk); - // ActivateBestChain considers blocks already in chainActive - // unconditionally valid already, so force disconnect away from it. - if (!DisconnectTip(state)) { - return false; - } - } - - // The resulting new best tip may not be in setBlockIndexCandidates anymore, so - // add it again. - BlockMap::iterator it = mapBlockIndex.begin(); - while (it != mapBlockIndex.end()) { - if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) { - setBlockIndexCandidates.insert(it->second); - } - it++; - } - - InvalidChainFound(pindex); - return true; -} - -bool ReconsiderBlock(CValidationState& state, CBlockIndex *pindex) { - AssertLockHeld(cs_main); - - int nHeight = pindex->nHeight; - - // Remove the invalidity flag from this block and all its descendants. - BlockMap::iterator it = mapBlockIndex.begin(); - while (it != mapBlockIndex.end()) { - if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) { - it->second->nStatus &= ~BLOCK_FAILED_MASK; - setDirtyBlockIndex.insert(it->second); - if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) { - setBlockIndexCandidates.insert(it->second); - } - if (it->second == pindexBestInvalid) { - // Reset invalid block marker if it was pointing to one of those. - pindexBestInvalid = NULL; - } - } - it++; - } - - // Remove the invalidity flag from all ancestors too. - while (pindex != NULL) { - if (pindex->nStatus & BLOCK_FAILED_MASK) { - pindex->nStatus &= ~BLOCK_FAILED_MASK; - setDirtyBlockIndex.insert(pindex); - } - pindex = pindex->pprev; - } - return true; -} - -CBlockIndex* AddToBlockIndex(const CBlockHeader& block) -{ - // Check for duplicate - uint256 hash = block.GetHash(); - BlockMap::iterator it = mapBlockIndex.find(hash); - if (it != mapBlockIndex.end()) - return it->second; - - // Construct new block index object - CBlockIndex* pindexNew = new CBlockIndex(block); - assert(pindexNew); - // We assign the sequence id to blocks only when the full data is available, - // to avoid miners withholding blocks but broadcasting headers, to get a - // competitive advantage. - pindexNew->nSequenceId = 0; - BlockMap::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; - pindexNew->phashBlock = &((*mi).first); - BlockMap::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock); - if (miPrev != mapBlockIndex.end()) - { - pindexNew->pprev = (*miPrev).second; - pindexNew->nHeight = pindexNew->pprev->nHeight + 1; - pindexNew->BuildSkip(); - } - pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew); - // Dogecoin: Add AuxPoW - if (block.nVersion.IsAuxpow()) { - pindexNew->pauxpow = block.auxpow; - assert(NULL != pindexNew->pauxpow.get()); - } - pindexNew->RaiseValidity(BLOCK_VALID_TREE); - if (pindexBestHeader == NULL || pindexBestHeader->nChainWork < pindexNew->nChainWork) - pindexBestHeader = pindexNew; - - setDirtyBlockIndex.insert(pindexNew); - - return pindexNew; -} - -/** Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). */ -bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos) -{ - pindexNew->nTx = block.vtx.size(); - pindexNew->nChainTx = 0; - pindexNew->nFile = pos.nFile; - pindexNew->nDataPos = pos.nPos; - pindexNew->nUndoPos = 0; - pindexNew->nStatus |= BLOCK_HAVE_DATA; - pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS); - setDirtyBlockIndex.insert(pindexNew); - - if (pindexNew->pprev == NULL || pindexNew->pprev->nChainTx) { - // If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS. - deque<CBlockIndex*> queue; - queue.push_back(pindexNew); - - // Recursively process any descendant blocks that now may be eligible to be connected. - while (!queue.empty()) { - CBlockIndex *pindex = queue.front(); - queue.pop_front(); - pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx; - { - LOCK(cs_nBlockSequenceId); - pindex->nSequenceId = nBlockSequenceId++; - } - if (chainActive.Tip() == NULL || !setBlockIndexCandidates.value_comp()(pindex, chainActive.Tip())) { - setBlockIndexCandidates.insert(pindex); - } - std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> range = mapBlocksUnlinked.equal_range(pindex); - while (range.first != range.second) { - std::multimap<CBlockIndex*, CBlockIndex*>::iterator it = range.first; - queue.push_back(it->second); - range.first++; - mapBlocksUnlinked.erase(it); - } - } - } else { - if (pindexNew->pprev && pindexNew->pprev->IsValid(BLOCK_VALID_TREE)) { - mapBlocksUnlinked.insert(std::make_pair(pindexNew->pprev, pindexNew)); - } - } - - return true; -} - -bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false) -{ - LOCK(cs_LastBlockFile); - - unsigned int nFile = fKnown ? pos.nFile : nLastBlockFile; - if (vinfoBlockFile.size() <= nFile) { - vinfoBlockFile.resize(nFile + 1); - } - - if (!fKnown) { - while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { - LogPrintf("Leaving block file %i: %s\n", nFile, vinfoBlockFile[nFile].ToString()); - FlushBlockFile(true); - nFile++; - if (vinfoBlockFile.size() <= nFile) { - vinfoBlockFile.resize(nFile + 1); - } - } - pos.nFile = nFile; - pos.nPos = vinfoBlockFile[nFile].nSize; - } - - nLastBlockFile = nFile; - vinfoBlockFile[nFile].AddBlock(nHeight, nTime); - if (fKnown) - vinfoBlockFile[nFile].nSize = std::max(pos.nPos + nAddSize, vinfoBlockFile[nFile].nSize); - else - vinfoBlockFile[nFile].nSize += nAddSize; - - if (!fKnown) { - unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; - unsigned int nNewChunks = (vinfoBlockFile[nFile].nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; - if (nNewChunks > nOldChunks) { - if (fPruneMode) - fCheckForPruning = true; - if (CheckDiskSpace(nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos)) { - FILE *file = OpenBlockFile(pos); - if (file) { - LogPrintf("Pre-allocating up to position 0x%x in blk%05u.dat\n", nNewChunks * BLOCKFILE_CHUNK_SIZE, pos.nFile); - AllocateFileRange(file, pos.nPos, nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos); - fclose(file); - } - } - else - return state.Error("out of disk space"); - } - } - - setDirtyFileInfo.insert(nFile); - return true; -} - -bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize) -{ - pos.nFile = nFile; - - LOCK(cs_LastBlockFile); - - unsigned int nNewSize; - pos.nPos = vinfoBlockFile[nFile].nUndoSize; - nNewSize = vinfoBlockFile[nFile].nUndoSize += nAddSize; - setDirtyFileInfo.insert(nFile); - - unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; - unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; - if (nNewChunks > nOldChunks) { - if (fPruneMode) - fCheckForPruning = true; - if (CheckDiskSpace(nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos)) { - FILE *file = OpenUndoFile(pos); - if (file) { - LogPrintf("Pre-allocating up to position 0x%x in rev%05u.dat\n", nNewChunks * UNDOFILE_CHUNK_SIZE, pos.nFile); - AllocateFileRange(file, pos.nPos, nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos); - fclose(file); - } - } - else - return state.Error("out of disk space"); - } - - return true; -} - -bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW) -{ - // Check proof of work matches claimed amount - // We don't have block height as this is called without context (i.e. without - // knowing the previous block), but that's okay, as the checks done are permissive - // (i.e. doesn't check work limit or whether AuxPoW is enabled) - if (fCheckPOW && !CheckAuxPowProofOfWork(block, Params().GetConsensus(0))) - return state.DoS(50, error("CheckBlockHeader(): proof of work failed"), - REJECT_INVALID, "high-hash"); - - // Check timestamp - if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) - return state.Invalid(error("CheckBlockHeader(): block timestamp too far in the future"), - REJECT_INVALID, "time-too-new"); - - return true; -} - -bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bool fCheckMerkleRoot) -{ - // These are checks that are independent of context. - - // Check that the header is valid (particularly PoW). This is mostly - // redundant with the call in AcceptBlockHeader. - if (!CheckBlockHeader(block, state, fCheckPOW)) - return false; - - // Check the merkle root. - if (fCheckMerkleRoot) { - bool mutated; - uint256 hashMerkleRoot2 = block.BuildMerkleTree(&mutated); - if (block.hashMerkleRoot != hashMerkleRoot2) - return state.DoS(100, error("CheckBlock(): hashMerkleRoot mismatch"), - REJECT_INVALID, "bad-txnmrklroot", true); - - // Check for merkle tree malleability (CVE-2012-2459): repeating sequences - // of transactions in a block without affecting the merkle root of a block, - // while still invalidating it. - if (mutated) - return state.DoS(100, error("CheckBlock(): duplicate transaction"), - REJECT_INVALID, "bad-txns-duplicate", true); - } - - // All potential-corruption validation must be done before we do any - // transaction validation, as otherwise we may mark the header as invalid - // because we receive the wrong transactions for it. - - // Size limits - if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return state.DoS(100, error("CheckBlock(): size limits failed"), - REJECT_INVALID, "bad-blk-length"); - - // First transaction must be coinbase, the rest must not be - if (block.vtx.empty() || !block.vtx[0].IsCoinBase()) - return state.DoS(100, error("CheckBlock(): first tx is not coinbase"), - REJECT_INVALID, "bad-cb-missing"); - for (unsigned int i = 1; i < block.vtx.size(); i++) - if (block.vtx[i].IsCoinBase()) - return state.DoS(100, error("CheckBlock(): more than one coinbase"), - REJECT_INVALID, "bad-cb-multiple"); - - // Check transactions - BOOST_FOREACH(const CTransaction& tx, block.vtx) - if (!CheckTransaction(tx, state)) - return error("CheckBlock(): CheckTransaction failed"); - - unsigned int nSigOps = 0; - BOOST_FOREACH(const CTransaction& tx, block.vtx) - { - nSigOps += GetLegacySigOpCount(tx); - } - if (nSigOps > MAX_BLOCK_SIGOPS) - return state.DoS(100, error("CheckBlock(): out-of-bounds SigOpCount"), - REJECT_INVALID, "bad-blk-sigops", true); - - return true; -} - -bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex * const pindexPrev) -{ - const CChainParams& chainParams = Params(); - uint256 hash = block.GetHash(); - if (hash == chainParams.GetConsensus(0).hashGenesisBlock) - return true; - - assert(pindexPrev); - - int nHeight = pindexPrev->nHeight+1; - const Consensus::Params& consensusParams = chainParams.GetConsensus(nHeight); - - // Disallow legacy blocks after merge-mining start. - if (!consensusParams.fAllowLegacyBlocks - && block.nVersion.IsLegacy()) - return state.DoS(100, error("%s : legacy block after auxpow start at height %d, parameters effective from %d", - __func__, pindexPrev->nHeight + 1, consensusParams.nHeightEffective), - REJECT_INVALID, "late-legacy-block"); - - // Disallow AuxPow blocks before it is activated. - if (!consensusParams.fAllowAuxPow - && block.nVersion.IsAuxpow()) - return state.DoS(100, error("%s : auxpow blocks are not allowed at height %d, parameters effective from %d", - __func__, pindexPrev->nHeight + 1, consensusParams.nHeightEffective), - REJECT_INVALID, "early-auxpow-block"); - - // Check proof of work - if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) - return state.DoS(100, error("%s: incorrect proof of work at height %d", __func__, nHeight), - REJECT_INVALID, "bad-diffbits"); - - // Check timestamp against prev - if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) - return state.Invalid(error("%s: block's timestamp is too early", __func__), - REJECT_INVALID, "time-too-old"); - - if(fCheckpointsEnabled) - { - // Check that the block chain matches the known block chain up to a checkpoint - if (!Checkpoints::CheckBlock(chainParams.Checkpoints(), nHeight, hash)) - return state.DoS(100, error("%s: rejected by checkpoint lock-in at %d", __func__, nHeight), - REJECT_CHECKPOINT, "checkpoint mismatch"); - - // Don't accept any forks from the main chain prior to last checkpoint - CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(chainParams.Checkpoints()); - if (pcheckpoint && nHeight < pcheckpoint->nHeight) - return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight)); - } - - // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: - // Dogecoin: Version 2 enforcement was never used, reject from v3 softfork - //if (block.nVersion < 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) - // return state.Invalid(error("%s: rejected nVersion=1 block", __func__), - // REJECT_OBSOLETE, "bad-version"); - - // Reject block.nVersion=2 blocks when 95% (75% on testnet) of the network has upgraded: - if (block.nVersion < 3 && IsSuperMajority(3, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) - return state.Invalid(error("%s : rejected nVersion=2 block", __func__), - REJECT_OBSOLETE, "bad-version"); - - return true; -} - -bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex * const pindexPrev) -{ - const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1; - - const CChainParams& chainParams = Params(); - const Consensus::Params& consensusParams = chainParams.GetConsensus(nHeight); - - // Check that all transactions are finalized - BOOST_FOREACH(const CTransaction& tx, block.vtx) - if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) { - return state.DoS(10, error("%s: contains a non-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal"); - } - - // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height - // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): - // Dogecoin: Block v2 was never enforced, so we trigger this against v3 - if (block.nVersion >= 2 && IsSuperMajority(3, pindexPrev, consensusParams.nMajorityEnforceBlockUpgrade, consensusParams)) - { - CScript expect = CScript() << nHeight; - if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || - !std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) { - return state.DoS(100, error("%s: block height mismatch in coinbase", __func__), REJECT_INVALID, "bad-cb-height"); - } - } - - return true; -} - -bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex** ppindex) -{ - const CChainParams& chainparams = Params(); - AssertLockHeld(cs_main); - // Check for duplicate - uint256 hash = block.GetHash(); - BlockMap::iterator miSelf = mapBlockIndex.find(hash); - CBlockIndex *pindex = NULL; - if (miSelf != mapBlockIndex.end()) { - // Block header is already known. - pindex = miSelf->second; - if (ppindex) - *ppindex = pindex; - if (pindex->nStatus & BLOCK_FAILED_MASK) - return state.Invalid(error("%s: block is marked invalid", __func__), 0, "duplicate"); - return true; - } - - if (!CheckBlockHeader(block, state)) - return false; - - // Get prev block index - CBlockIndex* pindexPrev = NULL; - if (hash != chainparams.GetConsensus(0).hashGenesisBlock) { - BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); - if (mi == mapBlockIndex.end()) - return state.DoS(10, error("%s: prev block not found", __func__), 0, "bad-prevblk"); - pindexPrev = (*mi).second; - if (pindexPrev->nStatus & BLOCK_FAILED_MASK) - return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); - } - - if (!ContextualCheckBlockHeader(block, state, pindexPrev)) - return false; - - if (pindex == NULL) - pindex = AddToBlockIndex(block); - - if (ppindex) - *ppindex = pindex; - - return true; -} - -bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, bool fRequested, CDiskBlockPos* dbp) -{ - const CChainParams& chainparams = Params(); - AssertLockHeld(cs_main); - - CBlockIndex *&pindex = *ppindex; - - if (!AcceptBlockHeader(block, state, &pindex)) - return false; - - // Try to process all requested blocks that we don't have, but only - // process an unrequested block if it's new and has enough work to - // advance our tip. - bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA; - bool fHasMoreWork = (chainActive.Tip() ? pindex->nChainWork > chainActive.Tip()->nChainWork : true); - - // TODO: deal better with return value and error conditions for duplicate - // and unrequested blocks. - if (fAlreadyHave) return true; - if (!fRequested) { // If we didn't ask for it: - if (pindex->nTx != 0) return true; // This is a previously-processed block that was pruned - if (!fHasMoreWork) return true; // Don't process less-work chains - } - - if ((!CheckBlock(block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) { - if (state.IsInvalid() && !state.CorruptionPossible()) { - pindex->nStatus |= BLOCK_FAILED_VALID; - setDirtyBlockIndex.insert(pindex); - } - return false; - } - - int nHeight = pindex->nHeight; - - // Write block to history file - try { - unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); - CDiskBlockPos blockPos; - if (dbp != NULL) - blockPos = *dbp; - if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, block.GetBlockTime(), dbp != NULL)) - return error("AcceptBlock(): FindBlockPos failed"); - if (dbp == NULL) - if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) - AbortNode(state, "Failed to write block"); - if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) - return error("AcceptBlock(): ReceivedBlockTransactions failed"); - } catch (const std::runtime_error& e) { - return AbortNode(state, std::string("System error: ") + e.what()); - } - - if (fCheckForPruning) - FlushStateToDisk(state, FLUSH_STATE_NONE); // we just allocated more disk space for block files - - return true; -} - -static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned nRequired, const Consensus::Params& consensusParams) -{ - unsigned int nFound = 0; - for (int i = 0; i < consensusParams.nMajorityWindow && nFound < nRequired && pstart != NULL; i++) - { - if (pstart->nVersion >= minVersion) - ++nFound; - pstart = pstart->pprev; - } - return (nFound >= nRequired); -} - - -bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp) -{ - // Preliminary checks - bool checked = CheckBlock(*pblock, state); - - { - LOCK(cs_main); - bool fRequested = MarkBlockAsReceived(pblock->GetHash()); - fRequested |= fForceProcessing; - if (!checked) { - return error("%s: CheckBlock FAILED", __func__); - } - - // Store to disk - CBlockIndex *pindex = NULL; - bool ret = AcceptBlock(*pblock, state, &pindex, fRequested, dbp); - if (pindex && pfrom) { - mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId(); - } - CheckBlockIndex(); - if (!ret) - return error("%s: AcceptBlock FAILED", __func__); - } - - if (!ActivateBestChain(state, pblock)) - return error("%s: ActivateBestChain failed", __func__); - - return true; -} - -bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex * const pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot) -{ - AssertLockHeld(cs_main); - assert(pindexPrev == chainActive.Tip()); - - CCoinsViewCache viewNew(pcoinsTip); - CBlockIndex indexDummy(block); - indexDummy.pprev = pindexPrev; - indexDummy.nHeight = pindexPrev->nHeight + 1; - - // NOTE: CheckBlockHeader is called by CheckBlock - if (!ContextualCheckBlockHeader(block, state, pindexPrev)) - return false; - if (!CheckBlock(block, state, fCheckPOW, fCheckMerkleRoot)) - return false; - if (!ContextualCheckBlock(block, state, pindexPrev)) - return false; - if (!ConnectBlock(block, state, &indexDummy, viewNew, true)) - return false; - assert(state.IsValid()); - - return true; -} - -/** - * BLOCK PRUNING CODE - */ - -/* Calculate the amount of disk space the block & undo files currently use */ -uint64_t CalculateCurrentUsage() -{ - uint64_t retval = 0; - BOOST_FOREACH(const CBlockFileInfo &file, vinfoBlockFile) { - retval += file.nSize + file.nUndoSize; - } - return retval; -} - -/* Prune a block file (modify associated database entries)*/ -void PruneOneBlockFile(const int fileNumber) -{ - for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); ++it) { - CBlockIndex* pindex = it->second; - if (pindex->nFile == fileNumber) { - pindex->nStatus &= ~BLOCK_HAVE_DATA; - pindex->nStatus &= ~BLOCK_HAVE_UNDO; - pindex->nFile = 0; - pindex->nDataPos = 0; - pindex->nUndoPos = 0; - setDirtyBlockIndex.insert(pindex); - - // Prune from mapBlocksUnlinked -- any block we prune would have - // to be downloaded again in order to consider its chain, at which - // point it would be considered as a candidate for - // mapBlocksUnlinked or setBlockIndexCandidates. - std::pair<std::multimap<CBlockIndex*, CBlockIndex*>::iterator, std::multimap<CBlockIndex*, CBlockIndex*>::iterator> range = mapBlocksUnlinked.equal_range(pindex->pprev); - while (range.first != range.second) { - std::multimap<CBlockIndex *, CBlockIndex *>::iterator it = range.first; - range.first++; - if (it->second == pindex) { - mapBlocksUnlinked.erase(it); - } - } - } - } - - vinfoBlockFile[fileNumber].SetNull(); - setDirtyFileInfo.insert(fileNumber); -} - - -void UnlinkPrunedFiles(std::set<int>& setFilesToPrune) -{ - for (set<int>::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) { - CDiskBlockPos pos(*it, 0); - boost::filesystem::remove(GetBlockPosFilename(pos, "blk")); - boost::filesystem::remove(GetBlockPosFilename(pos, "rev")); - LogPrintf("Prune: %s deleted blk/rev (%05u)\n", __func__, *it); - } -} - -/* Calculate the block/rev files that should be deleted to remain under target*/ -void FindFilesToPrune(std::set<int>& setFilesToPrune) -{ - LOCK2(cs_main, cs_LastBlockFile); - if (chainActive.Tip() == NULL || nPruneTarget == 0) { - return; - } - if (chainActive.Tip()->nHeight <= Params().PruneAfterHeight()) { - return; - } - - unsigned int nLastBlockWeCanPrune = chainActive.Tip()->nHeight - MIN_BLOCKS_TO_KEEP; - uint64_t nCurrentUsage = CalculateCurrentUsage(); - // We don't check to prune until after we've allocated new space for files - // So we should leave a buffer under our target to account for another allocation - // before the next pruning. - uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE; - uint64_t nBytesToPrune; - int count=0; - - if (nCurrentUsage + nBuffer >= nPruneTarget) { - for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) { - nBytesToPrune = vinfoBlockFile[fileNumber].nSize + vinfoBlockFile[fileNumber].nUndoSize; - - if (vinfoBlockFile[fileNumber].nSize == 0) - continue; - - if (nCurrentUsage + nBuffer < nPruneTarget) // are we below our target? - break; - - // don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning - if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) - continue; - - PruneOneBlockFile(fileNumber); - // Queue up the files for removal - setFilesToPrune.insert(fileNumber); - nCurrentUsage -= nBytesToPrune; - count++; - } - } - - LogPrint("prune", "Prune: target=%dMiB actual=%dMiB diff=%dMiB max_prune_height=%d removed %d blk/rev pairs\n", - nPruneTarget/1024/1024, nCurrentUsage/1024/1024, - ((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024, - nLastBlockWeCanPrune, count); -} - -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; -} - -FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly) -{ - if (pos.IsNull()) - return NULL; - boost::filesystem::path path = GetBlockPosFilename(pos, prefix); - boost::filesystem::create_directories(path.parent_path()); - FILE* file = fopen(path.string().c_str(), "rb+"); - if (!file && !fReadOnly) - file = fopen(path.string().c_str(), "wb+"); - if (!file) { - LogPrintf("Unable to open file %s\n", path.string()); - return NULL; - } - if (pos.nPos) { - if (fseek(file, pos.nPos, SEEK_SET)) { - LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, path.string()); - fclose(file); - return NULL; - } - } - return file; -} - -FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly) { - return OpenDiskFile(pos, "blk", fReadOnly); -} - -FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) { - return OpenDiskFile(pos, "rev", fReadOnly); -} - -boost::filesystem::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix) -{ - return GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile); -} - -CBlockIndex * InsertBlockIndex(uint256 hash) -{ - if (hash.IsNull()) - return NULL; - - // Return existing - BlockMap::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) - return (*mi).second; - - // Create new - CBlockIndex* pindexNew = new CBlockIndex(); - if (!pindexNew) - throw runtime_error("LoadBlockIndex(): new CBlockIndex failed"); - mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; - pindexNew->phashBlock = &((*mi).first); - - return pindexNew; -} - -bool static LoadBlockIndexDB() -{ - const CChainParams& chainparams = Params(); - if (!pblocktree->LoadBlockIndexGuts()) - return false; - - boost::this_thread::interruption_point(); - - // Calculate nChainWork - vector<pair<int, CBlockIndex*> > vSortedByHeight; - vSortedByHeight.reserve(mapBlockIndex.size()); - BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) - { - CBlockIndex* pindex = item.second; - vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex)); - } - sort(vSortedByHeight.begin(), vSortedByHeight.end()); - BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight) - { - CBlockIndex* pindex = item.second; - pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex); - // We can link the chain of blocks for which we've received transactions at some point. - // Pruned nodes may have deleted the block. - if (pindex->nTx > 0) { - if (pindex->pprev) { - if (pindex->pprev->nChainTx) { - pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx; - } else { - pindex->nChainTx = 0; - mapBlocksUnlinked.insert(std::make_pair(pindex->pprev, pindex)); - } - } else { - pindex->nChainTx = pindex->nTx; - } - } - if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == NULL)) - setBlockIndexCandidates.insert(pindex); - if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork)) - pindexBestInvalid = pindex; - if (pindex->pprev) - pindex->BuildSkip(); - if (pindex->IsValid(BLOCK_VALID_TREE) && (pindexBestHeader == NULL || CBlockIndexWorkComparator()(pindexBestHeader, pindex))) - pindexBestHeader = pindex; - } - - // Load block file info - pblocktree->ReadLastBlockFile(nLastBlockFile); - vinfoBlockFile.resize(nLastBlockFile + 1); - LogPrintf("%s: last block file = %i\n", __func__, nLastBlockFile); - for (int nFile = 0; nFile <= nLastBlockFile; nFile++) { - pblocktree->ReadBlockFileInfo(nFile, vinfoBlockFile[nFile]); - } - LogPrintf("%s: last block file info: %s\n", __func__, vinfoBlockFile[nLastBlockFile].ToString()); - for (int nFile = nLastBlockFile + 1; true; nFile++) { - CBlockFileInfo info; - if (pblocktree->ReadBlockFileInfo(nFile, info)) { - vinfoBlockFile.push_back(info); - } else { - break; - } - } - - // Check presence of blk files - LogPrintf("Checking all blk files are present...\n"); - set<int> setBlkDataFiles; - BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) - { - CBlockIndex* pindex = item.second; - if (pindex->nStatus & BLOCK_HAVE_DATA) { - setBlkDataFiles.insert(pindex->nFile); - } - } - for (std::set<int>::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++) - { - CDiskBlockPos pos(*it, 0); - if (CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION).IsNull()) { - return false; - } - } - - // Check whether we have ever pruned block & undo files - pblocktree->ReadFlag("prunedblockfiles", fHavePruned); - if (fHavePruned) - LogPrintf("LoadBlockIndexDB(): Block files have previously been pruned\n"); - - // Check whether we need to continue reindexing - bool fReindexing = false; - pblocktree->ReadReindexing(fReindexing); - fReindex |= fReindexing; - - // Check whether we have a transaction index - pblocktree->ReadFlag("txindex", fTxIndex); - LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled"); - - // Load pointer to end of best chain - BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); - if (it == mapBlockIndex.end()) - return true; - chainActive.SetTip(it->second); - - PruneBlockIndexCandidates(); - - LogPrintf("%s: hashBestChain=%s height=%d date=%s progress=%f\n", __func__, - chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), - Checkpoints::GuessVerificationProgress(chainparams.Checkpoints(), chainActive.Tip())); - - return true; -} - -CVerifyDB::CVerifyDB() -{ - uiInterface.ShowProgress(_("Verifying blocks..."), 0); -} - -CVerifyDB::~CVerifyDB() -{ - uiInterface.ShowProgress("", 100); -} - -bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth) -{ - LOCK(cs_main); - if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL) - return true; - - // Verify blocks in the best chain - if (nCheckDepth <= 0) - nCheckDepth = 1000000000; // suffices until the year 19000 - if (nCheckDepth > chainActive.Height()) - nCheckDepth = chainActive.Height(); - nCheckLevel = std::max(0, std::min(4, nCheckLevel)); - LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); - CCoinsViewCache coins(coinsview); - CBlockIndex* pindexState = chainActive.Tip(); - CBlockIndex* pindexFailure = NULL; - int nGoodTransactions = 0; - CValidationState state; - for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) - { - boost::this_thread::interruption_point(); - uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))))); - if (pindex->nHeight < chainActive.Height()-nCheckDepth) - break; - CBlock block; - // check level 0: read from disk - if (!ReadBlockFromDisk(block, pindex)) - return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); - // check level 1: verify block validity - if (nCheckLevel >= 1 && !CheckBlock(block, state)) - return error("VerifyDB(): *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); - // check level 2: verify undo validity - if (nCheckLevel >= 2 && pindex) { - CBlockUndo undo; - CDiskBlockPos pos = pindex->GetUndoPos(); - if (!pos.IsNull()) { - if (!UndoReadFromDisk(undo, pos, pindex->pprev->GetBlockHash())) - return error("VerifyDB(): *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); - } - } - // check level 3: check for inconsistencies during memory-only disconnect of tip blocks - if (nCheckLevel >= 3 && pindex == pindexState && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) { - bool fClean = true; - if (!DisconnectBlock(block, state, pindex, coins, &fClean)) - return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); - pindexState = pindex->pprev; - if (!fClean) { - nGoodTransactions = 0; - pindexFailure = pindex; - } else - nGoodTransactions += block.vtx.size(); - } - if (ShutdownRequested()) - return true; - } - if (pindexFailure) - return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainActive.Height() - pindexFailure->nHeight + 1, nGoodTransactions); - - // check level 4: try reconnecting blocks - if (nCheckLevel >= 4) { - CBlockIndex *pindex = pindexState; - while (pindex != chainActive.Tip()) { - boost::this_thread::interruption_point(); - uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50)))); - pindex = chainActive.Next(pindex); - CBlock block; - if (!ReadBlockFromDisk(block, pindex)) - return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); - if (!ConnectBlock(block, state, pindex, coins)) - return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); - } - } - - LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", chainActive.Height() - pindexState->nHeight, nGoodTransactions); - - return true; -} - -void UnloadBlockIndex() -{ - LOCK(cs_main); - setBlockIndexCandidates.clear(); - chainActive.SetTip(NULL); - pindexBestInvalid = NULL; - pindexBestHeader = NULL; - mempool.clear(); - mapOrphanTransactions.clear(); - mapOrphanTransactionsByPrev.clear(); - nSyncStarted = 0; - mapBlocksUnlinked.clear(); - vinfoBlockFile.clear(); - nLastBlockFile = 0; - nBlockSequenceId = 1; - mapBlockSource.clear(); - mapBlocksInFlight.clear(); - nQueuedValidatedHeaders = 0; - nPreferredDownload = 0; - setDirtyBlockIndex.clear(); - setDirtyFileInfo.clear(); - mapNodeState.clear(); - - BOOST_FOREACH(BlockMap::value_type& entry, mapBlockIndex) { - delete entry.second; - } - mapBlockIndex.clear(); - fHavePruned = false; -} - -bool LoadBlockIndex() -{ - // Load block index from databases - if (!fReindex && !LoadBlockIndexDB()) - return false; - return true; -} - - -bool InitBlockIndex() { - const CChainParams& chainparams = Params(); - LOCK(cs_main); - // Check whether we're already initialized - if (chainActive.Genesis() != NULL) - return true; - - // Use the provided setting for -txindex in the new database - fTxIndex = GetBoolArg("-txindex", false); - pblocktree->WriteFlag("txindex", fTxIndex); - LogPrintf("Initializing databases...\n"); - - // Only add the genesis block if not reindexing (in which case we reuse the one already on disk) - if (!fReindex) { - try { - CBlock &block = const_cast<CBlock&>(Params().GenesisBlock()); - // Start new block file - unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); - CDiskBlockPos blockPos; - CValidationState state; - if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.GetBlockTime())) - return error("LoadBlockIndex(): FindBlockPos failed"); - if (!WriteBlockToDisk(block, blockPos, chainparams.MessageStart())) - return error("LoadBlockIndex(): writing genesis block to disk failed"); - CBlockIndex *pindex = AddToBlockIndex(block); - if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) - return error("LoadBlockIndex(): genesis block not accepted"); - if (!ActivateBestChain(state, &block)) - return error("LoadBlockIndex(): genesis block cannot be activated"); - // Force a chainstate write so that when we VerifyDB in a moment, it doesn't check stale data - return FlushStateToDisk(state, FLUSH_STATE_ALWAYS); - } catch (const std::runtime_error& e) { - return error("LoadBlockIndex(): failed to initialize block database: %s", e.what()); - } - } - - return true; -} - - - -bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) -{ - const CChainParams& chainparams = Params(); - // Map of disk positions for blocks with unknown parent (only used for reindex) - static std::multimap<uint256, CDiskBlockPos> mapBlocksUnknownParent; - int64_t nStart = GetTimeMillis(); - - int nLoaded = 0; - try { - // This takes over fileIn and calls fclose() on it in the CBufferedFile destructor - CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION); - uint64_t nRewind = blkdat.GetPos(); - while (!blkdat.eof()) { - boost::this_thread::interruption_point(); - - blkdat.SetPos(nRewind); - nRewind++; // start one byte further next time, in case of failure - blkdat.SetLimit(); // remove former limit - unsigned int nSize = 0; - try { - // locate a header - unsigned char buf[MESSAGE_START_SIZE]; - blkdat.FindByte(Params().MessageStart()[0]); - nRewind = blkdat.GetPos()+1; - blkdat >> FLATDATA(buf); - if (memcmp(buf, Params().MessageStart(), MESSAGE_START_SIZE)) - continue; - // read size - blkdat >> nSize; - if (nSize < 80 || nSize > MAX_BLOCK_SIZE) - continue; - } catch (const std::exception&) { - // no valid block header found; don't complain - break; - } - try { - // read block - uint64_t nBlockPos = blkdat.GetPos(); - if (dbp) - dbp->nPos = nBlockPos; - blkdat.SetLimit(nBlockPos + nSize); - blkdat.SetPos(nBlockPos); - CBlock block; - blkdat >> block; - nRewind = blkdat.GetPos(); - - // detect out of order blocks, and store them for later - uint256 hash = block.GetHash(); - if (hash != chainparams.GetConsensus(0).hashGenesisBlock && mapBlockIndex.find(block.hashPrevBlock) == mapBlockIndex.end()) { - LogPrint("reindex", "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(), - block.hashPrevBlock.ToString()); - if (dbp) - mapBlocksUnknownParent.insert(std::make_pair(block.hashPrevBlock, *dbp)); - continue; - } - - // process in case the block isn't known yet - if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) { - CValidationState state; - if (ProcessNewBlock(state, NULL, &block, true, dbp)) - nLoaded++; - if (state.IsError()) - break; - } else if (hash != chainparams.GetConsensus(0).hashGenesisBlock && mapBlockIndex[hash]->nHeight % 1000 == 0) { - LogPrintf("Block Import: already had block %s at height %d\n", hash.ToString(), mapBlockIndex[hash]->nHeight); - } - - // Recursively process earlier encountered successors of this block - deque<uint256> queue; - queue.push_back(hash); - while (!queue.empty()) { - uint256 head = queue.front(); - queue.pop_front(); - std::pair<std::multimap<uint256, CDiskBlockPos>::iterator, std::multimap<uint256, CDiskBlockPos>::iterator> range = mapBlocksUnknownParent.equal_range(head); - while (range.first != range.second) { - std::multimap<uint256, CDiskBlockPos>::iterator it = range.first; - if (ReadBlockFromDisk(block, it->second)) - { - LogPrintf("%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(), - head.ToString()); - CValidationState dummy; - if (ProcessNewBlock(dummy, NULL, &block, true, &it->second)) - { - nLoaded++; - queue.push_back(block.GetHash()); - } - } - range.first++; - mapBlocksUnknownParent.erase(it); - } - } - } catch (const std::exception& e) { - LogPrintf("%s: Deserialize or I/O error - %s\n", __func__, e.what()); - } - } - } catch (const std::runtime_error& e) { - AbortNode(std::string("System error: ") + e.what()); - } - if (nLoaded > 0) - LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, GetTimeMillis() - nStart); - return nLoaded > 0; -} - -void static CheckBlockIndex() -{ - if (!fCheckBlockIndex) { - return; - } - - LOCK(cs_main); - - // During a reindex, we read the genesis block and call CheckBlockIndex before ActivateBestChain, - // so we have the genesis block in mapBlockIndex but no active chain. (A few of the tests when - // iterating the block tree require that chainActive has been initialized.) - if (chainActive.Height() < 0) { - assert(mapBlockIndex.size() <= 1); - return; - } - - - // Build forward-pointing map of the entire block tree. - std::multimap<CBlockIndex*,CBlockIndex*> forward; - for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { - forward.insert(std::make_pair(it->second->pprev, it->second)); - } - - assert(forward.size() == mapBlockIndex.size()); - - std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> rangeGenesis = forward.equal_range(NULL); - CBlockIndex *pindex = rangeGenesis.first->second; - rangeGenesis.first++; - assert(rangeGenesis.first == rangeGenesis.second); // There is only one index entry with parent NULL. - - // Iterate over the entire block tree, using depth-first search. - // Along the way, remember whether there are blocks on the path from genesis - // block being explored which are the first to have certain properties. - size_t nNodes = 0; - int nHeight = 0; - CBlockIndex* pindexFirstInvalid = NULL; // Oldest ancestor of pindex which is invalid. - CBlockIndex* pindexFirstMissing = NULL; // Oldest ancestor of pindex which does not have BLOCK_HAVE_DATA. - CBlockIndex* pindexFirstNeverProcessed = NULL; // Oldest ancestor of pindex for which nTx == 0. - CBlockIndex* pindexFirstNotTreeValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_TREE (regardless of being valid or not). - CBlockIndex* pindexFirstNotTransactionsValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_TRANSACTIONS (regardless of being valid or not). - CBlockIndex* pindexFirstNotChainValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_CHAIN (regardless of being valid or not). - CBlockIndex* pindexFirstNotScriptsValid = NULL; // Oldest ancestor of pindex which does not have BLOCK_VALID_SCRIPTS (regardless of being valid or not). - while (pindex != NULL) { - const Consensus::Params& consensusParams = Params().GetConsensus(nHeight); - nNodes++; - if (pindexFirstInvalid == NULL && pindex->nStatus & BLOCK_FAILED_VALID) pindexFirstInvalid = pindex; - if (pindexFirstMissing == NULL && !(pindex->nStatus & BLOCK_HAVE_DATA)) pindexFirstMissing = pindex; - if (pindexFirstNeverProcessed == NULL && pindex->nTx == 0) pindexFirstNeverProcessed = pindex; - if (pindex->pprev != NULL && pindexFirstNotTreeValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TREE) pindexFirstNotTreeValid = pindex; - if (pindex->pprev != NULL && pindexFirstNotTransactionsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TRANSACTIONS) pindexFirstNotTransactionsValid = pindex; - if (pindex->pprev != NULL && pindexFirstNotChainValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) pindexFirstNotChainValid = pindex; - if (pindex->pprev != NULL && pindexFirstNotScriptsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) pindexFirstNotScriptsValid = pindex; - - // Begin: actual consistency checks. - if (pindex->pprev == NULL) { - // Genesis block checks. - assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock); // Genesis block's hash must match. - assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block. - } - if (pindex->nChainTx == 0) assert(pindex->nSequenceId == 0); // nSequenceId can't be set for blocks that aren't linked - // VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred). - // HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred. - if (!fHavePruned) { - // If we've never pruned, then HAVE_DATA should be equivalent to nTx > 0 - assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0)); - assert(pindexFirstMissing == pindexFirstNeverProcessed); - } else { - // If we have pruned, then we can only say that HAVE_DATA implies nTx > 0 - if (pindex->nStatus & BLOCK_HAVE_DATA) assert(pindex->nTx > 0); - } - if (pindex->nStatus & BLOCK_HAVE_UNDO) assert(pindex->nStatus & BLOCK_HAVE_DATA); - assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); // This is pruning-independent. - // All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to nChainTx being set. - assert((pindexFirstNeverProcessed != NULL) == (pindex->nChainTx == 0)); // nChainTx != 0 is used to signal that all parent blocks have been processed (but may have been pruned). - assert((pindexFirstNotTransactionsValid != NULL) == (pindex->nChainTx == 0)); - assert(pindex->nHeight == nHeight); // nHeight must be consistent. - assert(pindex->pprev == NULL || pindex->nChainWork >= pindex->pprev->nChainWork); // For every block except the genesis block, the chainwork must be larger than the parent's. - assert(nHeight < 2 || (pindex->pskip && (pindex->pskip->nHeight < nHeight))); // The pskip pointer must point back for all but the first 2 blocks. - assert(pindexFirstNotTreeValid == NULL); // All mapBlockIndex entries must at least be TREE valid - if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TREE) assert(pindexFirstNotTreeValid == NULL); // TREE valid implies all parents are TREE valid - if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_CHAIN) assert(pindexFirstNotChainValid == NULL); // CHAIN valid implies all parents are CHAIN valid - if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_SCRIPTS) assert(pindexFirstNotScriptsValid == NULL); // SCRIPTS valid implies all parents are SCRIPTS valid - if (pindexFirstInvalid == NULL) { - // Checks for not-invalid blocks. - assert((pindex->nStatus & BLOCK_FAILED_MASK) == 0); // The failed mask cannot be set for blocks without invalid parents. - } - if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && pindexFirstNeverProcessed == NULL) { - if (pindexFirstInvalid == NULL) { - // If this block sorts at least as good as the current tip and - // is valid and we have all data for its parents, it must be in - // setBlockIndexCandidates. chainActive.Tip() must also be there - // even if some data has been pruned. - if (pindexFirstMissing == NULL || pindex == chainActive.Tip()) { - assert(setBlockIndexCandidates.count(pindex)); - } - // If some parent is missing, then it could be that this block was in - // setBlockIndexCandidates but had to be removed because of the missing data. - // In this case it must be in mapBlocksUnlinked -- see test below. - } - } else { // If this block sorts worse than the current tip or some ancestor's block has never been seen, it cannot be in setBlockIndexCandidates. - assert(setBlockIndexCandidates.count(pindex) == 0); - } - // Check whether this block is in mapBlocksUnlinked. - std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> rangeUnlinked = mapBlocksUnlinked.equal_range(pindex->pprev); - bool foundInUnlinked = false; - while (rangeUnlinked.first != rangeUnlinked.second) { - assert(rangeUnlinked.first->first == pindex->pprev); - if (rangeUnlinked.first->second == pindex) { - foundInUnlinked = true; - break; - } - rangeUnlinked.first++; - } - if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed != NULL && pindexFirstInvalid == NULL) { - // If this block has block data available, some parent was never received, and has no invalid parents, it must be in mapBlocksUnlinked. - assert(foundInUnlinked); - } - if (!(pindex->nStatus & BLOCK_HAVE_DATA)) assert(!foundInUnlinked); // Can't be in mapBlocksUnlinked if we don't HAVE_DATA - if (pindexFirstMissing == NULL) assert(!foundInUnlinked); // We aren't missing data for any parent -- cannot be in mapBlocksUnlinked. - if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed == NULL && pindexFirstMissing != NULL) { - // We HAVE_DATA for this block, have received data for all parents at some point, but we're currently missing data for some parent. - assert(fHavePruned); // We must have pruned. - // This block may have entered mapBlocksUnlinked if: - // - it has a descendant that at some point had more work than the - // tip, and - // - we tried switching to that descendant but were missing - // data for some intermediate block between chainActive and the - // tip. - // So if this block is itself better than chainActive.Tip() and it wasn't in - // setBlockIndexCandidates, then it must be in mapBlocksUnlinked. - if (!CBlockIndexWorkComparator()(pindex, chainActive.Tip()) && setBlockIndexCandidates.count(pindex) == 0) { - if (pindexFirstInvalid == NULL) { - assert(foundInUnlinked); - } - } - } - // assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow - // End: actual consistency checks. - - // Try descending into the first subnode. - std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> range = forward.equal_range(pindex); - if (range.first != range.second) { - // A subnode was found. - pindex = range.first->second; - nHeight++; - continue; - } - // This is a leaf node. - // Move upwards until we reach a node of which we have not yet visited the last child. - while (pindex) { - // We are going to either move to a parent or a sibling of pindex. - // If pindex was the first with a certain property, unset the corresponding variable. - if (pindex == pindexFirstInvalid) pindexFirstInvalid = NULL; - if (pindex == pindexFirstMissing) pindexFirstMissing = NULL; - if (pindex == pindexFirstNeverProcessed) pindexFirstNeverProcessed = NULL; - if (pindex == pindexFirstNotTreeValid) pindexFirstNotTreeValid = NULL; - if (pindex == pindexFirstNotTransactionsValid) pindexFirstNotTransactionsValid = NULL; - if (pindex == pindexFirstNotChainValid) pindexFirstNotChainValid = NULL; - if (pindex == pindexFirstNotScriptsValid) pindexFirstNotScriptsValid = NULL; - // Find our parent. - CBlockIndex* pindexPar = pindex->pprev; - // Find which child we just visited. - std::pair<std::multimap<CBlockIndex*,CBlockIndex*>::iterator,std::multimap<CBlockIndex*,CBlockIndex*>::iterator> rangePar = forward.equal_range(pindexPar); - while (rangePar.first->second != pindex) { - assert(rangePar.first != rangePar.second); // Our parent must have at least the node we're coming from as child. - rangePar.first++; - } - // Proceed to the next one. - rangePar.first++; - if (rangePar.first != rangePar.second) { - // Move to the sibling. - pindex = rangePar.first->second; - break; - } else { - // Move up further. - pindex = pindexPar; - nHeight--; - continue; - } - } - } - - // Check that we actually traversed the entire map. - assert(nNodes == forward.size()); -} - -////////////////////////////////////////////////////////////////////////////// -// -// CAlert -// - -string GetWarnings(string strFor) -{ - int nPriority = 0; - string strStatusBar; - string strRPC; - - 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"); - - if (GetBoolArg("-testsafemode", false)) - strStatusBar = strRPC = "testsafemode enabled"; - - // Misc warnings like out of disk space and clock is wrong - if (strMiscWarning != "") - { - nPriority = 1000; - strStatusBar = strMiscWarning; - } - - if (fLargeWorkForkFound) - { - nPriority = 2000; - strStatusBar = strRPC = _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues."); - } - else if (fLargeWorkInvalidChainFound) - { - nPriority = 2000; - strStatusBar = strRPC = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."); - } - - // Alerts - { - LOCK(cs_mapAlerts); - BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) - { - const CAlert& alert = item.second; - if (alert.AppliesToMe() && alert.nPriority > nPriority) - { - nPriority = alert.nPriority; - strStatusBar = alert.strStatusBar; - } - } - } - - if (strFor == "statusbar") - return strStatusBar; - else if (strFor == "rpc") - return strRPC; - assert(!"GetWarnings(): invalid parameter"); - return "error"; -} - - - - - - - - -////////////////////////////////////////////////////////////////////////////// -// -// Messages -// - - -bool static AlreadyHave(const CInv& inv) -{ - switch (inv.type) - { - case MSG_TX: - { - bool txInMap = false; - txInMap = mempool.exists(inv.hash); - return txInMap || mapOrphanTransactions.count(inv.hash) || - pcoinsTip->HaveCoins(inv.hash); - } - case MSG_BLOCK: - return mapBlockIndex.count(inv.hash); - } - // Don't know what it is, just say we already got one - return true; -} - -void static ProcessGetData(CNode* pfrom) -{ - std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin(); - vector<CInv> vNotFound; - const Consensus::Params &consensus = Params().GetConsensus(chainActive.Height()); - - 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; - { - boost::this_thread::interruption_point(); - it++; - - if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) - { - bool send = false; - BlockMap::iterator mi = mapBlockIndex.find(inv.hash); - if (mi != mapBlockIndex.end()) - { - if (chainActive.Contains(mi->second)) { - 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->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != NULL) && - (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) && - (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, consensus) < nOneMonth); - if (!send) { - LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId()); - } - } - } - // Pruned nodes may have deleted the block, so check whether - // it's available before trying to send. - if (send && (mi->second->nStatus & BLOCK_HAVE_DATA)) - { - // Send block from disk - CBlock block; - if (!ReadBlockFromDisk(block, (*mi).second)) - assert(!"cannot load block from disk"); - if (inv.type == MSG_BLOCK) - pfrom->PushMessage("block", block); - else // MSG_FILTERED_BLOCK) - { - LOCK(pfrom->cs_filter); - if (pfrom->pfilter) - { - CMerkleBlock merkleBlock(block, *pfrom->pfilter); - pfrom->PushMessage("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<unsigned int, uint256> PairType; - BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn) - if (!pfrom->setInventoryKnown.count(CInv(MSG_TX, pair.second))) - pfrom->PushMessage("tx", block.vtx[pair.first]); - } - // else - // no response - } - - // 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. - vector<CInv> vInv; - vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); - pfrom->PushMessage("inv", vInv); - pfrom->hashContinue.SetNull(); - } - } - } - else if (inv.IsKnownType()) - { - // Send stream from relay memory - bool pushed = false; - { - LOCK(cs_mapRelay); - map<CInv, CDataStream>::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("tx", ss); - pushed = true; - } - } - if (!pushed) { - vNotFound.push_back(inv); - } - } - - // Track requests for our stuff. - GetMainSignals().Inventory(inv.hash); - - if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) - 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("notfound", vNotFound); - } -} - -bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived) -{ - const CChainParams& chainparams = Params(); - RandAddSeedPerfmon(); - LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); - if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) - { - LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); - return true; - } - - - - - if (strCommand == "version") - { - // Each connection can only send one version message - if (pfrom->nVersion != 0) - { - pfrom->PushMessage("reject", strCommand, REJECT_DUPLICATE, string("Duplicate version message")); - Misbehaving(pfrom->GetId(), 1); - return false; - } - - int64_t nTime; - CAddress addrMe; - CAddress addrFrom; - uint64_t nNonce = 1; - vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; - if (pfrom->nVersion < MIN_PEER_PROTO_VERSION) - { - // disconnect from peers older than this proto version - LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion); - pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, - strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION)); - pfrom->fDisconnect = true; - return false; - } - - if (pfrom->nVersion == 10300) - pfrom->nVersion = 300; - if (!vRecv.empty()) - vRecv >> addrFrom >> nNonce; - if (!vRecv.empty()) { - vRecv >> LIMITED_STRING(pfrom->strSubVer, 256); - 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; - - // Disconnect if we connected to ourself - if (nNonce == nLocalHostNonce && nNonce > 1) - { - LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString()); - 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("verack"); - pfrom->ssSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); - - if (!pfrom->fInbound) - { - // Advertise our address - if (fListen && !IsInitialBlockDownload()) - { - CAddress addr = GetLocalAddress(&pfrom->addr); - if (addr.IsRoutable()) - { - pfrom->PushAddress(addr); - } else if (IsPeerAddrLocalGood(pfrom)) { - addr.SetIP(pfrom->addrLocal); - pfrom->PushAddress(addr); - } - } - - // Get recent addresses - if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000) - { - pfrom->PushMessage("getaddr"); - pfrom->fGetAddr = true; - } - addrman.Good(pfrom->addr); - } else { - if (((CNetAddr)pfrom->addr) == (CNetAddr)addrFrom) - { - addrman.Add(addrFrom, addrFrom); - addrman.Good(addrFrom); - } - } - - // Relay alerts - { - LOCK(cs_mapAlerts); - BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) - item.second.RelayTo(pfrom); - } - - pfrom->fSuccessfullyConnected = true; - - string remoteAddr; - if (fLogIPs) - remoteAddr = ", peeraddr=" + pfrom->addr.ToString(); - - LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n", - pfrom->cleanSubVer, pfrom->nVersion, - pfrom->nStartingHeight, addrMe.ToString(), pfrom->id, - remoteAddr); - - 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 - Misbehaving(pfrom->GetId(), 1); - return false; - } - - - else if (strCommand == "verack") - { - pfrom->SetRecvVersion(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; - } - } - - - else if (strCommand == "addr") - { - vector<CAddress> 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 - vector<CAddress> vAddrOk; - int64_t nNow = GetAdjustedTime(); - int64_t nSince = nNow - 10 * 60; - BOOST_FOREACH(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)); - multimap<uint256, CNode*> mapMix; - BOOST_FOREACH(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(make_pair(hashKey, pnode)); - } - int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s) - for (multimap<uint256, CNode*>::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 == "inv") - { - vector<CInv> vInv; - vRecv >> vInv; - if (vInv.size() > MAX_INV_SZ) - { - Misbehaving(pfrom->GetId(), 20); - return error("message inv size() = %u", vInv.size()); - } - - LOCK(cs_main); - - std::vector<CInv> vToFetch; - - for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) - { - const CInv &inv = vInv[nInv]; - - boost::this_thread::interruption_point(); - pfrom->AddInventoryKnown(inv); - - bool fAlreadyHave = AlreadyHave(inv); - LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id); - - if (!fAlreadyHave && !fImporting && !fReindex && inv.type != MSG_BLOCK) - pfrom->AskFor(inv); - - if (inv.type == MSG_BLOCK) { - UpdateBlockAvailability(pfrom->GetId(), inv.hash); - if (!fAlreadyHave && !fImporting && !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("getheaders", chainActive.GetLocator(pindexBestHeader), inv.hash); - CNodeState *nodestate = State(pfrom->GetId()); - if (chainActive.Tip()->GetBlockTime() > GetAdjustedTime() - chainparams.GetConsensus(chainActive.Height()).nPowTargetSpacing * 20 && - nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { - vToFetch.push_back(inv); - // Mark block as in flight already, even though the actual "getdata" message only goes out - // later (within the same cs_main lock, though). - MarkBlockAsInFlight(pfrom->GetId(), inv.hash, chainparams.GetConsensus(chainActive.Height())); - } - LogPrint("net", "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->id); - } - } - - // Track requests for our stuff - GetMainSignals().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("getdata", vToFetch); - } - - - else if (strCommand == "getdata") - { - vector<CInv> vInv; - vRecv >> vInv; - if (vInv.size() > MAX_INV_SZ) - { - Misbehaving(pfrom->GetId(), 20); - return error("message getdata size() = %u", vInv.size()); - } - - if (fDebug || (vInv.size() != 1)) - LogPrint("net", "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->id); - - if ((fDebug && vInv.size() > 0) || (vInv.size() == 1)) - LogPrint("net", "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id); - - pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); - ProcessGetData(pfrom); - } - - - else if (strCommand == "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 == "getheaders") - { - CBlockLocator locator; - uint256 hashStop; - vRecv >> locator >> hashStop; - - LOCK(cs_main); - - if (IsInitialBlockDownload()) - return true; - - CBlockIndex* pindex = NULL; - if (locator.IsNull()) - { - // If locator is null, return the hashStop block - BlockMap::iterator mi = mapBlockIndex.find(hashStop); - if (mi == mapBlockIndex.end()) - return true; - pindex = (*mi).second; - } - else - { - // Find the last block the caller has in the main chain - pindex = FindForkInGlobalIndex(chainActive, locator); - if (pindex) - pindex = chainActive.Next(pindex); - } - - // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end - vector<CBlock> vHeaders; - int nLimit = MAX_HEADERS_RESULTS; - LogPrint("net", "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString(), pfrom->id); - for (; pindex; pindex = chainActive.Next(pindex)) - { - vHeaders.push_back(pindex->GetBlockHeader()); - if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) - break; - } - pfrom->PushMessage("headers", vHeaders); - } - - - else if (strCommand == "tx") - { - vector<uint256> vWorkQueue; - vector<uint256> vEraseQueue; - CTransaction tx; - vRecv >> tx; - - CInv inv(MSG_TX, tx.GetHash()); - pfrom->AddInventoryKnown(inv); - - LOCK(cs_main); - - bool fMissingInputs = false; - CValidationState state; - - mapAlreadyAskedFor.erase(inv); - - if (AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) - { - mempool.check(pcoinsTip); - RelayTransaction(tx); - vWorkQueue.push_back(inv.hash); - - LogPrint("mempool", "AcceptToMemoryPool: peer=%d %s: accepted %s (poolsz %u)\n", - pfrom->id, pfrom->cleanSubVer, - tx.GetHash().ToString(), - mempool.mapTx.size()); - - // Recursively process any orphan transactions that depended on this one - set<NodeId> setMisbehaving; - for (unsigned int i = 0; i < vWorkQueue.size(); i++) - { - map<uint256, set<uint256> >::iterator itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue[i]); - if (itByPrev == mapOrphanTransactionsByPrev.end()) - continue; - for (set<uint256>::iterator mi = itByPrev->second.begin(); - mi != itByPrev->second.end(); - ++mi) - { - const uint256& orphanHash = *mi; - const CTransaction& orphanTx = mapOrphanTransactions[orphanHash].tx; - NodeId fromPeer = mapOrphanTransactions[orphanHash].fromPeer; - bool fMissingInputs2 = false; - // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan - // resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get - // anyone relaying LegitTxX banned) - CValidationState stateDummy; - - - if (setMisbehaving.count(fromPeer)) - continue; - if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) - { - LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString()); - RelayTransaction(orphanTx); - vWorkQueue.push_back(orphanHash); - vEraseQueue.push_back(orphanHash); - } - else if (!fMissingInputs2) - { - int nDos = 0; - if (stateDummy.IsInvalid(nDos) && nDos > 0) - { - // Punish peer that gave us an invalid orphan tx - Misbehaving(fromPeer, nDos); - setMisbehaving.insert(fromPeer); - LogPrint("mempool", " invalid orphan tx %s\n", orphanHash.ToString()); - } - // Has inputs but not accepted to mempool - // Probably non-standard or insufficient fee/priority - LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString()); - vEraseQueue.push_back(orphanHash); - } - mempool.check(pcoinsTip); - } - } - - BOOST_FOREACH(uint256 hash, vEraseQueue) - EraseOrphanTx(hash); - } - else if (fMissingInputs) - { - AddOrphanTx(tx, pfrom->GetId()); - - // DoS prevention: do not allow mapOrphanTransactions to grow unbounded - unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS)); - unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx); - if (nEvicted > 0) - LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted); - } else if (pfrom->fWhitelisted) { - // Always relay transactions received from whitelisted peers, even - // if they are already in the mempool (allowing the node to function - // as a gateway for nodes hidden behind it). - RelayTransaction(tx); - } - int nDoS = 0; - if (state.IsInvalid(nDoS)) - { - LogPrint("mempool", "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), - pfrom->id, pfrom->cleanSubVer, - state.GetRejectReason()); - pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), - state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); - if (nDoS > 0) - Misbehaving(pfrom->GetId(), nDoS); - } - } - - - else if (strCommand == "headers" && !fImporting && !fReindex) // Ignore headers received while importing - { - std::vector<CBlockHeader> 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. - } - - LOCK(cs_main); - - if (nCount == 0) { - // Nothing interesting. Stop asking this peers for more headers. - return true; - } - - CBlockIndex *pindexLast = NULL; - BOOST_FOREACH(const CBlockHeader& header, headers) { - CValidationState state; - if (pindexLast != NULL && header.hashPrevBlock != pindexLast->GetBlockHash()) { - Misbehaving(pfrom->GetId(), 20); - return error("non-continuous headers sequence"); - } - if (!AcceptBlockHeader(header, state, &pindexLast)) { - int nDoS; - if (state.IsInvalid(nDoS)) { - if (nDoS > 0) - Misbehaving(pfrom->GetId(), nDoS); - return error("invalid header received"); - } - } - } - - if (pindexLast) - UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); - - if (nCount == MAX_HEADERS_RESULTS && pindexLast) { - // 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. - LogPrint("net", "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->id, pfrom->nStartingHeight); - pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256()); - } - - CheckBlockIndex(); - } - - else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing - { - CBlock block; - vRecv >> block; - - CInv inv(MSG_BLOCK, block.GetHash()); - LogPrint("net", "received block %s peer=%d\n", inv.hash.ToString(), pfrom->id); - - pfrom->AddInventoryKnown(inv); - - CValidationState state; - // Process all blocks from whitelisted peers, even if not requested. - ProcessNewBlock(state, pfrom, &block, pfrom->fWhitelisted, NULL); - int nDoS; - if (state.IsInvalid(nDoS)) { - pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), - state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); - if (nDoS > 0) { - LOCK(cs_main); - Misbehaving(pfrom->GetId(), nDoS); - } - } - - } - - - // 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. - else if ((strCommand == "getaddr") && (pfrom->fInbound)) - { - pfrom->vAddrToSend.clear(); - vector<CAddress> vAddr = addrman.GetAddr(); - BOOST_FOREACH(const CAddress &addr, vAddr) - pfrom->PushAddress(addr); - } - - - else if (strCommand == "mempool") - { - LOCK2(cs_main, pfrom->cs_filter); - - std::vector<uint256> vtxid; - mempool.queryHashes(vtxid); - vector<CInv> vInv; - BOOST_FOREACH(uint256& hash, vtxid) { - CInv inv(MSG_TX, hash); - CTransaction tx; - bool fInMemPool = mempool.lookup(hash, tx); - if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... - if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(tx)) || - (!pfrom->pfilter)) - vInv.push_back(inv); - if (vInv.size() == MAX_INV_SZ) { - pfrom->PushMessage("inv", vInv); - vInv.clear(); - } - } - if (vInv.size() > 0) - pfrom->PushMessage("inv", vInv); - } - - - else if (strCommand == "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("pong", nonce); - } - } - - - else if (strCommand == "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; - } 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: %s, %x expected, %x received, %u bytes\n", - pfrom->id, - pfrom->cleanSubVer, - sProblem, - pfrom->nPingNonceSent, - nonce, - nAvail); - } - if (bPingFinished) { - pfrom->nPingNonceSent = 0; - } - } - - - else if (fAlerts && strCommand == "alert") - { - CAlert alert; - vRecv >> alert; - - uint256 alertHash = alert.GetHash(); - if (pfrom->setKnown.count(alertHash) == 0) - { - if (alert.ProcessAlert(Params().AlertKey())) - { - // Relay - pfrom->setKnown.insert(alertHash); - { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - alert.RelayTo(pnode); - } - } - else { - // Small DoS penalty so peers that send us lots of - // duplicate/expired/invalid-signature/whatever alerts - // eventually get banned. - // This isn't a Misbehaving(100) (immediate ban) because the - // peer might be an older or different implementation with - // a different signature key, etc. - Misbehaving(pfrom->GetId(), 10); - } - } - } - - else if (!fBloomFilters && - (strCommand == "filterload" || - strCommand == "filteradd" || - strCommand == "filterclear")) - { - pfrom->CloseSocketDisconnect(); - return error("peer %s attempted to set a bloom filter even though we do not advertise that service", - pfrom->addr.ToString().c_str()); - } - - else if (strCommand == "filterload") - { - CBloomFilter filter; - vRecv >> filter; - - if (!filter.IsWithinSizeConstraints()) - // There is no excuse for sending a too-large filter - Misbehaving(pfrom->GetId(), 100); - else - { - LOCK(pfrom->cs_filter); - delete pfrom->pfilter; - pfrom->pfilter = new CBloomFilter(filter); - pfrom->pfilter->UpdateEmptyFull(); - } - pfrom->fRelayTxes = true; - } - - - else if (strCommand == "filteradd") - { - vector<unsigned char> 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) - { - Misbehaving(pfrom->GetId(), 100); - } else { - LOCK(pfrom->cs_filter); - if (pfrom->pfilter) - pfrom->pfilter->insert(vData); - else - Misbehaving(pfrom->GetId(), 100); - } - } - - - else if (strCommand == "filterclear") - { - LOCK(pfrom->cs_filter); - delete pfrom->pfilter; - pfrom->pfilter = new CBloomFilter(); - pfrom->fRelayTxes = true; - } - - - else if (strCommand == "reject") - { - if (fDebug) { - try { - string strMsg; unsigned char ccode; string strReason; - vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, MAX_REJECT_MESSAGE_LENGTH); - - ostringstream ss; - ss << strMsg << " code " << itostr(ccode) << ": " << strReason; - - if (strMsg == "block" || strMsg == "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"); - } - } - } - - else - { - // Ignore unknown commands for extensibility - LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id); - } - - - - return true; -} - -// requires LOCK(cs_vRecvMsg) -bool ProcessMessages(CNode* pfrom) -{ - //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); - - // this maintains the order of responses - if (!pfrom->vRecvGetData.empty()) return fOk; - - std::deque<CNetMessage>::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 (memcmp(msg.hdr.pchMessageStart, Params().MessageStart(), MESSAGE_START_SIZE) != 0) { - LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->id); - fOk = false; - break; - } - - // Read header - CMessageHeader& hdr = msg.hdr; - if (!hdr.IsValid(Params().MessageStart())) - { - LogPrintf("PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(hdr.GetCommand()), pfrom->id); - continue; - } - 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("reject", strCommand, REJECT_MALFORMED, 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(NULL, "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, bool fSendTrickle) -{ - const Consensus::Params& consensusParams = Params().GetConsensus(chainActive.Height()); - { - // 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("ping", nonce); - } else { - // Peer is too old to support ping command with nonce, pong will never arrive. - pto->nPingNonceSent = 0; - pto->PushMessage("ping"); - } - } - - TRY_LOCK(cs_main, lockMain); // Acquire cs_main for IsInitialBlockDownload() and CNodeState() - if (!lockMain) - return true; - - // Address refresh broadcast - static int64_t nLastRebroadcast; - if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60)) - { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - { - // Periodically clear addrKnown to allow refresh broadcasts - if (nLastRebroadcast) - pnode->addrKnown.clear(); - - // Rebroadcast our address - AdvertizeLocal(pnode); - } - if (!vNodes.empty()) - nLastRebroadcast = GetTime(); - } - - // - // Message: addr - // - if (fSendTrickle) - { - vector<CAddress> vAddr; - vAddr.reserve(pto->vAddrToSend.size()); - BOOST_FOREACH(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("addr", vAddr); - vAddr.clear(); - } - } - } - pto->vAddrToSend.clear(); - if (!vAddr.empty()) - pto->PushMessage("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); - } - } - state.fShouldBan = false; - } - - BOOST_FOREACH(const CBlockReject& reject, state.rejects) - pto->PushMessage("reject", (string)"block", reject.chRejectCode, reject.strRejectReason, reject.hashBlock); - state.rejects.clear(); - - // Start block sync - if (pindexBestHeader == NULL) - pindexBestHeader = chainActive.Tip(); - 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 && !fImporting && !fReindex) { - // Only actively request headers from a single peer, unless we're close to today. - if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) { - state.fSyncStarted = true; - nSyncStarted++; - CBlockIndex *pindexStart = pindexBestHeader->pprev ? pindexBestHeader->pprev : pindexBestHeader; - LogPrint("net", "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->id, pto->nStartingHeight); - pto->PushMessage("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 && !fImporting && !IsInitialBlockDownload()) - { - GetMainSignals().Broadcast(nTimeBestReceived); - } - - // - // Message: inventory - // - vector<CInv> vInv; - vector<CInv> vInvWait; - { - LOCK(pto->cs_inventory); - vInv.reserve(pto->vInventoryToSend.size()); - vInvWait.reserve(pto->vInventoryToSend.size()); - BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend) - { - if (pto->setInventoryKnown.count(inv)) - 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; - } - } - - // returns true if wasn't already contained in the set - if (pto->setInventoryKnown.insert(inv).second) - { - vInv.push_back(inv); - if (vInv.size() >= 1000) - { - pto->PushMessage("inv", vInv); - vInv.clear(); - } - } - } - pto->vInventoryToSend = vInvWait; - } - if (!vInv.empty()) - pto->PushMessage("inv", vInv); - - // Detect whether we're stalling - int64_t 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. - LogPrintf("Peer=%d is stalling block download, disconnecting\n", pto->id); - 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 validated blocks that were in flight at the time it was requested), disconnect due to - // timeout. We compensate for in-flight blocks 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. - // We also compare the block download timeout originally calculated against the time at which we'd disconnect - // if we assumed the block were being requested now (ignoring blocks we've requested from this peer, since we're - // only looking at this peer's oldest request). This way a large queue in the past doesn't result in a - // permanently large window for this block to be delivered (ie if the number of blocks in flight is decreasing - // more quickly than once every 5 minutes, then we'll shorten the download window for this block). - if (!pto->fDisconnect && state.vBlocksInFlight.size() > 0) { - QueuedBlock &queuedBlock = state.vBlocksInFlight.front(); - int64_t nTimeoutIfRequestedNow = GetBlockTimeout(nNow, nQueuedValidatedHeaders - state.nBlocksInFlightValidHeaders, consensusParams); - if (queuedBlock.nTimeDisconnect > nTimeoutIfRequestedNow) { - LogPrint("net", "Reducing block download timeout for peer=%d block=%s, orig=%d new=%d\n", pto->id, queuedBlock.hash.ToString(), queuedBlock.nTimeDisconnect, nTimeoutIfRequestedNow); - queuedBlock.nTimeDisconnect = nTimeoutIfRequestedNow; - } - if (queuedBlock.nTimeDisconnect < nNow) { - LogPrintf("Timeout downloading block %s from peer=%d, disconnecting\n", queuedBlock.hash.ToString(), pto->id); - pto->fDisconnect = true; - } - } - - // - // Message: getdata (blocks) - // - vector<CInv> vGetData; - if (!pto->fDisconnect && !pto->fClient && (fFetch || !IsInitialBlockDownload()) && state.nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { - vector<CBlockIndex*> vToDownload; - NodeId staller = -1; - FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller); - BOOST_FOREACH(CBlockIndex *pindex, vToDownload) { - vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); - MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex); - LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), - pindex->nHeight, pto->id); - } - if (state.nBlocksInFlight == 0 && staller != -1) { - if (State(staller)->nStallingSince == 0) { - State(staller)->nStallingSince = nNow; - LogPrint("net", "Stall started peer=%d\n", 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)) - { - if (fDebug) - LogPrint("net", "Requesting %s peer=%d\n", inv.ToString(), pto->id); - vGetData.push_back(inv); - if (vGetData.size() >= 1000) - { - pto->PushMessage("getdata", vGetData); - vGetData.clear(); - } - } - pto->mapAskFor.erase(pto->mapAskFor.begin()); - } - if (!vGetData.empty()) - pto->PushMessage("getdata", vGetData); - - } - return true; -} - - std::string CBlockFileInfo::ToString() const { - return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast)); - } - - - -class CMainCleanup -{ -public: - CMainCleanup() {} - ~CMainCleanup() { - // block headers - BlockMap::iterator it1 = mapBlockIndex.begin(); - for (; it1 != mapBlockIndex.end(); it1++) - delete (*it1).second; - mapBlockIndex.clear(); - - // orphan transactions - mapOrphanTransactions.clear(); - mapOrphanTransactionsByPrev.clear(); - } -} instance_of_cmaincleanup; |