diff --git a/src/init.cpp b/src/init.cpp index 7213477b2..8520d63c8 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -983,7 +983,7 @@ bool AppInit2(boost::thread_group& threadGroup) // scan for better chains in the block chain database, that are not yet connected in the active best chain CValidationState state; - if (!ConnectBestBlock(state)) + if (!ActivateBestChain(state)) strErrors << "Failed to connect best block"; std::vector vImportFiles; diff --git a/src/main.cpp b/src/main.cpp index 6e1919c9a..660380b99 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,6 +41,7 @@ CTxMemPool mempool; map mapBlockIndex; CChain chainActive; +CChain chainMostWork; int64_t nTimeBestReceived = 0; int nScriptCheckThreads = 0; bool fImporting = false; @@ -77,13 +78,21 @@ namespace { struct CBlockIndexWorkComparator { bool operator()(CBlockIndex *pa, CBlockIndex *pb) { + // First sort by most total work, ... if (pa->nChainWork > pb->nChainWork) return false; if (pa->nChainWork < pb->nChainWork) return true; - if (pa->GetBlockHash() < pb->GetBlockHash()) return false; - if (pa->GetBlockHash() > pb->GetBlockHash()) return true; + // ... then by earliest time received, ... + if (pa->nSequenceId < pb->nSequenceId) return false; + if (pa->nSequenceId > pb->nSequenceId) return true; - return false; // identical blocks + // 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; } }; @@ -93,6 +102,16 @@ set setBlockIndexValid; // may contain CCriticalSection cs_LastBlockFile; CBlockFileInfo infoLastBlockFile; int nLastBlockFile = 0; + +// Every received block is assigned a unique and increasing identifier, so we +// know which one to give priority in case of a fork. +CCriticalSection cs_nBlockSequenceId; +// Blocks loaded from disk are assigned id 0, so start the counter at 1. +uint32_t nBlockSequenceId = 1; + +// Sources of received blocks, to be able to send them reject messages or ban +// them, if processing happens afterwards. Protected by cs_main. +map mapBlockSource; } ////////////////////////////////////////////////////////////////////////////// @@ -156,14 +175,26 @@ void SyncWithWallets(const uint256 &hash, const CTransaction &tx, const CBlock * // 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 { + // Accumulated misbehaviour score for this peer. int nMisbehavior; + // Whether this peer should be disconnected and banned. 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 rejects; CNodeState() { nMisbehavior = 0; @@ -171,6 +202,7 @@ struct CNodeState { } }; +// Map maintaining per-node state. Requires cs_main. map mapNodeState; // Requires cs_main. @@ -1242,6 +1274,24 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) CheckForkWarningConditions(); } +void Misbehaving(NodeId pnode, int howmuch) +{ + if (howmuch == 0) + return; + + CNodeState *state = State(pnode); + if (state == NULL) + return; + + state->nMisbehavior += howmuch; + if (state->nMisbehavior >= GetArg("-banscore", 100)) + { + LogPrintf("Misbehaving: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior); + state->fShouldBan = true; + } else + LogPrintf("Misbehaving: %s (%d -> %d)\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior); +} + void static InvalidChainFound(CBlockIndex* pindexNew) { if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork) @@ -1263,67 +1313,23 @@ void static InvalidChainFound(CBlockIndex* pindexNew) CheckForkWarningConditions(); } -void static InvalidBlockFound(CBlockIndex *pindex) { - pindex->nStatus |= BLOCK_FAILED_VALID; - pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)); - setBlockIndexValid.erase(pindex); - InvalidChainFound(pindex); - if (chainActive.Next(pindex)) { - CValidationState stateDummy; - ConnectBestBlock(stateDummy); // reorganise away from the failed block - } -} - -bool ConnectBestBlock(CValidationState &state) { - do { - CBlockIndex *pindexNewBest; - - { - std::set::reverse_iterator it = setBlockIndexValid.rbegin(); - if (it == setBlockIndexValid.rend()) - return true; - pindexNewBest = *it; +void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) { + int nDoS = 0; + if (state.IsInvalid(nDoS)) { + std::map::iterator it = mapBlockSource.find(pindex->GetBlockHash()); + if (it != mapBlockSource.end() && State(it->second)) { + CBlockReject reject = {state.GetRejectCode(), state.GetRejectReason(), pindex->GetBlockHash()}; + State(it->second)->rejects.push_back(reject); + if (nDoS > 0) + Misbehaving(it->second, nDoS); } - - if (pindexNewBest == chainActive.Tip() || (chainActive.Tip() && pindexNewBest->nChainWork == chainActive.Tip()->nChainWork)) - return true; // nothing to do - - // check ancestry - CBlockIndex *pindexTest = pindexNewBest; - std::vector vAttach; - do { - if (pindexTest->nStatus & BLOCK_FAILED_MASK) { - // mark descendants failed - CBlockIndex *pindexFailed = pindexNewBest; - while (pindexTest != pindexFailed) { - pindexFailed->nStatus |= BLOCK_FAILED_CHILD; - setBlockIndexValid.erase(pindexFailed); - pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexFailed)); - pindexFailed = pindexFailed->pprev; - } - InvalidChainFound(pindexNewBest); - break; - } - - if (chainActive.Tip() == NULL || pindexTest->nChainWork > chainActive.Tip()->nChainWork) - vAttach.push_back(pindexTest); - - if (pindexTest->pprev == NULL || chainActive.Next(pindexTest)) { - reverse(vAttach.begin(), vAttach.end()); - BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) { - boost::this_thread::interruption_point(); - try { - if (!SetBestChain(state, pindexSwitch)) - return false; - } catch(std::runtime_error &e) { - return state.Abort(_("System error: ") + e.what()); - } - } - return true; - } - pindexTest = pindexTest->pprev; - } while(true); - } while(true); + } + if (!state.CorruptionPossible()) { + pindex->nStatus |= BLOCK_FAILED_VALID; + pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)); + setBlockIndexValid.erase(pindex); + InvalidChainFound(pindex); + } } void UpdateTime(CBlockHeader& block, const CBlockIndex* pindexPrev) @@ -1746,8 +1752,10 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C return true; } +// Update the on-disk chain state. bool static WriteChainState(CValidationState &state) { - if (!IsInitialBlockDownload() || pcoinsTip->GetCacheSize() > nCoinCacheSize) { + static int64_t nLastWrite = 0; + if (!IsInitialBlockDownload() || pcoinsTip->GetCacheSize() > nCoinCacheSize || GetTimeMicros() > nLastWrite + 600*1000000) { // Typical CCoins structures on disk are around 100 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 @@ -1759,10 +1767,12 @@ bool static WriteChainState(CValidationState &state) { pblocktree->Sync(); if (!pcoinsTip->Flush()) return state.Abort(_("Failed to write to coin database")); + nLastWrite = GetTimeMicros(); } return true; } +// Update chainActive and related internal data structures. void static UpdateTip(CBlockIndex *pindexNew) { chainActive.SetTip(pindexNew); @@ -1796,132 +1806,182 @@ void static UpdateTip(CBlockIndex *pindexNew) { // 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!"); } - - std::string strCmd = GetArg("-blocknotify", ""); - - if (!fIsInitialDownload && !strCmd.empty()) - { - boost::replace_all(strCmd, "%s", chainActive.Tip()->GetBlockHash().GetHex()); - boost::thread t(runCommand, strCmd); // thread runs free - } } -bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) -{ +// Disconnect chainActive's tip. +bool static DisconnectTip(CValidationState &state) { + CBlockIndex *pindexDelete = chainActive.Tip(); + assert(pindexDelete); mempool.check(pcoinsTip); - - // All modifications to the coin state will be done in this cache. - // Only when all have succeeded, we push it to pcoinsTip. - CCoinsViewCache view(*pcoinsTip, true); - - // Find the fork (typically, there is none) - std::map::iterator it = mapBlockIndex.find(view.GetBestBlock()); - CBlockIndex* ptip = (it != mapBlockIndex.end()) ? it->second : NULL; - CBlockIndex* pfork = ptip; - CBlockIndex* plonger = pindexNew; - while (pfork && pfork != plonger) - { - while (plonger->nHeight > pfork->nHeight) { - plonger = plonger->pprev; - assert(plonger != NULL); - } - if (pfork == plonger) - break; - pfork = pfork->pprev; - assert(pfork != NULL); - } - - // List of what to disconnect (typically nothing) - vector vDisconnect; - for (CBlockIndex* pindex = ptip; pindex != pfork; pindex = pindex->pprev) - vDisconnect.push_back(pindex); - - // List of what to connect (typically only pindexNew) - vector vConnect; - for (CBlockIndex* pindex = pindexNew; pindex != pfork; pindex = pindex->pprev) - vConnect.push_back(pindex); - reverse(vConnect.begin(), vConnect.end()); - - if (vDisconnect.size() > 0) { - LogPrintf("REORGANIZE: Disconnect %"PRIszu" blocks; %s...\n", vDisconnect.size(), pfork->GetBlockHash().ToString()); - LogPrintf("REORGANIZE: Connect %"PRIszu" blocks; ...%s\n", vConnect.size(), pindexNew->GetBlockHash().ToString()); - } - - // Disconnect shorter branch - list vResurrect; - BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) { - CBlock block; - if (!ReadBlockFromDisk(block, pindex)) - return state.Abort(_("Failed to read block")); - int64_t nStart = GetTimeMicros(); - if (!DisconnectBlock(block, state, pindex, view)) - return error("SetBestBlock() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString()); - if (fBenchmark) - LogPrintf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); - - // Queue memory transactions to resurrect. - // We only do this for blocks after the last checkpoint (reorganisation before that - // point should only happen with -reindex/-loadblock, or a misbehaving peer. - BOOST_REVERSE_FOREACH(const CTransaction& tx, block.vtx) - if (!tx.IsCoinBase() && pindex->nHeight > Checkpoints::GetTotalBlocksEstimate()) - vResurrect.push_front(tx); - } - - // Connect longer branch - vector vDelete; - BOOST_FOREACH(CBlockIndex *pindex, vConnect) { - CBlock block; - if (!ReadBlockFromDisk(block, pindex)) - return state.Abort(_("Failed to read block")); - int64_t nStart = GetTimeMicros(); - if (!ConnectBlock(block, state, pindex, view)) { - if (state.IsInvalid()) { - InvalidChainFound(pindexNew); - InvalidBlockFound(pindex); - } - return error("SetBestBlock() : ConnectBlock %s failed", pindex->GetBlockHash().ToString()); - } - if (fBenchmark) - LogPrintf("- Connect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); - - // Queue memory transactions to delete - BOOST_FOREACH(const CTransaction& tx, block.vtx) - vDelete.push_back(tx); - } - - // Flush changes to global coin state + // Read block from disk. + CBlock block; + if (!ReadBlockFromDisk(block, pindexDelete)) + return state.Abort(_("Failed to read block")); + // Apply the block atomically to the chain state. int64_t nStart = GetTimeMicros(); - int nModified = view.GetCacheSize(); - bool ret; - ret = view.Flush(); - assert(ret); - int64_t nTime = GetTimeMicros() - nStart; + { + CCoinsViewCache view(*pcoinsTip, true); + if (!DisconnectBlock(block, state, pindexDelete, view)) + return error("DisconnectTip() : DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString()); + assert(view.Flush()); + } if (fBenchmark) - LogPrintf("- Flush %i transactions: %.2fms (%.4fms/tx)\n", nModified, 0.001 * nTime, 0.001 * nTime / nModified); - + LogPrintf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); + // Write the chain state to disk, if necessary. if (!WriteChainState(state)) return false; - - // Resurrect memory transactions that were in the disconnected branch - BOOST_FOREACH(CTransaction& tx, vResurrect) { + // Ressurect mempool transactions from the disconnected block. + BOOST_FOREACH(const CTransaction &tx, block.vtx) { // ignore validation errors in resurrected transactions - CValidationState stateDummy; - if (!AcceptToMemoryPool(mempool,stateDummy, tx, false, NULL)) - mempool.remove(tx, true); + CValidationState stateDummy; + if (!tx.IsCoinBase()) + if (!AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) + mempool.remove(tx, true); } + mempool.check(pcoinsTip); + // Update chainActive and related variables. + UpdateTip(pindexDelete->pprev); + return true; +} - // Delete redundant memory transactions that are in the connected branch - BOOST_FOREACH(CTransaction& tx, vDelete) { +// Connect a new block to chainActive. +bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew) { + assert(pindexNew->pprev == chainActive.Tip()); + mempool.check(pcoinsTip); + // Read block from disk. + CBlock block; + if (!ReadBlockFromDisk(block, pindexNew)) + return state.Abort(_("Failed to read block")); + // Apply the block atomically to the chain state. + int64_t nStart = GetTimeMicros(); + { + CCoinsViewCache view(*pcoinsTip, true); + CInv inv(MSG_BLOCK, pindexNew->GetBlockHash()); + if (!ConnectBlock(block, state, pindexNew, view)) { + if (state.IsInvalid()) + InvalidBlockFound(pindexNew, state); + return error("ConnectTip() : ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); + } + mapBlockSource.erase(inv.hash); + assert(view.Flush()); + } + if (fBenchmark) + LogPrintf("- Connect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); + // Write the chain state to disk, if necessary. + if (!WriteChainState(state)) + return false; + // Remove conflicting transactions from the mempool. + BOOST_FOREACH(const CTransaction &tx, block.vtx) { mempool.remove(tx); mempool.removeConflicts(tx); } - mempool.check(pcoinsTip); - + // Update chainActive & related variables. UpdateTip(pindexNew); return true; } +// Make chainMostWork correspond to the chain with the most work in it, that isn't +// known to be invalid (it's however far from certain to be valid). +void static FindMostWorkChain() { + CBlockIndex *pindexNew = NULL; + + // In case the current best is invalid, do not consider it. + while (chainMostWork.Tip() && (chainMostWork.Tip()->nStatus & BLOCK_FAILED_MASK)) { + setBlockIndexValid.erase(chainMostWork.Tip()); + chainMostWork.SetTip(chainMostWork.Tip()->pprev); + } + + do { + // Find the best candidate header. + { + std::set::reverse_iterator it = setBlockIndexValid.rbegin(); + if (it == setBlockIndexValid.rend()) + return; + 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)) { + if (pindexTest->nStatus & BLOCK_FAILED_MASK) { + // Candidate has an invalid ancestor, remove entire chain from the set. + if (pindexBestInvalid == NULL || pindexNew->nChainWork > pindexBestInvalid->nChainWork) + pindexBestInvalid = pindexNew; CBlockIndex *pindexFailed = pindexNew; + while (pindexTest != pindexFailed) { + pindexFailed->nStatus |= BLOCK_FAILED_CHILD; + setBlockIndexValid.erase(pindexFailed); + pindexFailed = pindexFailed->pprev; + } + fInvalidAncestor = true; + break; + } + pindexTest = pindexTest->pprev; + } + if (fInvalidAncestor) + continue; + + break; + } while(true); + + // Check whether it's actually an improvement. + if (chainMostWork.Tip() && !CBlockIndexWorkComparator()(chainMostWork.Tip(), pindexNew)) + return; + + // We have a new best. + chainMostWork.SetTip(pindexNew); +} + +// Try to activate to the most-work chain (thereby connecting it). +bool ActivateBestChain(CValidationState &state) { + CBlockIndex *pindexOldTip = chainActive.Tip(); + bool fComplete = false; + while (!fComplete) { + FindMostWorkChain(); + fComplete = true; + + // Check whether we have something to do. + if (chainMostWork.Tip() == NULL) break; + + // Disconnect active blocks which are no longer in the best chain. + while (chainActive.Tip() && !chainMostWork.Contains(chainActive.Tip())) { + if (!DisconnectTip(state)) + return false; + } + + // Connect new blocks. + while (!chainActive.Contains(chainMostWork.Tip())) { + CBlockIndex *pindexConnect = chainMostWork[chainActive.Height() + 1]; + if (!ConnectTip(state, pindexConnect)) { + if (state.IsInvalid()) { + // The block violates a consensus rule. + if (!state.CorruptionPossible()) + InvalidChainFound(chainMostWork.Tip()); + fComplete = false; + state = CValidationState(); + break; + } else { + // A system error occurred (disk space, database error, ...). + return false; + } + } + } + } + + if (chainActive.Tip() != pindexOldTip) { + std::string strCmd = GetArg("-blocknotify", ""); + if (!IsInitialBlockDownload() && !strCmd.empty()) + { + boost::replace_all(strCmd, "%s", chainActive.Tip()->GetBlockHash().GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } + } + + return true; +} + bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos& pos) { // Check for duplicate @@ -1931,7 +1991,12 @@ bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos // Construct new block index object CBlockIndex* pindexNew = new CBlockIndex(block); + { + LOCK(cs_nBlockSequenceId); + pindexNew->nSequenceId = nBlockSequenceId++; + } assert(pindexNew); + mapAlreadyAskedFor.erase(CInv(MSG_BLOCK, hash)); map::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); map::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock); @@ -1953,7 +2018,7 @@ bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos return state.Abort(_("Failed to write block index")); // New best? - if (!ConnectBestBlock(state)) + if (!ActivateBestChain(state)) return false; if (pindexNew == chainActive.Tip()) @@ -2277,8 +2342,11 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", hash.ToString())); // Preliminary checks - if (!CheckBlock(*pblock, state)) + if (!CheckBlock(*pblock, state)) { + if (state.CorruptionPossible()) + mapAlreadyAskedFor.erase(CInv(MSG_BLOCK, hash)); return error("ProcessBlock() : CheckBlock FAILED"); + } CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); if (pcheckpoint && pblock->hashPrevBlock != (chainActive.Tip() ? chainActive.Tip()->GetBlockHash() : uint256(0))) @@ -3007,24 +3075,6 @@ bool static AlreadyHave(const CInv& inv) } -void Misbehaving(NodeId pnode, int howmuch) -{ - if (howmuch == 0) - return; - - CNodeState *state = State(pnode); - if (state == NULL) - return; - - state->nMisbehavior += howmuch; - if (state->nMisbehavior >= GetArg("-banscore", 100)) - { - LogPrintf("Misbehaving: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior); - state->fShouldBan = true; - } else - LogPrintf("Misbehaving: %s (%d -> %d)\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior); -} - void static ProcessGetData(CNode* pfrom) { std::deque::iterator it = pfrom->vRecvGetData.begin(); @@ -3587,18 +3637,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->AddInventoryKnown(inv); LOCK(cs_main); + // Remember who we got this block from. + mapBlockSource[inv.hash] = pfrom->GetId(); CValidationState state; - if (ProcessBlock(state, pfrom, &block) || state.CorruptionPossible()) - mapAlreadyAskedFor.erase(inv); - int nDoS = 0; - if (state.IsInvalid(nDoS)) - { - pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), - state.GetRejectReason(), inv.hash); - if (nDoS > 0) - Misbehaving(pfrom->GetId(), nDoS); - } + ProcessBlock(state, pfrom, &block); } @@ -4045,16 +4088,21 @@ bool SendMessages(CNode* pto, bool fSendTrickle) if (!lockMain) return true; - if (State(pto->GetId())->fShouldBan) { + CNodeState &state = *State(pto->GetId()); + if (state.fShouldBan) { if (pto->addr.IsLocal()) LogPrintf("Warning: not banning local node %s!\n", pto->addr.ToString()); else { pto->fDisconnect = true; CNode::Ban(pto->addr); } - State(pto->GetId())->fShouldBan = false; + 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 (pto->fStartSync && !fImporting && !fReindex) { pto->fStartSync = false; diff --git a/src/main.h b/src/main.h index 60e733b23..ba353a885 100644 --- a/src/main.h +++ b/src/main.h @@ -165,10 +165,8 @@ bool IsInitialBlockDownload(); std::string GetWarnings(std::string strFor); /** Retrieve a transaction (from memory pool, or from disk, if possible) */ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false); -/** Connect/disconnect blocks until pindexNew is the new tip of the active block chain */ -bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew); /** Find the best known block, and make it the tip of the block chain */ -bool ConnectBestBlock(CValidationState &state); +bool ActivateBestChain(CValidationState &state); int64_t GetBlockValue(int nHeight, int64_t nFees); unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock); @@ -716,6 +714,8 @@ public: unsigned int nBits; unsigned int nNonce; + // (memory only) Sequencial id assigned to distinguish order in which blocks are received. + uint32_t nSequenceId; CBlockIndex() { @@ -729,6 +729,7 @@ public: nTx = 0; nChainTx = 0; nStatus = 0; + nSequenceId = 0; nVersion = 0; hashMerkleRoot = 0; @@ -749,6 +750,7 @@ public: nTx = 0; nChainTx = 0; nStatus = 0; + nSequenceId = 0; nVersion = block.nVersion; hashMerkleRoot = block.hashMerkleRoot; @@ -958,23 +960,23 @@ public: AbortNode(msg); return Error(); } - bool IsValid() { + bool IsValid() const { return mode == MODE_VALID; } - bool IsInvalid() { + bool IsInvalid() const { return mode == MODE_INVALID; } - bool IsError() { + bool IsError() const { return mode == MODE_ERROR; } - bool IsInvalid(int &nDoSOut) { + bool IsInvalid(int &nDoSOut) const { if (IsInvalid()) { nDoSOut = nDoS; return true; } return false; } - bool CorruptionPossible() { + bool CorruptionPossible() const { return corruptionPossible; } unsigned char GetRejectCode() const { return chRejectCode; } @@ -1041,6 +1043,8 @@ public: /** The currently-connected chain of blocks. */ extern CChain chainActive; +/** The currently best known chain of headers (some of which may be invalid). */ +extern CChain chainMostWork; /** Global variable that points to the active CCoinsView (protected by cs_main) */ extern CCoinsViewCache *pcoinsTip;