diff options
Diffstat (limited to 'src/main.cpp')
| -rw-r--r-- | src/main.cpp | 798 |
1 files changed, 463 insertions, 335 deletions
diff --git a/src/main.cpp b/src/main.cpp index 3ff1fd3a9..53b99101d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2013 The Bitcoin developers +// Copyright (c) 2009-2014 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -17,7 +17,6 @@ #include "ui_interface.h" #include "util.h" -#include <inttypes.h> #include <sstream> #include <boost/algorithm/string/replace.hpp> @@ -41,6 +40,7 @@ CTxMemPool mempool; map<uint256, CBlockIndex*> mapBlockIndex; CChain chainActive; +CChain chainMostWork; int64_t nTimeBestReceived = 0; int nScriptCheckThreads = 0; bool fImporting = false; @@ -52,7 +52,7 @@ unsigned int nCoinCacheSize = 5000; /** Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) */ int64_t CTransaction::nMinTxFee = 10000; // Override with -mintxfee /** Fees smaller than this (in satoshi) are considered zero fee (for relaying) */ -int64_t CTransaction::nMinRelayTxFee = 10000; +int64_t CTransaction::nMinRelayTxFee = 1000; static CMedianFilter<int> cPeerBlockCounts(8, 0); // Amount of blocks that other nodes claim to have @@ -77,13 +77,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 +101,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 +174,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 +201,7 @@ struct CNodeState { } }; +// Map maintaining per-node state. Requires cs_main. map<NodeId, CNodeState> mapNodeState; // Requires cs_main. @@ -309,7 +340,7 @@ bool AddOrphanTx(const CTransaction& tx) unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); if (sz > 5000) { - LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString().c_str()); + LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString()); return false; } @@ -317,7 +348,7 @@ bool AddOrphanTx(const CTransaction& tx) BOOST_FOREACH(const CTxIn& txin, tx.vin) mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash); - LogPrint("mempool", "stored orphan tx %s (mapsz %"PRIszu")\n", hash.ToString().c_str(), + LogPrint("mempool", "stored orphan tx %s (mapsz %"PRIszu")\n", hash.ToString(), mapOrphanTransactions.size()); return true; } @@ -365,7 +396,24 @@ bool IsStandardTx(const CTransaction& tx, string& reason) return false; } - if (!IsFinalTx(tx)) { + // Treat non-final transactions as non-standard to prevent a specific type + // of double-spend attack, as well as DoS attacks. (if the transaction + // can't be mined, the attacker isn't expending resources broadcasting it) + // Basically we don't want to propagate transactions that can't included in + // the next block. + // + // However, IsFinalTx() is confusing... Without arguments, it uses + // chainActive.Height() to evaluate nLockTime; when a block is accepted, chainActive.Height() + // is set to the value of nHeight in the block. However, when IsFinalTx() + // is called within CBlock::AcceptBlock(), the height of the block *being* + // evaluated is what is used. Thus if we want to know if a transaction can + // be part of the *next* block, we need to call IsFinalTx() with one more + // than chainActive.Height(). + // + // Timestamps on the other hand don't get any special treatment, because we + // can't know what timestamp the next block will have, and there aren't + // timestamp applications where it matters. + if (!IsFinalTx(tx, chainActive.Height() + 1)) { reason = "non-final"; return false; } @@ -393,6 +441,10 @@ bool IsStandardTx(const CTransaction& tx, string& reason) reason = "scriptsig-not-pushonly"; return false; } + if (!txin.scriptSig.HasCanonicalPushes()) { + reason = "scriptsig-non-canonical-push"; + return false; + } } unsigned int nDataOut = 0; @@ -412,7 +464,7 @@ bool IsStandardTx(const CTransaction& tx, string& reason) // only one OP_RETURN txout is permitted if (nDataOut > 1) { - reason = "mucho-data"; + reason = "multi-op-return"; return false; } @@ -588,14 +640,14 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) // Basic checks that don't depend on any context if (tx.vin.empty()) return state.DoS(10, error("CheckTransaction() : vin empty"), - REJECT_INVALID, "vin empty"); + REJECT_INVALID, "bad-txns-vin-empty"); if (tx.vout.empty()) return state.DoS(10, error("CheckTransaction() : vout empty"), - REJECT_INVALID, "vout empty"); + REJECT_INVALID, "bad-txns-vout-empty"); // Size limits if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return state.DoS(100, error("CheckTransaction() : size limits failed"), - REJECT_INVALID, "oversize"); + REJECT_INVALID, "bad-txns-oversize"); // Check for negative or overflow output values int64_t nValueOut = 0; @@ -603,14 +655,14 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) { if (txout.nValue < 0) return state.DoS(100, error("CheckTransaction() : txout.nValue negative"), - REJECT_INVALID, "vout negative"); + REJECT_INVALID, "bad-txns-vout-negative"); if (txout.nValue > MAX_MONEY) return state.DoS(100, error("CheckTransaction() : txout.nValue too high"), - REJECT_INVALID, "vout too large"); + REJECT_INVALID, "bad-txns-vout-toolarge"); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) return state.DoS(100, error("CheckTransaction() : txout total out of range"), - REJECT_INVALID, "txout total too large"); + REJECT_INVALID, "bad-txns-txouttotal-toolarge"); } // Check for duplicate inputs @@ -619,7 +671,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) { if (vInOutPoints.count(txin.prevout)) return state.DoS(100, error("CheckTransaction() : duplicate inputs"), - REJECT_INVALID, "duplicate inputs"); + REJECT_INVALID, "bad-txns-inputs-duplicate"); vInOutPoints.insert(txin.prevout); } @@ -627,14 +679,14 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) { if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) return state.DoS(100, error("CheckTransaction() : coinbase script size"), - REJECT_INVALID, "coinbase script too large"); + REJECT_INVALID, "bad-cb-length"); } else { BOOST_FOREACH(const CTxIn& txin, tx.vin) if (txin.prevout.IsNull()) return state.DoS(10, error("CheckTransaction() : prevout is null"), - REJECT_INVALID, "prevout null"); + REJECT_INVALID, "bad-txns-prevout-null"); } return true; @@ -693,7 +745,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa string reason; if (Params().NetworkID() == CChainParams::MAIN && !IsStandardTx(tx, reason)) return state.DoS(0, - error("AcceptToMemoryPool : nonstandard transaction: %s", reason.c_str()), + error("AcceptToMemoryPool : nonstandard transaction: %s", reason), REJECT_NONSTANDARD, reason); // is it already in the memory pool? @@ -742,7 +794,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // are the actual inputs available? if (!view.HaveInputs(tx)) return state.Invalid(error("AcceptToMemoryPool : inputs already spent"), - REJECT_DUPLICATE, "inputs spent"); + REJECT_DUPLICATE, "bad-txns-inputs-spent"); // Bring the best block into scope view.GetBestBlock(); @@ -771,7 +823,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa int64_t txMinFee = GetMinFee(tx, nSize, true, GMF_RELAY); if (fLimitFree && nFees < txMinFee) return state.DoS(0, error("AcceptToMemoryPool : not enough fees %s, %"PRId64" < %"PRId64, - hash.ToString().c_str(), nFees, txMinFee), + hash.ToString(), nFees, txMinFee), REJECT_INSUFFICIENTFEE, "insufficient fee"); // Continuously rate-limit free transactions @@ -800,14 +852,14 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if (fRejectInsaneFee && nFees > CTransaction::nMinRelayTxFee * 10000) return error("AcceptToMemoryPool: : insane fees %s, %"PRId64" > %"PRId64, - hash.ToString().c_str(), + hash.ToString(), nFees, CTransaction::nMinRelayTxFee * 10000); // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. if (!CheckInputs(tx, state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC)) { - return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString().c_str()); + return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString()); } // Store transaction in memory pool.addUnchecked(hash, entry); @@ -819,7 +871,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } -int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const +int CMerkleTx::GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const { if (hashBlock == 0 || nIndex == -1) return 0; @@ -844,6 +896,14 @@ int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const return chainActive.Height() - pindex->nHeight + 1; } +int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const +{ + int nResult = GetDepthInMainChainINTERNAL(pindexRet); + if (nResult == 0 && !mempool.exists(GetHash())) + return -1; // Not in chain, not in mempool + + return nResult; +} int CMerkleTx::GetBlocksToMaturity() const { @@ -1005,6 +1065,31 @@ uint256 static GetOrphanRoot(const uint256& hash) } while(true); } +// Remove a random orphan block (which does not have any dependent orphans). +void static PruneOrphanBlocks() +{ + if (mapOrphanBlocksByPrev.size() <= MAX_ORPHAN_BLOCKS) + return; + + // Pick a random orphan block. + int pos = insecure_rand() % mapOrphanBlocksByPrev.size(); + std::multimap<uint256, COrphanBlock*>::iterator it = mapOrphanBlocksByPrev.begin(); + while (pos--) it++; + + // As long as this block has other orphans depending on it, move to one of those successors. + do { + std::multimap<uint256, COrphanBlock*>::iterator it2 = mapOrphanBlocksByPrev.find(it->second->hashBlock); + if (it2 == mapOrphanBlocksByPrev.end()) + break; + it = it2; + } while(1); + + uint256 hash = it->second->hashBlock; + delete it->second; + mapOrphanBlocksByPrev.erase(it); + mapOrphanBlocks.erase(hash); +} + int64_t GetBlockValue(int nHeight, int64_t nFees) { int64_t nSubsidy = 50 * COIN; @@ -1101,8 +1186,8 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead /// debug print LogPrintf("GetNextWorkRequired RETARGET\n"); LogPrintf("nTargetTimespan = %"PRId64" nActualTimespan = %"PRId64"\n", nTargetTimespan, nActualTimespan); - LogPrintf("Before: %08x %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString().c_str()); - LogPrintf("After: %08x %s\n", bnNew.GetCompact(), bnNew.getuint256().ToString().c_str()); + LogPrintf("Before: %08x %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString()); + LogPrintf("After: %08x %s\n", bnNew.GetCompact(), bnNew.getuint256().ToString()); return bnNew.GetCompact(); } @@ -1176,8 +1261,8 @@ void CheckForkWarningConditions() if (pindexBestForkTip) { LogPrintf("CheckForkWarningConditions: Warning: Large valid fork found\n forking the chain at height %d (%s)\n lasting to height %d (%s).\nChain state database corruption likely.\n", - pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString().c_str(), - pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString().c_str()); + pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString(), + pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString()); fLargeWorkForkFound = true; } else @@ -1225,6 +1310,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) @@ -1237,76 +1340,32 @@ void static InvalidChainFound(CBlockIndex* pindexNew) uiInterface.NotifyBlocksChanged(); } LogPrintf("InvalidChainFound: invalid block=%s height=%d log2_work=%.8g date=%s\n", - pindexNew->GetBlockHash().ToString().c_str(), pindexNew->nHeight, + pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, log(pindexNew->nChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", - pindexNew->GetBlockTime()).c_str()); + pindexNew->GetBlockTime())); LogPrintf("InvalidChainFound: current best=%s height=%d log2_work=%.8g date=%s\n", - chainActive.Tip()->GetBlockHash().ToString().c_str(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()).c_str()); + chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime())); 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) @@ -1350,7 +1409,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach bool CScriptCheck::operator()() const { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; if (!VerifyScript(scriptSig, scriptPubKey, *ptxTo, nIn, nFlags, nHashType)) - return error("CScriptCheck() : %s VerifySignature failed", ptxTo->GetHash().ToString().c_str()); + return error("CScriptCheck() : %s VerifySignature failed", ptxTo->GetHash().ToString()); return true; } @@ -1369,7 +1428,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach // This doesn't trigger the DoS code on purpose; if it did, it would make it easier // for an attacker to attempt to split the network. if (!inputs.HaveInputs(tx)) - return state.Invalid(error("CheckInputs() : %s inputs unavailable", tx.GetHash().ToString().c_str())); + return state.Invalid(error("CheckInputs() : %s inputs unavailable", tx.GetHash().ToString())); // While checking, GetBestBlock() refers to the parent block. // This is also true for mempool checks. @@ -1387,30 +1446,30 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach if (nSpendHeight - coins.nHeight < COINBASE_MATURITY) return state.Invalid( error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight), - REJECT_INVALID, "premature spend of coinbase"); + REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); } // Check for negative or overflow input values nValueIn += coins.vout[prevout.n].nValue; if (!MoneyRange(coins.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) return state.DoS(100, error("CheckInputs() : txin values out of range"), - REJECT_INVALID, "input values out of range"); + REJECT_INVALID, "bad-txns-inputvalues-outofrange"); } if (nValueIn < tx.GetValueOut()) - return state.DoS(100, error("CheckInputs() : %s value in < value out", tx.GetHash().ToString().c_str()), - REJECT_INVALID, "in < out"); + return state.DoS(100, error("CheckInputs() : %s value in < value out", tx.GetHash().ToString()), + REJECT_INVALID, "bad-txns-in-belowout"); // Tally transaction fees int64_t nTxFee = nValueIn - tx.GetValueOut(); if (nTxFee < 0) - return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", tx.GetHash().ToString().c_str()), - REJECT_INVALID, "fee < 0"); + return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", tx.GetHash().ToString()), + REJECT_INVALID, "bad-txns-fee-negative"); nFees += nTxFee; if (!MoneyRange(nFees)) return state.DoS(100, error("CheckInputs() : nFees out of range"), - REJECT_INVALID, "fee out of range"); + REJECT_INVALID, "bad-txns-fee-outofrange"); // The first loop above does all the inexpensive checks. // Only if ALL inputs pass do we perform expensive ECDSA signature checks. @@ -1607,7 +1666,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C uint256 hash = block.GetTxHash(i); if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned()) return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction"), - REJECT_INVALID, "BIP30"); + REJECT_INVALID, "bad-txns-BIP30"); } } @@ -1637,13 +1696,13 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C nSigOps += GetLegacySigOpCount(tx); if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("ConnectBlock() : too many sigops"), - REJECT_INVALID, "too many sigops"); + REJECT_INVALID, "bad-blk-sigops"); if (!tx.IsCoinBase()) { if (!view.HaveInputs(tx)) return state.DoS(100, error("ConnectBlock() : inputs missing/spent"), - REJECT_INVALID, "inputs missing/spent"); + REJECT_INVALID, "bad-txns-inputs-missingorspent"); if (fStrictPayToScriptHash) { @@ -1653,7 +1712,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C nSigOps += GetP2SHSigOpCount(tx, view); if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("ConnectBlock() : too many sigops"), - REJECT_INVALID, "too many sigops"); + REJECT_INVALID, "bad-blk-sigops"); } nFees += view.GetValueIn(tx)-tx.GetValueOut(); @@ -1680,7 +1739,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRId64" vs limit=%"PRId64")", block.vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees)), - REJECT_INVALID, "coinbase too large"); + REJECT_INVALID, "bad-cb-amount"); if (!control.Wait()) return state.DoS(100, false); @@ -1729,147 +1788,41 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C return true; } -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().c_str()); - LogPrintf("REORGANIZE: Connect %"PRIszu" blocks; ...%s\n", vConnect.size(), pindexNew->GetBlockHash().ToString().c_str()); - } - - // 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().c_str()); - 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().c_str()); - } - 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) { +// Update the on-disk chain state. +bool static WriteChainState(CValidationState &state) { + 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 // an overestimation, as most will delete an existing entry or // overwrite one. Still, use a conservative safety factor of 2. if (!CheckDiskSpace(100 * 2 * 2 * pcoinsTip->GetCacheSize())) - return state.Error(); + return state.Error("out of disk space"); FlushBlockFile(); pblocktree->Sync(); if (!pcoinsTip->Flush()) return state.Abort(_("Failed to write to coin database")); + nLastWrite = GetTimeMicros(); } + return true; +} - // At this point, all changes have been done to the database. - // Proceed by updating the memory structures. - - // Register new best chain +// Update chainActive and related internal data structures. +void static UpdateTip(CBlockIndex *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) - if ((pindexNew->nHeight % 20160) == 0 || (!fIsInitialDownload && (pindexNew->nHeight % 144) == 0)) - g_signals.SetBestChain(chainActive.GetLocator(pindexNew)); + bool fIsInitialDownload = IsInitialBlockDownload(); + if ((chainActive.Height() % 20160) == 0 || (!fIsInitialDownload && (chainActive.Height() % 144) == 0)) + g_signals.SetBestChain(chainActive.GetLocator()); // New best block nTimeBestReceived = GetTime(); mempool.AddTransactionsUpdated(1); - LogPrintf("SetBestChain: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f\n", - chainActive.Tip()->GetBlockHash().ToString().c_str(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx, - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()).c_str(), + 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)chainActive.Tip()->nChainTx, + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), Checkpoints::GuessVerificationProgress(chainActive.Tip())); // Check the version of the last 100 blocks to see if we need to upgrade: @@ -1884,34 +1837,202 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) pindex = pindex->pprev; } if (nUpgraded > 0) - LogPrintf("SetBestChain: %d of last 100 blocks above version %d\n", nUpgraded, CBlock::CURRENT_VERSION); + LogPrintf("SetBestChain: %d of last 100 blocks above version %d\n", nUpgraded, (int)CBlock::CURRENT_VERSION); if (nUpgraded > 100/2) // 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; +} +// 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 (!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) { // Check for duplicate uint256 hash = block.GetHash(); if (mapBlockIndex.count(hash)) - return state.Invalid(error("AddToBlockIndex() : %s already exists", hash.ToString().c_str())); + return state.Invalid(error("AddToBlockIndex() : %s already exists", hash.ToString()), 0, "duplicate"); // 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); @@ -1933,7 +2054,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()) @@ -1970,7 +2091,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd } } else { while (infoLastBlockFile.nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { - LogPrintf("Leaving block file %i: %s\n", nLastBlockFile, infoLastBlockFile.ToString().c_str()); + LogPrintf("Leaving block file %i: %s\n", nLastBlockFile, infoLastBlockFile.ToString()); FlushBlockFile(true); nLastBlockFile++; infoLastBlockFile.SetNull(); @@ -1997,7 +2118,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd } } else - return state.Error(); + return state.Error("out of disk space"); } } @@ -2043,7 +2164,7 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne } } else - return state.Error(); + return state.Error("out of disk space"); } return true; @@ -2058,26 +2179,26 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo // Size limits if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return state.DoS(100, error("CheckBlock() : size limits failed"), - REJECT_INVALID, "block size too large"); + REJECT_INVALID, "bad-blk-length"); // Check proof of work matches claimed amount if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits)) return state.DoS(50, error("CheckBlock() : proof of work failed"), - REJECT_INVALID, "invalid pow"); + REJECT_INVALID, "high-hash"); // Check timestamp if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) return state.Invalid(error("CheckBlock() : block timestamp too far in the future"), - REJECT_INVALID, "time in future"); + REJECT_INVALID, "time-too-new"); // First transaction must be coinbase, the rest must not be if (block.vtx.empty() || !block.vtx[0].IsCoinBase()) return state.DoS(100, error("CheckBlock() : first tx is not coinbase"), - REJECT_INVALID, "no coinbase"); + REJECT_INVALID, "bad-cb-missing"); for (unsigned int i = 1; i < block.vtx.size(); i++) if (block.vtx[i].IsCoinBase()) return state.DoS(100, error("CheckBlock() : more than one coinbase"), - REJECT_INVALID, "duplicate coinbase"); + REJECT_INVALID, "bad-cb-multiple"); // Check transactions BOOST_FOREACH(const CTransaction& tx, block.vtx) @@ -2097,7 +2218,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo } if (uniqueTx.size() != block.vtx.size()) return state.DoS(100, error("CheckBlock() : duplicate transaction"), - REJECT_INVALID, "duplicate transaction", true); + REJECT_INVALID, "bad-txns-duplicate", true); unsigned int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, block.vtx) @@ -2106,12 +2227,12 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo } if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"), - REJECT_INVALID, "sig op count", true); + REJECT_INVALID, "bad-blk-sigops", true); // Check merkle root if (fCheckMerkleRoot && block.hashMerkleRoot != block.vMerkleTree.back()) return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"), - REJECT_INVALID, "bad merkle root", true); + REJECT_INVALID, "bad-txnmrklroot", true); return true; } @@ -2121,7 +2242,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp) // Check for duplicate uint256 hash = block.GetHash(); if (mapBlockIndex.count(hash)) - return state.Invalid(error("AcceptBlock() : block already in mapBlockIndex")); + return state.Invalid(error("AcceptBlock() : block already in mapBlockIndex"), 0, "duplicate"); // Get prev block index CBlockIndex* pindexPrev = NULL; @@ -2129,31 +2250,36 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp) if (hash != Params().HashGenesisBlock()) { map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(block.hashPrevBlock); if (mi == mapBlockIndex.end()) - return state.DoS(10, error("AcceptBlock() : prev block not found")); + return state.DoS(10, error("AcceptBlock() : prev block not found"), 0, "bad-prevblk"); pindexPrev = (*mi).second; nHeight = pindexPrev->nHeight+1; // Check proof of work if (block.nBits != GetNextWorkRequired(pindexPrev, &block)) return state.DoS(100, error("AcceptBlock() : incorrect proof of work"), - REJECT_INVALID, "bad pow"); + REJECT_INVALID, "bad-diffbits"); // Check timestamp against prev if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) return state.Invalid(error("AcceptBlock() : block's timestamp is too early"), - REJECT_INVALID, "timestamp too early"); + REJECT_INVALID, "time-too-old"); // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, block.vtx) if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) return state.DoS(10, error("AcceptBlock() : contains a non-final transaction"), - REJECT_INVALID, "non-final tx"); + REJECT_INVALID, "bad-txns-nonfinal"); // Check that the block chain matches the known block chain up to a checkpoint if (!Checkpoints::CheckBlock(nHeight, hash)) return state.DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight), REJECT_CHECKPOINT, "checkpoint mismatch"); + // Don't accept any forks from the main chain prior to last checkpoint + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); + if (pcheckpoint && nHeight < pcheckpoint->nHeight) + return state.DoS(100, error("AcceptBlock() : forked chain older than last checkpoint (height %d)", nHeight)); + // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: if (block.nVersion < 2) { @@ -2161,7 +2287,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp) (TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100))) { return state.Invalid(error("AcceptBlock() : rejected nVersion=1 block"), - REJECT_OBSOLETE, "version 1 blocks obsolete"); + REJECT_OBSOLETE, "bad-version"); } } // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height @@ -2175,7 +2301,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp) if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || !std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase"), - REJECT_INVALID, "height incorrect in coinbase"); + REJECT_INVALID, "bad-cb-height"); } } } @@ -2252,13 +2378,16 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl // Check for duplicate uint256 hash = pblock->GetHash(); if (mapBlockIndex.count(hash)) - return state.Invalid(error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString().c_str())); + return state.Invalid(error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString()), 0, "duplicate"); if (mapOrphanBlocks.count(hash)) - return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", hash.ToString().c_str())); + return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", hash.ToString()), 0, "duplicate"); // 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))) @@ -2268,7 +2397,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl if (deltaTime < 0) { return state.DoS(100, error("ProcessBlock() : block with timestamp before last checkpoint"), - REJECT_CHECKPOINT, "timestamp before checkpoint"); + REJECT_CHECKPOINT, "time-too-old"); } CBigNum bnNewBlock; bnNewBlock.SetCompact(pblock->nBits); @@ -2277,7 +2406,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl if (bnNewBlock > bnRequired) { return state.DoS(100, error("ProcessBlock() : block with too little proof-of-work"), - REJECT_INVALID, "invalid pow"); + REJECT_INVALID, "bad-diffbits"); } } @@ -2285,10 +2414,11 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl // If we don't already have its previous block, shunt it off to holding area until we get it if (pblock->hashPrevBlock != 0 && !mapBlockIndex.count(pblock->hashPrevBlock)) { - LogPrintf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().c_str()); + LogPrintf("ProcessBlock: ORPHAN BLOCK %lu, prev=%s\n", (unsigned long)mapOrphanBlocks.size(), pblock->hashPrevBlock.ToString()); // Accept orphans as long as there is a node to request its parents from if (pfrom) { + PruneOrphanBlocks(); COrphanBlock* pblock2 = new COrphanBlock(); { CDataStream ss(SER_DISK, CLIENT_VERSION); @@ -2502,7 +2632,7 @@ uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch) { bool AbortNode(const std::string &strMessage) { strMiscWarning = strMessage; - LogPrintf("*** %s\n", strMessage.c_str()); + LogPrintf("*** %s\n", strMessage); uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR); StartShutdown(); return false; @@ -2529,12 +2659,12 @@ FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly) if (!file && !fReadOnly) file = fopen(path.string().c_str(), "wb+"); if (!file) { - LogPrintf("Unable to open file %s\n", path.string().c_str()); + LogPrintf("Unable to open file %s\n", path.string()); return NULL; } if (pos.nPos) { if (fseek(file, pos.nPos, SEEK_SET)) { - LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, path.string().c_str()); + LogPrintf("Unable to seek to position %u of %s\n", pos.nPos, path.string()); fclose(file); return NULL; } @@ -2601,7 +2731,7 @@ bool static LoadBlockIndexDB() pblocktree->ReadLastBlockFile(nLastBlockFile); LogPrintf("LoadBlockIndexDB(): last block file = %i\n", nLastBlockFile); if (pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile)) - LogPrintf("LoadBlockIndexDB(): last block file info: %s\n", infoLastBlockFile.ToString().c_str()); + LogPrintf("LoadBlockIndexDB(): last block file info: %s\n", infoLastBlockFile.ToString()); // Check whether we need to continue reindexing bool fReindexing = false; @@ -2618,8 +2748,8 @@ bool static LoadBlockIndexDB() return true; chainActive.SetTip(it->second); LogPrintf("LoadBlockIndexDB(): hashBestChain=%s height=%d date=%s\n", - chainActive.Tip()->GetBlockHash().ToString().c_str(), chainActive.Height(), - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()).c_str()); + chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime())); return true; } @@ -2649,24 +2779,24 @@ bool VerifyDB(int nCheckLevel, int nCheckDepth) CBlock block; // check level 0: read from disk if (!ReadBlockFromDisk(block, pindex)) - return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); // check level 1: verify block validity if (nCheckLevel >= 1 && !CheckBlock(block, state)) - return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); // check level 2: verify undo validity if (nCheckLevel >= 2 && pindex) { CBlockUndo undo; CDiskBlockPos pos = pindex->GetUndoPos(); if (!pos.IsNull()) { if (!undo.ReadFromDisk(pos, pindex->pprev->GetBlockHash())) - return error("VerifyDB() : *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + return error("VerifyDB() : *** found bad undo data at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); } } // check level 3: check for inconsistencies during memory-only disconnect of tip blocks if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= 2*nCoinCacheSize + 32000) { bool fClean = true; if (!DisconnectBlock(block, state, pindex, coins, &fClean)) - return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); pindexState = pindex->pprev; if (!fClean) { nGoodTransactions = 0; @@ -2686,9 +2816,9 @@ bool VerifyDB(int nCheckLevel, int nCheckDepth) pindex = chainActive.Next(pindex); CBlock block; if (!ReadBlockFromDisk(block, pindex)) - return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); if (!ConnectBlock(block, state, pindex, coins)) - return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } } @@ -2796,7 +2926,7 @@ void PrintBlockTree() LogPrintf("%d (blk%05u.dat:0x%x) %s tx %"PRIszu"", pindex->nHeight, pindex->GetBlockPos().nFile, pindex->GetBlockPos().nPos, - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", block.GetBlockTime()).c_str(), + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", block.GetBlockTime()), block.vtx.size()); // put the main time-chain first @@ -2987,24 +3117,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.c_str(), state->nMisbehavior-howmuch, state->nMisbehavior); - state->fShouldBan = true; - } else - LogPrintf("Misbehaving: %s (%d -> %d)\n", state->name.c_str(), state->nMisbehavior-howmuch, state->nMisbehavior); -} - void static ProcessGetData(CNode* pfrom) { std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin(); @@ -3025,10 +3137,28 @@ void static ProcessGetData(CNode* pfrom) if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) { - // Send block from disk + bool send = false; map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(inv.hash); if (mi != mapBlockIndex.end()) { + // If the requested block is at a height below our last + // checkpoint, only serve it if it's in the checkpointed chain + int nHeight = mi->second->nHeight; + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); + if (pcheckpoint && nHeight < pcheckpoint->nHeight) { + if (!chainActive.Contains(mi->second)) + { + LogPrintf("ProcessGetData(): ignoring request for old block that isn't in the main chain\n"); + } else { + send = true; + } + } else { + send = true; + } + } + if (send) + { + // Send block from disk CBlock block; ReadBlockFromDisk(block, (*mi).second); if (inv.type == MSG_BLOCK) @@ -3120,7 +3250,7 @@ void static ProcessGetData(CNode* pfrom) bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { RandAddSeedPerfmon(); - LogPrint("net", "received: %s (%"PRIszu" bytes)\n", strCommand.c_str(), vRecv.size()); + LogPrint("net", "received: %s (%"PRIszu" bytes)\n", strCommand, vRecv.size()); if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); @@ -3149,7 +3279,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (pfrom->nVersion < MIN_PEER_PROTO_VERSION) { // disconnect from peers older than this proto version - LogPrintf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion); + LogPrintf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString(), pfrom->nVersion); pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION)); pfrom->fDisconnect = true; @@ -3180,7 +3310,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // Disconnect if we connected to ourself if (nNonce == nLocalHostNonce && nNonce > 1) { - LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString().c_str()); + LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString()); pfrom->fDisconnect = true; return true; } @@ -3230,7 +3360,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fSuccessfullyConnected = true; - LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", pfrom->cleanSubVer.c_str(), pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString().c_str(), addrFrom.ToString().c_str(), pfrom->addr.ToString().c_str()); + LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", pfrom->cleanSubVer, pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString(), addrFrom.ToString(), pfrom->addr.ToString()); AddTimeData(pfrom->addr, nTime); @@ -3349,7 +3479,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->AddInventoryKnown(inv); bool fAlreadyHave = AlreadyHave(inv); - LogPrint("net", " got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new"); + LogPrint("net", " got inventory: %s %s\n", inv.ToString(), fAlreadyHave ? "have" : "new"); if (!fAlreadyHave) { if (!fImporting && !fReindex) @@ -3362,7 +3492,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // this situation and push another getblocks to continue. PushGetBlocks(pfrom, mapBlockIndex[inv.hash], uint256(0)); if (fDebug) - LogPrintf("force request: %s\n", inv.ToString().c_str()); + LogPrintf("force request: %s\n", inv.ToString()); } // Track requests for our stuff @@ -3385,7 +3515,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) LogPrint("net", "received getdata (%"PRIszu" invsz)\n", vInv.size()); if ((fDebug && vInv.size() > 0) || (vInv.size() == 1)) - LogPrint("net", "received getdata for: %s\n", vInv[0].ToString().c_str()); + LogPrint("net", "received getdata for: %s\n", vInv[0].ToString()); pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); ProcessGetData(pfrom); @@ -3407,12 +3537,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (pindex) pindex = chainActive.Next(pindex); int nLimit = 500; - LogPrint("net", "getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().c_str(), nLimit); + LogPrint("net", "getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString(), nLimit); for (; pindex; pindex = chainActive.Next(pindex)) { if (pindex->GetBlockHash() == hashStop) { - LogPrint("net", " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + LogPrint("net", " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); break; } pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); @@ -3420,7 +3550,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { // When this block is requested, we'll send an inv that'll make them // getblocks the next batch of inventory. - LogPrint("net", " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + LogPrint("net", " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); pfrom->hashContinue = pindex->GetBlockHash(); break; } @@ -3456,7 +3586,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end vector<CBlock> vHeaders; int nLimit = 2000; - LogPrint("net", "getheaders %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().c_str()); + LogPrint("net", "getheaders %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString()); for (; pindex; pindex = chainActive.Next(pindex)) { vHeaders.push_back(pindex->GetBlockHeader()); @@ -3491,8 +3621,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) LogPrint("mempool", "AcceptToMemoryPool: %s %s : accepted %s (poolsz %"PRIszu")\n", - pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str(), - tx.GetHash().ToString().c_str(), + pfrom->addr.ToString(), pfrom->cleanSubVer, + tx.GetHash().ToString(), mempool.mapTx.size()); // Recursively process any orphan transactions that depended on this one @@ -3513,7 +3643,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) { - LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString().c_str()); + LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString()); RelayTransaction(orphanTx, orphanHash); mapAlreadyAskedFor.erase(CInv(MSG_TX, orphanHash)); vWorkQueue.push_back(orphanHash); @@ -3523,7 +3653,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { // invalid or too-little-fee orphan vEraseQueue.push_back(orphanHash); - LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString().c_str()); + LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString()); } mempool.check(pcoinsTip); } @@ -3544,9 +3674,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) int nDoS = 0; if (state.IsInvalid(nDoS)) { - LogPrint("mempool", "%s from %s %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString().c_str(), - pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str(), - state.GetRejectReason().c_str()); + LogPrint("mempool", "%s from %s %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), + pfrom->addr.ToString(), pfrom->cleanSubVer, + state.GetRejectReason()); pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), state.GetRejectReason(), inv.hash); if (nDoS > 0) @@ -3560,25 +3690,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CBlock block; vRecv >> block; - LogPrint("net", "received block %s\n", block.GetHash().ToString().c_str()); + LogPrint("net", "received block %s\n", block.GetHash().ToString()); // block.print(); CInv inv(MSG_BLOCK, block.GetHash()); 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); } @@ -3682,9 +3805,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (!(sProblem.empty())) { LogPrint("net", "pong %s %s: %s, %"PRIx64" expected, %"PRIx64" received, %"PRIszu" bytes\n", - pfrom->addr.ToString().c_str(), - pfrom->cleanSubVer.c_str(), - sProblem.c_str(), + pfrom->addr.ToString(), + pfrom->cleanSubVer, + sProblem, pfrom->nPingNonceSent, nonce, nAvail); @@ -3793,7 +3916,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // Truncate to reasonable length and sanitize before printing: string s = ss.str(); if (s.size() > 111) s.erase(111, string::npos); - LogPrint("net", "Reject %s\n", SanitizeString(s).c_str()); + LogPrint("net", "Reject %s\n", SanitizeString(s)); } } @@ -3866,7 +3989,7 @@ bool ProcessMessages(CNode* pfrom) CMessageHeader& hdr = msg.hdr; if (!hdr.IsValid()) { - LogPrintf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str()); + LogPrintf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand()); continue; } string strCommand = hdr.GetCommand(); @@ -3882,7 +4005,7 @@ bool ProcessMessages(CNode* pfrom) if (nChecksum != hdr.nChecksum) { LogPrintf("ProcessMessages(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", - strCommand.c_str(), nMessageSize, nChecksum, hdr.nChecksum); + strCommand, nMessageSize, nChecksum, hdr.nChecksum); continue; } @@ -3899,12 +4022,12 @@ bool ProcessMessages(CNode* pfrom) if (strstr(e.what(), "end of data")) { // Allow exceptions from under-length message on vRecv - LogPrintf("ProcessMessages(%s, %u bytes) : Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand.c_str(), nMessageSize, e.what()); + LogPrintf("ProcessMessages(%s, %u bytes) : Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand, nMessageSize, e.what()); } else if (strstr(e.what(), "size too large")) { // Allow exceptions from over-long size - LogPrintf("ProcessMessages(%s, %u bytes) : Exception '%s' caught\n", strCommand.c_str(), nMessageSize, e.what()); + LogPrintf("ProcessMessages(%s, %u bytes) : Exception '%s' caught\n", strCommand, nMessageSize, e.what()); } else { @@ -3921,7 +4044,7 @@ bool ProcessMessages(CNode* pfrom) } if (!fRet) - LogPrintf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand.c_str(), nMessageSize); + LogPrintf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand, nMessageSize); break; } @@ -4025,16 +4148,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().c_str()); + 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; @@ -4109,7 +4237,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) if (!AlreadyHave(inv)) { if (fDebug) - LogPrint("net", "sending getdata: %s\n", inv.ToString().c_str()); + LogPrint("net", "sending getdata: %s\n", inv.ToString()); vGetData.push_back(inv); if (vGetData.size() >= 1000) { |