diff options
author | Pieter Wuille <pieter.wuille@gmail.com> | 2013-11-16 19:28:24 +0100 |
---|---|---|
committer | Pieter Wuille <pieter.wuille@gmail.com> | 2014-01-27 21:20:41 +0100 |
commit | 75f51f2a63e0ebe34ab290c2b7141dd240b98c3b (patch) | |
tree | ed9be9e3acbcd9d9df9f67264042109b8cd7db98 /src | |
parent | 0ec16f35d6b8d9f1fe876f1405f105f6e5aa0933 (diff) |
Prepare block connection logic for headers-first.
This changes the block processing logic from "try to atomically switch
to a new block" to a continuous "(dis)connect a block, aiming for the
assumed best chain".
This means the smallest atomic operations on the chainstate become
individual block connections or disconnections, instead of entire
reorganizations. It may mean that we try to reorganize to one block,
fail, and rereorganize again to the old block. This is slower, but
doesn't require unbounded RAM.
It also means that a ConnectBlock which fails may be no longer called
from the ProcessBlock which knows which node sent it. To deal with that,
a mapBlockSource is kept, and invalid blocks cause asynchronous "reject"
messages and banning (if necessary).
Diffstat (limited to 'src')
-rw-r--r-- | src/init.cpp | 2 | ||||
-rw-r--r-- | src/main.cpp | 438 | ||||
-rw-r--r-- | src/main.h | 20 |
3 files changed, 256 insertions, 204 deletions
diff --git a/src/init.cpp b/src/init.cpp index 7213477b22..8520d63c89 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<boost::filesystem::path> vImportFiles; diff --git a/src/main.cpp b/src/main.cpp index 6e1919c9a6..660380b991 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,6 +41,7 @@ CTxMemPool mempool; map<uint256, CBlockIndex*> 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<CBlockIndex*, CBlockIndexWorkComparator> 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<uint256, NodeId> 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<CBlockReject> rejects; CNodeState() { nMisbehavior = 0; @@ -171,6 +202,7 @@ struct CNodeState { } }; +// Map maintaining per-node state. Requires cs_main. map<NodeId, CNodeState> 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<CBlockIndex*,CBlockIndexWorkComparator>::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<uint256, NodeId>::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<CBlockIndex*> 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,129 +1806,179 @@ 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()) +// 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 state.Abort(_("Failed to read block")); + // Apply the block atomically to the chain state. + int64_t nStart = GetTimeMicros(); { - boost::replace_all(strCmd, "%s", chainActive.Tip()->GetBlockHash().GetHex()); - boost::thread t(runCommand, strCmd); // thread runs free + 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("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); + // Write the chain state to disk, if necessary. + if (!WriteChainState(state)) + return false; + // Ressurect mempool transactions from the disconnected block. + BOOST_FOREACH(const CTransaction &tx, block.vtx) { + // ignore validation errors in resurrected transactions + 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; } -bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) -{ +// Connect a new block to chainActive. +bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew) { + assert(pindexNew->pprev == chainActive.Tip()); 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<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(view.GetBestBlock()); - CBlockIndex* ptip = (it != mapBlockIndex.end()) ? it->second : NULL; - CBlockIndex* pfork = ptip; - CBlockIndex* plonger = pindexNew; - while (pfork && pfork != plonger) + // 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(); { - while (plonger->nHeight > pfork->nHeight) { - plonger = plonger->pprev; - assert(plonger != NULL); + 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()); } - if (pfork == plonger) - break; - pfork = pfork->pprev; - assert(pfork != NULL); + 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; +} - // List of what to disconnect (typically nothing) - vector<CBlockIndex*> vDisconnect; - for (CBlockIndex* pindex = ptip; pindex != pfork; pindex = pindex->pprev) - vDisconnect.push_back(pindex); - - // List of what to connect (typically only pindexNew) - vector<CBlockIndex*> vConnect; - for (CBlockIndex* pindex = pindexNew; pindex != pfork; pindex = pindex->pprev) - vConnect.push_back(pindex); - reverse(vConnect.begin(), vConnect.end()); +// 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; - 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()); + // 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); } - // Disconnect shorter branch - list<CTransaction> 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); - } + do { + // Find the best candidate header. + { + std::set<CBlockIndex*, CBlockIndexWorkComparator>::reverse_iterator it = setBlockIndexValid.rbegin(); + if (it == setBlockIndexValid.rend()) + return; + pindexNew = *it; + } - // Connect longer branch - vector<CTransaction> 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); + // 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; } - return error("SetBestBlock() : ConnectBlock %s failed", pindex->GetBlockHash().ToString()); + pindexTest = pindexTest->pprev; } - if (fBenchmark) - LogPrintf("- Connect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); + if (fInvalidAncestor) + continue; - // Queue memory transactions to delete - BOOST_FOREACH(const CTransaction& tx, block.vtx) - vDelete.push_back(tx); - } + break; + } while(true); - // Flush changes to global coin state - int64_t nStart = GetTimeMicros(); - int nModified = view.GetCacheSize(); - bool ret; - ret = view.Flush(); - assert(ret); - int64_t nTime = GetTimeMicros() - nStart; - if (fBenchmark) - LogPrintf("- Flush %i transactions: %.2fms (%.4fms/tx)\n", nModified, 0.001 * nTime, 0.001 * nTime / nModified); + // Check whether it's actually an improvement. + if (chainMostWork.Tip() && !CBlockIndexWorkComparator()(chainMostWork.Tip(), pindexNew)) + return; - if (!WriteChainState(state)) - return false; + // We have a new best. + chainMostWork.SetTip(pindexNew); +} - // Resurrect memory transactions that were in the disconnected branch - BOOST_FOREACH(CTransaction& tx, vResurrect) { - // ignore validation errors in resurrected transactions - CValidationState stateDummy; - if (!AcceptToMemoryPool(mempool,stateDummy, tx, false, NULL)) - mempool.remove(tx, true); - } +// 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; - // Delete redundant memory transactions that are in the connected branch - BOOST_FOREACH(CTransaction& tx, vDelete) { - mempool.remove(tx); - mempool.removeConflicts(tx); + // 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; + } + } + } } - mempool.check(pcoinsTip); + 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 + } + } - UpdateTip(pindexNew); return true; } @@ -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<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); map<uint256, CBlockIndex*>::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<CInv>::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 60e733b23a..ba353a885b 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; |