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).
This commit is contained in:
parent
0ec16f35d6
commit
75f51f2a63
3 changed files with 265 additions and 213 deletions
|
@ -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;
|
||||||
|
|
456
src/main.cpp
456
src/main.cpp
|
@ -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,67 +1313,23 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
|
||||||
CheckForkWarningConditions();
|
CheckForkWarningConditions();
|
||||||
}
|
}
|
||||||
|
|
||||||
void static InvalidBlockFound(CBlockIndex *pindex) {
|
void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) {
|
||||||
pindex->nStatus |= BLOCK_FAILED_VALID;
|
int nDoS = 0;
|
||||||
pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex));
|
if (state.IsInvalid(nDoS)) {
|
||||||
setBlockIndexValid.erase(pindex);
|
std::map<uint256, NodeId>::iterator it = mapBlockSource.find(pindex->GetBlockHash());
|
||||||
InvalidChainFound(pindex);
|
if (it != mapBlockSource.end() && State(it->second)) {
|
||||||
if (chainActive.Next(pindex)) {
|
CBlockReject reject = {state.GetRejectCode(), state.GetRejectReason(), pindex->GetBlockHash()};
|
||||||
CValidationState stateDummy;
|
State(it->second)->rejects.push_back(reject);
|
||||||
ConnectBestBlock(stateDummy); // reorganise away from the failed block
|
if (nDoS > 0)
|
||||||
}
|
Misbehaving(it->second, nDoS);
|
||||||
}
|
|
||||||
|
|
||||||
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))
|
if (!state.CorruptionPossible()) {
|
||||||
return true; // nothing to do
|
pindex->nStatus |= BLOCK_FAILED_VALID;
|
||||||
|
pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex));
|
||||||
// check ancestry
|
setBlockIndexValid.erase(pindex);
|
||||||
CBlockIndex *pindexTest = pindexNewBest;
|
InvalidChainFound(pindex);
|
||||||
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)
|
||||||
|
@ -1746,8 +1752,10 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the on-disk chain state.
|
||||||
bool static WriteChainState(CValidationState &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.
|
// 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
|
||||||
|
@ -1759,10 +1767,12 @@ bool static WriteChainState(CValidationState &state) {
|
||||||
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update chainActive and related internal data structures.
|
||||||
void static UpdateTip(CBlockIndex *pindexNew) {
|
void static UpdateTip(CBlockIndex *pindexNew) {
|
||||||
chainActive.SetTip(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 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!");
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
mempool.check(pcoinsTip);
|
||||||
|
// Read block from disk.
|
||||||
// All modifications to the coin state will be done in this cache.
|
CBlock block;
|
||||||
// Only when all have succeeded, we push it to pcoinsTip.
|
if (!ReadBlockFromDisk(block, pindexDelete))
|
||||||
CCoinsViewCache view(*pcoinsTip, true);
|
return state.Abort(_("Failed to read block"));
|
||||||
|
// Apply the block atomically to the chain state.
|
||||||
// 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();
|
int64_t nStart = GetTimeMicros();
|
||||||
int nModified = view.GetCacheSize();
|
{
|
||||||
bool ret;
|
CCoinsViewCache view(*pcoinsTip, true);
|
||||||
ret = view.Flush();
|
if (!DisconnectBlock(block, state, pindexDelete, view))
|
||||||
assert(ret);
|
return error("DisconnectTip() : DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
|
||||||
int64_t nTime = GetTimeMicros() - nStart;
|
assert(view.Flush());
|
||||||
|
}
|
||||||
if (fBenchmark)
|
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))
|
if (!WriteChainState(state))
|
||||||
return false;
|
return false;
|
||||||
|
// Ressurect mempool transactions from the disconnected block.
|
||||||
// Resurrect memory transactions that were in the disconnected branch
|
BOOST_FOREACH(const CTransaction &tx, block.vtx) {
|
||||||
BOOST_FOREACH(CTransaction& tx, vResurrect) {
|
|
||||||
// ignore validation errors in resurrected transactions
|
// ignore validation errors in resurrected transactions
|
||||||
CValidationState stateDummy;
|
CValidationState stateDummy;
|
||||||
if (!AcceptToMemoryPool(mempool,stateDummy, tx, false, NULL))
|
if (!tx.IsCoinBase())
|
||||||
mempool.remove(tx, true);
|
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
|
// Connect a new block to chainActive.
|
||||||
BOOST_FOREACH(CTransaction& tx, vDelete) {
|
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.remove(tx);
|
||||||
mempool.removeConflicts(tx);
|
mempool.removeConflicts(tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
mempool.check(pcoinsTip);
|
mempool.check(pcoinsTip);
|
||||||
|
// Update chainActive & related variables.
|
||||||
UpdateTip(pindexNew);
|
UpdateTip(pindexNew);
|
||||||
return true;
|
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)
|
bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos& pos)
|
||||||
{
|
{
|
||||||
// Check for duplicate
|
// Check for duplicate
|
||||||
|
@ -1931,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);
|
||||||
|
@ -1953,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())
|
||||||
|
@ -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()));
|
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)))
|
||||||
|
@ -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)
|
void static ProcessGetData(CNode* pfrom)
|
||||||
{
|
{
|
||||||
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
|
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
|
||||||
|
@ -3587,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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -4045,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;
|
||||||
|
|
20
src/main.h
20
src/main.h
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue