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
|
||||
CValidationState state;
|
||||
if (!ConnectBestBlock(state))
|
||||
if (!ActivateBestChain(state))
|
||||
strErrors << "Failed to connect best block";
|
||||
|
||||
std::vector<boost::filesystem::path> vImportFiles;
|
||||
|
|
462
src/main.cpp
462
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,69 +1313,25 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
|
|||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
block.nTime = max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
|
||||
|
@ -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!");
|
||||
}
|
||||
}
|
||||
|
||||
// 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", "");
|
||||
|
||||
if (!fIsInitialDownload && !strCmd.empty())
|
||||
if (!IsInitialBlockDownload() && !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)
|
||||
{
|
||||
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);
|
||||
|
||||
if (!WriteChainState(state))
|
||||
return false;
|
||||
|
||||
// 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);
|
||||
|
||||
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;
|
||||
|
|
20
src/main.h
20
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;
|
||||
|
|
Loading…
Reference in a new issue