Merge pull request #3370 from sipa/headersfirst3

Prepare block connection logic for headers-first
This commit is contained in:
Gavin Andresen 2014-01-29 13:49:15 -08:00
commit 3581abdd46
3 changed files with 285 additions and 230 deletions

View file

@ -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 // scan for better chains in the block chain database, that are not yet connected in the active best chain
CValidationState state; CValidationState state;
if (!ConnectBestBlock(state)) if (!ActivateBestChain(state))
strErrors << "Failed to connect best block"; strErrors << "Failed to connect best block";
std::vector<boost::filesystem::path> vImportFiles; std::vector<boost::filesystem::path> vImportFiles;

View file

@ -41,6 +41,7 @@ CTxMemPool mempool;
map<uint256, CBlockIndex*> mapBlockIndex; map<uint256, CBlockIndex*> mapBlockIndex;
CChain chainActive; CChain chainActive;
CChain chainMostWork;
int64_t nTimeBestReceived = 0; int64_t nTimeBestReceived = 0;
int nScriptCheckThreads = 0; int nScriptCheckThreads = 0;
bool fImporting = false; bool fImporting = false;
@ -77,13 +78,21 @@ namespace {
struct CBlockIndexWorkComparator struct CBlockIndexWorkComparator
{ {
bool operator()(CBlockIndex *pa, CBlockIndex *pb) { 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 false;
if (pa->nChainWork < pb->nChainWork) return true; if (pa->nChainWork < pb->nChainWork) return true;
if (pa->GetBlockHash() < pb->GetBlockHash()) return false; // ... then by earliest time received, ...
if (pa->GetBlockHash() > pb->GetBlockHash()) return true; 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; CCriticalSection cs_LastBlockFile;
CBlockFileInfo infoLastBlockFile; CBlockFileInfo infoLastBlockFile;
int nLastBlockFile = 0; 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 { namespace {
struct CBlockReject {
unsigned char chRejectCode;
string strRejectReason;
uint256 hashBlock;
};
// Maintain validation-specific state about nodes, protected by cs_main, instead // Maintain validation-specific state about nodes, protected by cs_main, instead
// by CNode's own locks. This simplifies asynchronous operation, where // by CNode's own locks. This simplifies asynchronous operation, where
// processing of incoming data is done after the ProcessMessage call returns, // processing of incoming data is done after the ProcessMessage call returns,
// and we're no longer holding the node's locks. // and we're no longer holding the node's locks.
struct CNodeState { struct CNodeState {
// Accumulated misbehaviour score for this peer.
int nMisbehavior; int nMisbehavior;
// Whether this peer should be disconnected and banned.
bool fShouldBan; bool fShouldBan;
// String name of this peer (debugging/logging purposes).
std::string name; std::string name;
// List of asynchronously-determined block rejections to notify this peer about.
std::vector<CBlockReject> rejects;
CNodeState() { CNodeState() {
nMisbehavior = 0; nMisbehavior = 0;
@ -171,6 +202,7 @@ struct CNodeState {
} }
}; };
// Map maintaining per-node state. Requires cs_main.
map<NodeId, CNodeState> mapNodeState; map<NodeId, CNodeState> mapNodeState;
// Requires cs_main. // Requires cs_main.
@ -1242,6 +1274,24 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip)
CheckForkWarningConditions(); 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) void static InvalidChainFound(CBlockIndex* pindexNew)
{ {
if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork) if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork)
@ -1263,69 +1313,25 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
CheckForkWarningConditions(); CheckForkWarningConditions();
} }
void static InvalidBlockFound(CBlockIndex *pindex) { 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 (!state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID; pindex->nStatus |= BLOCK_FAILED_VALID;
pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)); pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex));
setBlockIndexValid.erase(pindex); setBlockIndexValid.erase(pindex);
InvalidChainFound(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;
}
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);
}
void UpdateTime(CBlockHeader& block, const CBlockIndex* pindexPrev) void UpdateTime(CBlockHeader& block, const CBlockIndex* pindexPrev)
{ {
block.nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); block.nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
@ -1746,102 +1752,10 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
return true; return true;
} }
bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) // Update the on-disk chain state.
{ bool static WriteChainState(CValidationState &state) {
mempool.check(pcoinsTip); static int64_t nLastWrite = 0;
if (!IsInitialBlockDownload() || pcoinsTip->GetCacheSize() > nCoinCacheSize || GetTimeMicros() > nLastWrite + 600*1000000) {
// 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) {
// Typical CCoins structures on disk are around 100 bytes in size. // Typical CCoins structures on disk are around 100 bytes in size.
// Pushing a new one to the database can cause it to be written // 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 // 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(); pblocktree->Sync();
if (!pcoinsTip->Flush()) if (!pcoinsTip->Flush())
return state.Abort(_("Failed to write to coin database")); return state.Abort(_("Failed to write to coin database"));
nLastWrite = GetTimeMicros();
} }
return true;
}
// At this point, all changes have been done to the database. // Update chainActive and related internal data structures.
// Proceed by updating the memory structures. void static UpdateTip(CBlockIndex *pindexNew) {
// Register new best chain
chainActive.SetTip(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) // Update best block in wallet (so we can detect restored wallets)
if ((pindexNew->nHeight % 20160) == 0 || (!fIsInitialDownload && (pindexNew->nHeight % 144) == 0)) bool fIsInitialDownload = IsInitialBlockDownload();
g_signals.SetBestChain(chainActive.GetLocator(pindexNew)); if ((chainActive.Height() % 20160) == 0 || (!fIsInitialDownload && (chainActive.Height() % 144) == 0))
g_signals.SetBestChain(chainActive.GetLocator());
// New best block // New best block
nTimeBestReceived = GetTime(); nTimeBestReceived = GetTime();
mempool.AddTransactionsUpdated(1); mempool.AddTransactionsUpdated(1);
LogPrintf("SetBestChain: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f\n", 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)pindexNew->nChainTx, 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()), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()),
Checkpoints::GuessVerificationProgress(chainActive.Tip())); Checkpoints::GuessVerificationProgress(chainActive.Tip()));
@ -1906,19 +1806,182 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
// strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user: // 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!"); strMiscWarning = _("Warning: This version is obsolete, upgrade required!");
} }
}
// 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();
{
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", ""); std::string strCmd = GetArg("-blocknotify", "");
if (!IsInitialBlockDownload() && !strCmd.empty())
if (!fIsInitialDownload && !strCmd.empty())
{ {
boost::replace_all(strCmd, "%s", chainActive.Tip()->GetBlockHash().GetHex()); boost::replace_all(strCmd, "%s", chainActive.Tip()->GetBlockHash().GetHex());
boost::thread t(runCommand, strCmd); // thread runs free boost::thread t(runCommand, strCmd); // thread runs free
} }
}
return true; return true;
} }
bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos& pos) bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos& pos)
{ {
// Check for duplicate // Check for duplicate
@ -1928,7 +1991,12 @@ bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos
// Construct new block index object // Construct new block index object
CBlockIndex* pindexNew = new CBlockIndex(block); CBlockIndex* pindexNew = new CBlockIndex(block);
{
LOCK(cs_nBlockSequenceId);
pindexNew->nSequenceId = nBlockSequenceId++;
}
assert(pindexNew); assert(pindexNew);
mapAlreadyAskedFor.erase(CInv(MSG_BLOCK, hash));
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first); pindexNew->phashBlock = &((*mi).first);
map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock); 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")); return state.Abort(_("Failed to write block index"));
// New best? // New best?
if (!ConnectBestBlock(state)) if (!ActivateBestChain(state))
return false; return false;
if (pindexNew == chainActive.Tip()) 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())); return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", hash.ToString()));
// Preliminary checks // Preliminary checks
if (!CheckBlock(*pblock, state)) if (!CheckBlock(*pblock, state)) {
if (state.CorruptionPossible())
mapAlreadyAskedFor.erase(CInv(MSG_BLOCK, hash));
return error("ProcessBlock() : CheckBlock FAILED"); return error("ProcessBlock() : CheckBlock FAILED");
}
CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex);
if (pcheckpoint && pblock->hashPrevBlock != (chainActive.Tip() ? chainActive.Tip()->GetBlockHash() : uint256(0))) 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) void static ProcessGetData(CNode* pfrom)
{ {
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin(); std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
@ -3584,18 +3637,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
pfrom->AddInventoryKnown(inv); pfrom->AddInventoryKnown(inv);
LOCK(cs_main); LOCK(cs_main);
// Remember who we got this block from.
mapBlockSource[inv.hash] = pfrom->GetId();
CValidationState state; CValidationState state;
if (ProcessBlock(state, pfrom, &block) || state.CorruptionPossible()) ProcessBlock(state, pfrom, &block);
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);
}
} }
@ -4042,16 +4088,21 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
if (!lockMain) if (!lockMain)
return true; return true;
if (State(pto->GetId())->fShouldBan) { CNodeState &state = *State(pto->GetId());
if (state.fShouldBan) {
if (pto->addr.IsLocal()) if (pto->addr.IsLocal())
LogPrintf("Warning: not banning local node %s!\n", pto->addr.ToString()); LogPrintf("Warning: not banning local node %s!\n", pto->addr.ToString());
else { else {
pto->fDisconnect = true; pto->fDisconnect = true;
CNode::Ban(pto->addr); 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 // Start block sync
if (pto->fStartSync && !fImporting && !fReindex) { if (pto->fStartSync && !fImporting && !fReindex) {
pto->fStartSync = false; pto->fStartSync = false;

View file

@ -165,10 +165,8 @@ bool IsInitialBlockDownload();
std::string GetWarnings(std::string strFor); std::string GetWarnings(std::string strFor);
/** Retrieve a transaction (from memory pool, or from disk, if possible) */ /** Retrieve a transaction (from memory pool, or from disk, if possible) */
bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false); 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 */ /** 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); int64_t GetBlockValue(int nHeight, int64_t nFees);
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock); unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock);
@ -716,6 +714,8 @@ public:
unsigned int nBits; unsigned int nBits;
unsigned int nNonce; unsigned int nNonce;
// (memory only) Sequencial id assigned to distinguish order in which blocks are received.
uint32_t nSequenceId;
CBlockIndex() CBlockIndex()
{ {
@ -729,6 +729,7 @@ public:
nTx = 0; nTx = 0;
nChainTx = 0; nChainTx = 0;
nStatus = 0; nStatus = 0;
nSequenceId = 0;
nVersion = 0; nVersion = 0;
hashMerkleRoot = 0; hashMerkleRoot = 0;
@ -749,6 +750,7 @@ public:
nTx = 0; nTx = 0;
nChainTx = 0; nChainTx = 0;
nStatus = 0; nStatus = 0;
nSequenceId = 0;
nVersion = block.nVersion; nVersion = block.nVersion;
hashMerkleRoot = block.hashMerkleRoot; hashMerkleRoot = block.hashMerkleRoot;
@ -958,23 +960,23 @@ public:
AbortNode(msg); AbortNode(msg);
return Error(); return Error();
} }
bool IsValid() { bool IsValid() const {
return mode == MODE_VALID; return mode == MODE_VALID;
} }
bool IsInvalid() { bool IsInvalid() const {
return mode == MODE_INVALID; return mode == MODE_INVALID;
} }
bool IsError() { bool IsError() const {
return mode == MODE_ERROR; return mode == MODE_ERROR;
} }
bool IsInvalid(int &nDoSOut) { bool IsInvalid(int &nDoSOut) const {
if (IsInvalid()) { if (IsInvalid()) {
nDoSOut = nDoS; nDoSOut = nDoS;
return true; return true;
} }
return false; return false;
} }
bool CorruptionPossible() { bool CorruptionPossible() const {
return corruptionPossible; return corruptionPossible;
} }
unsigned char GetRejectCode() const { return chRejectCode; } unsigned char GetRejectCode() const { return chRejectCode; }
@ -1041,6 +1043,8 @@ public:
/** The currently-connected chain of blocks. */ /** The currently-connected chain of blocks. */
extern CChain chainActive; 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) */ /** Global variable that points to the active CCoinsView (protected by cs_main) */
extern CCoinsViewCache *pcoinsTip; extern CCoinsViewCache *pcoinsTip;