aboutsummaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
authorPieter Wuille <pieter.wuille@gmail.com>2013-11-16 19:28:24 +0100
committerPieter Wuille <pieter.wuille@gmail.com>2014-01-27 21:20:41 +0100
commit75f51f2a63e0ebe34ab290c2b7141dd240b98c3b (patch)
treeed9be9e3acbcd9d9df9f67264042109b8cd7db98 /src/main.cpp
parent0ec16f35d6b8d9f1fe876f1405f105f6e5aa0933 (diff)
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).
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp438
1 files changed, 243 insertions, 195 deletions
diff --git a/src/main.cpp b/src/main.cpp
index 6e1919c9a6..660380b991 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -41,6 +41,7 @@ CTxMemPool mempool;
map<uint256, CBlockIndex*> mapBlockIndex;
CChain chainActive;
+CChain chainMostWork;
int64_t nTimeBestReceived = 0;
int nScriptCheckThreads = 0;
bool fImporting = false;
@@ -77,13 +78,21 @@ namespace {
struct CBlockIndexWorkComparator
{
bool operator()(CBlockIndex *pa, CBlockIndex *pb) {
+ // First sort by most total work, ...
if (pa->nChainWork > pb->nChainWork) return false;
if (pa->nChainWork < pb->nChainWork) return true;
- if (pa->GetBlockHash() < pb->GetBlockHash()) return false;
- if (pa->GetBlockHash() > pb->GetBlockHash()) return true;
+ // ... then by earliest time received, ...
+ if (pa->nSequenceId < pb->nSequenceId) return false;
+ if (pa->nSequenceId > pb->nSequenceId) return true;
- return false; // identical blocks
+ // Use pointer address as tie breaker (should only happen with blocks
+ // loaded from disk, as those all have id 0).
+ if (pa < pb) return false;
+ if (pa > pb) return true;
+
+ // Identical blocks.
+ return false;
}
};
@@ -93,6 +102,16 @@ set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; // may contain
CCriticalSection cs_LastBlockFile;
CBlockFileInfo infoLastBlockFile;
int nLastBlockFile = 0;
+
+// Every received block is assigned a unique and increasing identifier, so we
+// know which one to give priority in case of a fork.
+CCriticalSection cs_nBlockSequenceId;
+// Blocks loaded from disk are assigned id 0, so start the counter at 1.
+uint32_t nBlockSequenceId = 1;
+
+// Sources of received blocks, to be able to send them reject messages or ban
+// them, if processing happens afterwards. Protected by cs_main.
+map<uint256, NodeId> mapBlockSource;
}
//////////////////////////////////////////////////////////////////////////////
@@ -156,14 +175,26 @@ void SyncWithWallets(const uint256 &hash, const CTransaction &tx, const CBlock *
//
namespace {
+
+struct CBlockReject {
+ unsigned char chRejectCode;
+ string strRejectReason;
+ uint256 hashBlock;
+};
+
// Maintain validation-specific state about nodes, protected by cs_main, instead
// by CNode's own locks. This simplifies asynchronous operation, where
// processing of incoming data is done after the ProcessMessage call returns,
// and we're no longer holding the node's locks.
struct CNodeState {
+ // Accumulated misbehaviour score for this peer.
int nMisbehavior;
+ // Whether this peer should be disconnected and banned.
bool fShouldBan;
+ // String name of this peer (debugging/logging purposes).
std::string name;
+ // List of asynchronously-determined block rejections to notify this peer about.
+ std::vector<CBlockReject> rejects;
CNodeState() {
nMisbehavior = 0;
@@ -171,6 +202,7 @@ struct CNodeState {
}
};
+// Map maintaining per-node state. Requires cs_main.
map<NodeId, CNodeState> mapNodeState;
// Requires cs_main.
@@ -1242,6 +1274,24 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip)
CheckForkWarningConditions();
}
+void Misbehaving(NodeId pnode, int howmuch)
+{
+ if (howmuch == 0)
+ return;
+
+ CNodeState *state = State(pnode);
+ if (state == NULL)
+ return;
+
+ state->nMisbehavior += howmuch;
+ if (state->nMisbehavior >= GetArg("-banscore", 100))
+ {
+ LogPrintf("Misbehaving: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior);
+ state->fShouldBan = true;
+ } else
+ LogPrintf("Misbehaving: %s (%d -> %d)\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior);
+}
+
void static InvalidChainFound(CBlockIndex* pindexNew)
{
if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork)
@@ -1263,67 +1313,23 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
CheckForkWarningConditions();
}
-void static InvalidBlockFound(CBlockIndex *pindex) {
- pindex->nStatus |= BLOCK_FAILED_VALID;
- pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex));
- setBlockIndexValid.erase(pindex);
- InvalidChainFound(pindex);
- if (chainActive.Next(pindex)) {
- CValidationState stateDummy;
- ConnectBestBlock(stateDummy); // reorganise away from the failed block
- }
-}
-
-bool ConnectBestBlock(CValidationState &state) {
- do {
- CBlockIndex *pindexNewBest;
-
- {
- std::set<CBlockIndex*,CBlockIndexWorkComparator>::reverse_iterator it = setBlockIndexValid.rbegin();
- if (it == setBlockIndexValid.rend())
- return true;
- pindexNewBest = *it;
+void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) {
+ int nDoS = 0;
+ if (state.IsInvalid(nDoS)) {
+ std::map<uint256, NodeId>::iterator it = mapBlockSource.find(pindex->GetBlockHash());
+ if (it != mapBlockSource.end() && State(it->second)) {
+ CBlockReject reject = {state.GetRejectCode(), state.GetRejectReason(), pindex->GetBlockHash()};
+ State(it->second)->rejects.push_back(reject);
+ if (nDoS > 0)
+ Misbehaving(it->second, nDoS);
}
-
- if (pindexNewBest == chainActive.Tip() || (chainActive.Tip() && pindexNewBest->nChainWork == chainActive.Tip()->nChainWork))
- return true; // nothing to do
-
- // check ancestry
- CBlockIndex *pindexTest = pindexNewBest;
- std::vector<CBlockIndex*> vAttach;
- do {
- if (pindexTest->nStatus & BLOCK_FAILED_MASK) {
- // mark descendants failed
- CBlockIndex *pindexFailed = pindexNewBest;
- while (pindexTest != pindexFailed) {
- pindexFailed->nStatus |= BLOCK_FAILED_CHILD;
- setBlockIndexValid.erase(pindexFailed);
- pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexFailed));
- pindexFailed = pindexFailed->pprev;
- }
- InvalidChainFound(pindexNewBest);
- break;
- }
-
- if (chainActive.Tip() == NULL || pindexTest->nChainWork > chainActive.Tip()->nChainWork)
- vAttach.push_back(pindexTest);
-
- if (pindexTest->pprev == NULL || chainActive.Next(pindexTest)) {
- reverse(vAttach.begin(), vAttach.end());
- BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) {
- boost::this_thread::interruption_point();
- try {
- if (!SetBestChain(state, pindexSwitch))
- return false;
- } catch(std::runtime_error &e) {
- return state.Abort(_("System error: ") + e.what());
- }
- }
- return true;
- }
- pindexTest = pindexTest->pprev;
- } while(true);
- } while(true);
+ }
+ if (!state.CorruptionPossible()) {
+ pindex->nStatus |= BLOCK_FAILED_VALID;
+ pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex));
+ setBlockIndexValid.erase(pindex);
+ InvalidChainFound(pindex);
+ }
}
void UpdateTime(CBlockHeader& block, const CBlockIndex* pindexPrev)
@@ -1746,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!");
}
+}
- std::string strCmd = GetArg("-blocknotify", "");
-
- if (!fIsInitialDownload && !strCmd.empty())
+// Disconnect chainActive's tip.
+bool static DisconnectTip(CValidationState &state) {
+ CBlockIndex *pindexDelete = chainActive.Tip();
+ assert(pindexDelete);
+ mempool.check(pcoinsTip);
+ // Read block from disk.
+ CBlock block;
+ if (!ReadBlockFromDisk(block, pindexDelete))
+ return state.Abort(_("Failed to read block"));
+ // Apply the block atomically to the chain state.
+ int64_t nStart = GetTimeMicros();
{
- boost::replace_all(strCmd, "%s", chainActive.Tip()->GetBlockHash().GetHex());
- boost::thread t(runCommand, strCmd); // thread runs free
+ CCoinsViewCache view(*pcoinsTip, true);
+ if (!DisconnectBlock(block, state, pindexDelete, view))
+ return error("DisconnectTip() : DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
+ assert(view.Flush());
}
+ if (fBenchmark)
+ LogPrintf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
+ // Write the chain state to disk, if necessary.
+ if (!WriteChainState(state))
+ return false;
+ // Ressurect mempool transactions from the disconnected block.
+ BOOST_FOREACH(const CTransaction &tx, block.vtx) {
+ // ignore validation errors in resurrected transactions
+ CValidationState stateDummy;
+ if (!tx.IsCoinBase())
+ if (!AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL))
+ mempool.remove(tx, true);
+ }
+ mempool.check(pcoinsTip);
+ // Update chainActive and related variables.
+ UpdateTip(pindexDelete->pprev);
+ return true;
}
-bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
-{
+// Connect a new block to chainActive.
+bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew) {
+ assert(pindexNew->pprev == chainActive.Tip());
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)
+ // 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();
{
- while (plonger->nHeight > pfork->nHeight) {
- plonger = plonger->pprev;
- assert(plonger != NULL);
+ 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());
}
- if (pfork == plonger)
- break;
- pfork = pfork->pprev;
- assert(pfork != NULL);
+ 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;
+}
- // 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());
+// 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;
- 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());
+ // 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);
}
- // 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);
- }
+ do {
+ // Find the best candidate header.
+ {
+ std::set<CBlockIndex*, CBlockIndexWorkComparator>::reverse_iterator it = setBlockIndexValid.rbegin();
+ if (it == setBlockIndexValid.rend())
+ return;
+ pindexNew = *it;
+ }
- // 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);
+ // 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;
}
- return error("SetBestBlock() : ConnectBlock %s failed", pindex->GetBlockHash().ToString());
+ pindexTest = pindexTest->pprev;
}
- if (fBenchmark)
- LogPrintf("- Connect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
+ if (fInvalidAncestor)
+ continue;
- // Queue memory transactions to delete
- BOOST_FOREACH(const CTransaction& tx, block.vtx)
- vDelete.push_back(tx);
- }
+ break;
+ } while(true);
- // 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);
+ // Check whether it's actually an improvement.
+ if (chainMostWork.Tip() && !CBlockIndexWorkComparator()(chainMostWork.Tip(), pindexNew))
+ return;
- if (!WriteChainState(state))
- return false;
+ // We have a new best.
+ chainMostWork.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);
- }
+// 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;
- // Delete redundant memory transactions that are in the connected branch
- BOOST_FOREACH(CTransaction& tx, vDelete) {
- mempool.remove(tx);
- mempool.removeConflicts(tx);
+ // 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;
+ }
+ }
+ }
}
- mempool.check(pcoinsTip);
+ 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
+ }
+ }
- 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;