|
|
@ -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,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,18 +1806,181 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) |
|
|
|
// strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user:
|
|
|
|
// strMiscWarning 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", ""); |
|
|
|
// Disconnect chainActive's tip.
|
|
|
|
|
|
|
|
bool static DisconnectTip(CValidationState &state) { |
|
|
|
if (!fIsInitialDownload && !strCmd.empty()) |
|
|
|
CBlockIndex *pindexDelete = chainActive.Tip(); |
|
|
|
|
|
|
|
assert(pindexDelete); |
|
|
|
|
|
|
|
mempool.check(pcoinsTip); |
|
|
|
|
|
|
|
// Read block from disk.
|
|
|
|
|
|
|
|
CBlock block; |
|
|
|
|
|
|
|
if (!ReadBlockFromDisk(block, pindexDelete)) |
|
|
|
|
|
|
|
return state.Abort(_("Failed to read block")); |
|
|
|
|
|
|
|
// Apply the block atomically to the chain state.
|
|
|
|
|
|
|
|
int64_t nStart = GetTimeMicros(); |
|
|
|
{ |
|
|
|
{ |
|
|
|
boost::replace_all(strCmd, "%s", chainActive.Tip()->GetBlockHash().GetHex()); |
|
|
|
CCoinsViewCache view(*pcoinsTip, true); |
|
|
|
boost::thread t(runCommand, strCmd); // thread runs free
|
|
|
|
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; |
|
|
|
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) |
|
|
|
{ |
|
|
|
{ |
|
|
@ -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; |
|
|
|