aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGavin Andresen <gavinandresen@gmail.com>2014-01-29 13:49:15 -0800
committerGavin Andresen <gavinandresen@gmail.com>2014-01-29 13:49:15 -0800
commit3581abdd460b3ffc2ceb645bfd53797eedd629d5 (patch)
tree42443ed33e586be5fc096c50a9a71773fe1044a7 /src
parenta7f3aedec3ce837c6932b1d2cf8e05ea3de93847 (diff)
parent75f51f2a63e0ebe34ab290c2b7141dd240b98c3b (diff)
Merge pull request #3370 from sipa/headersfirst3
Prepare block connection logic for headers-first
Diffstat (limited to 'src')
-rw-r--r--src/init.cpp2
-rw-r--r--src/main.cpp491
-rw-r--r--src/main.h20
3 files changed, 284 insertions, 229 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 610efaf07d..3c174a4650 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,102 +1752,10 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
return true;
}
-bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
-{
- 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)
- {
- 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<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());
-
- 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<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);
- }
-
- // 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);
- }
- 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
- 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);
-
- // Make sure it's successfully written to disk before changing memory structure
- bool fIsInitialDownload = IsInitialBlockDownload();
- if (!fIsInitialDownload || pcoinsTip->GetCacheSize() > nCoinCacheSize) {
+// Update the on-disk chain state.
+bool static WriteChainState(CValidationState &state) {
+ 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
@@ -1853,39 +1767,25 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
pblocktree->Sync();
if (!pcoinsTip->Flush())
return state.Abort(_("Failed to write to coin database"));
+ nLastWrite = GetTimeMicros();
}
+ return true;
+}
- // At this point, all changes have been done to the database.
- // Proceed by updating the memory structures.
-
- // Register new best chain
+// Update chainActive and related internal data structures.
+void static UpdateTip(CBlockIndex *pindexNew) {
chainActive.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);
- }
-
- // Delete redundant memory transactions that are in the connected branch
- BOOST_FOREACH(CTransaction& tx, vDelete) {
- mempool.remove(tx);
- mempool.removeConflicts(tx);
- }
-
- mempool.check(pcoinsTip);
-
// Update best block in wallet (so we can detect restored wallets)
- if ((pindexNew->nHeight % 20160) == 0 || (!fIsInitialDownload && (pindexNew->nHeight % 144) == 0))
- g_signals.SetBestChain(chainActive.GetLocator(pindexNew));
+ bool fIsInitialDownload = IsInitialBlockDownload();
+ if ((chainActive.Height() % 20160) == 0 || (!fIsInitialDownload && (chainActive.Height() % 144) == 0))
+ g_signals.SetBestChain(chainActive.GetLocator());
// New best block
nTimeBestReceived = GetTime();
mempool.AddTransactionsUpdated(1);
- LogPrintf("SetBestChain: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f\n",
- chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx,
+ LogPrintf("UpdateTip: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f\n",
+ 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(chainActive.Tip()));
@@ -1906,18 +1806,181 @@ bool SetBestChain(CValidationState &state, 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;
+}
+// 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<CBlockIndex*, CBlockIndexWorkComparator>::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)
{
@@ -1928,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);
@@ -1950,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())
@@ -2274,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)))
@@ -3004,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();
@@ -3584,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);
}
@@ -4042,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;