diff options
Diffstat (limited to 'src/main.cpp')
-rw-r--r-- | src/main.cpp | 1023 |
1 files changed, 616 insertions, 407 deletions
diff --git a/src/main.cpp b/src/main.cpp index 9e5ee84f59..53b99101d9 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> @@ -27,6 +26,10 @@ using namespace std; using namespace boost; +#if defined(NDEBUG) +# error "Bitcoin cannot be compiled without assertions." +#endif + // // Global state // @@ -37,6 +40,7 @@ CTxMemPool mempool; map<uint256, CBlockIndex*> mapBlockIndex; CChain chainActive; +CChain chainMostWork; int64_t nTimeBestReceived = 0; int nScriptCheckThreads = 0; bool fImporting = false; @@ -52,8 +56,13 @@ int64_t CTransaction::nMinRelayTxFee = 1000; static CMedianFilter<int> cPeerBlockCounts(8, 0); // Amount of blocks that other nodes claim to have -map<uint256, CBlock*> mapOrphanBlocks; -multimap<uint256, CBlock*> mapOrphanBlocksByPrev; +struct COrphanBlock { + uint256 hashBlock; + uint256 hashPrev; + vector<unsigned char> vchBlock; +}; +map<uint256, COrphanBlock*> mapOrphanBlocks; +multimap<uint256, COrphanBlock*> mapOrphanBlocksByPrev; map<uint256, CTransaction> mapOrphanTransactions; map<uint256, set<uint256> > mapOrphanTransactionsByPrev; @@ -63,21 +72,26 @@ CScript COINBASE_FLAGS; const string strMessageMagic = "Bitcoin Signed Message:\n"; -// Settings -int64_t nTransactionFee = 0; - // Internal stuff 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; } }; @@ -87,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; } ////////////////////////////////////////////////////////////////////////////// @@ -149,17 +173,79 @@ void SyncWithWallets(const uint256 &hash, const CTransaction &tx, const CBlock * // Registration of network node signals. // -int static GetHeight() +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; + fShouldBan = false; + } +}; + +// Map maintaining per-node state. Requires cs_main. +map<NodeId, CNodeState> mapNodeState; + +// Requires cs_main. +CNodeState *State(NodeId pnode) { + map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode); + if (it == mapNodeState.end()) + return NULL; + return &it->second; +} + +int GetHeight() { LOCK(cs_main); return chainActive.Height(); } +void InitializeNode(NodeId nodeid, const CNode *pnode) { + LOCK(cs_main); + CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second; + state.name = pnode->addrName; +} + +void FinalizeNode(NodeId nodeid) { + LOCK(cs_main); + mapNodeState.erase(nodeid); +} +} + +bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { + LOCK(cs_main); + CNodeState *state = State(nodeid); + if (state == NULL) + return false; + stats.nMisbehavior = state->nMisbehavior; + return true; +} + void RegisterNodeSignals(CNodeSignals& nodeSignals) { nodeSignals.GetHeight.connect(&GetHeight); nodeSignals.ProcessMessages.connect(&ProcessMessages); nodeSignals.SendMessages.connect(&SendMessages); + nodeSignals.InitializeNode.connect(&InitializeNode); + nodeSignals.FinalizeNode.connect(&FinalizeNode); } void UnregisterNodeSignals(CNodeSignals& nodeSignals) @@ -167,6 +253,8 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals) nodeSignals.GetHeight.disconnect(&GetHeight); nodeSignals.ProcessMessages.disconnect(&ProcessMessages); nodeSignals.SendMessages.disconnect(&SendMessages); + nodeSignals.InitializeNode.disconnect(&InitializeNode); + nodeSignals.FinalizeNode.disconnect(&FinalizeNode); } ////////////////////////////////////////////////////////////////////////////// @@ -252,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; } @@ -260,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; } @@ -308,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; } @@ -336,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; @@ -355,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; } @@ -379,21 +488,6 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) return true; } -/** Amount of bitcoins spent by the transaction. - @return sum of all outputs (note: does not include fees) - */ -int64_t GetValueOut(const CTransaction& tx) -{ - int64_t nValueOut = 0; - BOOST_FOREACH(const CTxOut& txout, tx.vout) - { - nValueOut += txout.nValue; - if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) - throw std::runtime_error("GetValueOut() : value out of range"); - } - return nValueOut; -} - // // Check transaction inputs, and make sure any // pay-to-script-hash transactions are evaluating IsStandard scripts @@ -546,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; @@ -561,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 @@ -577,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); } @@ -585,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; @@ -651,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? @@ -660,7 +754,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return false; // Check for conflicts with in-memory transactions - CTransaction* ptxOld = NULL; { LOCK(pool.cs); // protect pool.mapNextTx for (unsigned int i = 0; i < tx.vin.size(); i++) @@ -670,22 +763,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa { // Disable replacement feature for now return false; - - // Allow replacing with a newer version of the same transaction - if (i != 0) - return false; - ptxOld = pool.mapNextTx[outpoint].ptx; - if (IsFinalTx(*ptxOld)) - return false; - if (!tx.IsNewerThan(*ptxOld)) - return false; - for (unsigned int i = 0; i < tx.vin.size(); i++) - { - COutPoint outpoint = tx.vin[i].prevout; - if (!pool.mapNextTx.count(outpoint) || pool.mapNextTx[outpoint].ptx != ptxOld) - return false; - } - break; } } } @@ -717,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(); @@ -734,14 +811,19 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // you should add code here to check that the transaction does a // reasonable number of ECDSA signature verifications. - int64_t nFees = view.GetValueIn(tx)-GetValueOut(tx); - unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + int64_t nValueIn = view.GetValueIn(tx); + int64_t nValueOut = tx.GetValueOut(); + int64_t nFees = nValueIn-nValueOut; + double dPriority = view.GetPriority(tx, chainActive.Height()); + + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height()); + unsigned int nSize = entry.GetTxSize(); // Don't accept it if it can't get into a block 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 @@ -770,41 +852,26 @@ 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()); - } - } - - // Store transaction in memory - { - if (ptxOld) - { - LogPrint("mempool", "AcceptToMemoryPool: : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); - pool.remove(*ptxOld); + return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString()); } - pool.addUnchecked(hash, tx); + // Store transaction in memory + pool.addUnchecked(hash, entry); } - ///// are we sure this is ok when loading transactions or restoring block txes - // If updated, erase old tx from wallet - if (ptxOld) - g_signals.EraseTransaction(ptxOld->GetHash()); g_signals.SyncTransaction(hash, tx, NULL); - LogPrint("mempool", "AcceptToMemoryPool: : accepted %s (poolsz %"PRIszu")\n", - hash.ToString().c_str(), - pool.mapTx.size()); return true; } -int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const +int CMerkleTx::GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const { if (hashBlock == 0 || nIndex == -1) return 0; @@ -829,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 { @@ -868,11 +943,11 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock fseek(file, postx.nTxOffset, SEEK_CUR); file >> txOut; } catch (std::exception &e) { - return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); + return error("%s : Deserialize or I/O error - %s", __PRETTY_FUNCTION__, e.what()); } hashBlock = header.GetHash(); if (txOut.GetHash() != hash) - return error("%s() : txid mismatch", __PRETTY_FUNCTION__); + return error("%s : txid mismatch", __PRETTY_FUNCTION__); return true; } } @@ -921,7 +996,7 @@ bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos) // Open history file to append CAutoFile fileout = CAutoFile(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); if (!fileout) - return error("WriteBlockToDisk() : OpenBlockFile failed"); + return error("WriteBlockToDisk : OpenBlockFile failed"); // Write index header unsigned int nSize = fileout.GetSerializeSize(block); @@ -930,7 +1005,7 @@ bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos) // Write block long fileOutPos = ftell(fileout); if (fileOutPos < 0) - return error("WriteBlockToDisk() : ftell failed"); + return error("WriteBlockToDisk : ftell failed"); pos.nPos = (unsigned int)fileOutPos; fileout << block; @@ -949,19 +1024,19 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos) // Open history file to read CAutoFile filein = CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); if (!filein) - return error("ReadBlockFromDisk(CBlock&, CDiskBlockPos&) : OpenBlockFile failed"); + return error("ReadBlockFromDisk : OpenBlockFile failed"); // Read block try { filein >> block; } catch (std::exception &e) { - return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); + return error("%s : Deserialize or I/O error - %s", __PRETTY_FUNCTION__, e.what()); } // Check the header if (!CheckProofOfWork(block.GetHash(), block.nBits)) - return error("ReadBlockFromDisk(CBlock&, CDiskBlockPos&) : errors in block header"); + return error("ReadBlockFromDisk : Errors in block header"); return true; } @@ -975,12 +1050,44 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) return true; } -uint256 static GetOrphanRoot(const CBlockHeader* pblock) +uint256 static GetOrphanRoot(const uint256& hash) { + map<uint256, COrphanBlock*>::iterator it = mapOrphanBlocks.find(hash); + if (it == mapOrphanBlocks.end()) + return hash; + // Work back to the first block in the orphan chain - while (mapOrphanBlocks.count(pblock->hashPrevBlock)) - pblock = mapOrphanBlocks[pblock->hashPrevBlock]; - return pblock->GetHash(); + do { + map<uint256, COrphanBlock*>::iterator it2 = mapOrphanBlocks.find(it->second->hashPrev); + if (it2 == mapOrphanBlocks.end()) + return it->first; + it = it2; + } 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) @@ -1079,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(); } @@ -1154,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 @@ -1203,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) @@ -1215,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) @@ -1308,24 +1389,27 @@ void UpdateTime(CBlockHeader& block, const CBlockIndex* pindexPrev) void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) { + bool ret; // mark inputs spent if (!tx.IsCoinBase()) { BOOST_FOREACH(const CTxIn &txin, tx.vin) { CCoins &coins = inputs.GetCoins(txin.prevout.hash); CTxInUndo undo; - assert(coins.Spend(txin.prevout, undo)); + ret = coins.Spend(txin.prevout, undo); + assert(ret); txundo.vprevout.push_back(undo); } } // add outputs - assert(inputs.SetCoins(txhash, CCoins(tx, nHeight))); + ret = inputs.SetCoins(txhash, CCoins(tx, nHeight)); + assert(ret); } 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; } @@ -1344,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. @@ -1362,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 < GetValueOut(tx)) - return state.DoS(100, error("CheckInputs() : %s value in < value out", tx.GetHash().ToString().c_str()), - REJECT_INVALID, "in < out"); + if (nValueIn < tx.GetValueOut()) + 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 - GetValueOut(tx); + 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. @@ -1582,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"); } } @@ -1612,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) { @@ -1628,10 +1712,10 @@ 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)-GetValueOut(tx); + nFees += view.GetValueIn(tx)-tx.GetValueOut(); std::vector<CScriptCheck> vChecks; if (!CheckInputs(tx, state, view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL)) @@ -1651,11 +1735,11 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C if (fBenchmark) LogPrintf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)block.vtx.size(), 0.001 * nTime, 0.001 * nTime / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1)); - if (GetValueOut(block.vtx[0]) > GetBlockValue(pindex->nHeight, nFees)) + if (block.vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRId64" vs limit=%"PRId64")", - GetValueOut(block.vtx[0]), GetBlockValue(pindex->nHeight, nFees)), - REJECT_INVALID, "coinbase too large"); + block.vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees)), + REJECT_INVALID, "bad-cb-amount"); if (!control.Wait()) return state.DoS(100, false); @@ -1693,7 +1777,9 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C return state.Abort(_("Failed to write transaction index")); // add this block to the view's block chain - assert(view.SetBestBlock(pindex->GetBlockHash())); + bool ret; + ret = view.SetBestBlock(pindex->GetBlockHash()); + assert(ret); // Watch for transactions paying to me for (unsigned int i = 0; i < block.vtx.size(); i++) @@ -1702,145 +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(); - assert(view.Flush()); - 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: @@ -1855,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); @@ -1904,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()) @@ -1941,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(); @@ -1968,7 +2118,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd } } else - return state.Error(); + return state.Error("out of disk space"); } } @@ -2014,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; @@ -2029,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) @@ -2068,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) @@ -2077,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; } @@ -2092,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; @@ -2100,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) { @@ -2132,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 @@ -2146,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"); } } } @@ -2218,16 +2373,21 @@ void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd) bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) { + AssertLockHeld(cs_main); + // 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))) @@ -2237,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); @@ -2246,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"); } } @@ -2254,16 +2414,24 @@ 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) { - CBlock* pblock2 = new CBlock(*pblock); + PruneOrphanBlocks(); + COrphanBlock* pblock2 = new COrphanBlock(); + { + CDataStream ss(SER_DISK, CLIENT_VERSION); + ss << *pblock; + pblock2->vchBlock = std::vector<unsigned char>(ss.begin(), ss.end()); + } + pblock2->hashBlock = hash; + pblock2->hashPrev = pblock->hashPrevBlock; mapOrphanBlocks.insert(make_pair(hash, pblock2)); - mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2)); + mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrev, pblock2)); // Ask this guy to fill in what we're missing - PushGetBlocks(pfrom, chainActive.Tip(), GetOrphanRoot(pblock2)); + PushGetBlocks(pfrom, chainActive.Tip(), GetOrphanRoot(hash)); } return true; } @@ -2278,17 +2446,22 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl for (unsigned int i = 0; i < vWorkQueue.size(); i++) { uint256 hashPrev = vWorkQueue[i]; - for (multimap<uint256, CBlock*>::iterator mi = mapOrphanBlocksByPrev.lower_bound(hashPrev); + for (multimap<uint256, COrphanBlock*>::iterator mi = mapOrphanBlocksByPrev.lower_bound(hashPrev); mi != mapOrphanBlocksByPrev.upper_bound(hashPrev); ++mi) { - CBlock* pblockOrphan = (*mi).second; + CBlock block; + { + CDataStream ss(mi->second->vchBlock, SER_DISK, CLIENT_VERSION); + ss >> block; + } + block.BuildMerkleTree(); // Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan resolution (that is, feeding people an invalid block based on LegitBlockX in order to get anyone relaying LegitBlockX banned) CValidationState stateDummy; - if (AcceptBlock(*pblockOrphan, stateDummy)) - vWorkQueue.push_back(pblockOrphan->GetHash()); - mapOrphanBlocks.erase(pblockOrphan->GetHash()); - delete pblockOrphan; + if (AcceptBlock(block, stateDummy)) + vWorkQueue.push_back(mi->second->hashBlock); + mapOrphanBlocks.erase(mi->second->hashBlock); + delete mi->second; } mapOrphanBlocksByPrev.erase(hashPrev); } @@ -2459,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; @@ -2486,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; } @@ -2558,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; @@ -2575,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; } @@ -2606,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; @@ -2643,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()); } } @@ -2753,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 @@ -2833,7 +3006,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) break; } } catch (std::exception &e) { - LogPrintf("%s() : Deserialize or I/O error caught during load\n", __PRETTY_FUNCTION__); + LogPrintf("%s : Deserialize or I/O error - %s", __PRETTY_FUNCTION__, e.what()); } } fclose(fileIn); @@ -2944,7 +3117,6 @@ bool static AlreadyHave(const CInv& inv) } - void static ProcessGetData(CNode* pfrom) { std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin(); @@ -2965,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) @@ -3060,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"); @@ -3077,7 +3267,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (pfrom->nVersion != 0) { pfrom->PushMessage("reject", strCommand, REJECT_DUPLICATE, string("Duplicate version message")); - pfrom->Misbehaving(1); + Misbehaving(pfrom->GetId(), 1); return false; } @@ -3089,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; @@ -3100,8 +3290,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->nVersion = 300; if (!vRecv.empty()) vRecv >> addrFrom >> nNonce; - if (!vRecv.empty()) + if (!vRecv.empty()) { vRecv >> pfrom->strSubVer; + pfrom->cleanSubVer = SanitizeString(pfrom->strSubVer); + } if (!vRecv.empty()) vRecv >> pfrom->nStartingHeight; if (!vRecv.empty()) @@ -3118,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; } @@ -3168,7 +3360,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fSuccessfullyConnected = true; - LogPrintf("receive version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", 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); @@ -3180,7 +3372,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) else if (pfrom->nVersion == 0) { // Must have a version message before anything else - pfrom->Misbehaving(1); + Misbehaving(pfrom->GetId(), 1); return false; } @@ -3201,7 +3393,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) return true; if (vAddr.size() > 1000) { - pfrom->Misbehaving(20); + Misbehaving(pfrom->GetId(), 20); return error("message addr size() = %"PRIszu"", vAddr.size()); } @@ -3264,7 +3456,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { - pfrom->Misbehaving(20); + Misbehaving(pfrom->GetId(), 20); return error("message inv size() = %"PRIszu"", vInv.size()); } @@ -3287,20 +3479,20 @@ 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) pfrom->AskFor(inv); } else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) { - PushGetBlocks(pfrom, chainActive.Tip(), GetOrphanRoot(mapOrphanBlocks[inv.hash])); + PushGetBlocks(pfrom, chainActive.Tip(), GetOrphanRoot(inv.hash)); } else if (nInv == nLastBlock) { // In case we are on a very long side-chain, it is possible that we already have // the last block in an inv bundle sent in response to getblocks. Try to detect // 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 @@ -3315,7 +3507,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { - pfrom->Misbehaving(20); + Misbehaving(pfrom->GetId(), 20); return error("message getdata size() = %"PRIszu"", vInv.size()); } @@ -3323,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); @@ -3345,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())); @@ -3358,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; } @@ -3394,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()); @@ -3427,6 +3619,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vWorkQueue.push_back(inv.hash); vEraseQueue.push_back(inv.hash); + + LogPrint("mempool", "AcceptToMemoryPool: %s %s : accepted %s (poolsz %"PRIszu")\n", + pfrom->addr.ToString(), pfrom->cleanSubVer, + tx.GetHash().ToString(), + mempool.mapTx.size()); + // Recursively process any orphan transactions that depended on this one for (unsigned int i = 0; i < vWorkQueue.size(); i++) { @@ -3445,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); @@ -3455,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); } @@ -3475,11 +3673,14 @@ 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(), + pfrom->addr.ToString(), pfrom->cleanSubVer, + state.GetRejectReason()); pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), state.GetRejectReason(), inv.hash); if (nDoS > 0) - pfrom->Misbehaving(nDoS); + Misbehaving(pfrom->GetId(), nDoS); } } @@ -3489,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) - pfrom->Misbehaving(nDoS); - } + ProcessBlock(state, pfrom, &block); } @@ -3611,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->strSubVer.c_str(), - sProblem.c_str(), + pfrom->addr.ToString(), + pfrom->cleanSubVer, + sProblem, pfrom->nPingNonceSent, nonce, nAvail); @@ -3649,7 +3843,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // This isn't a Misbehaving(100) (immediate ban) because the // peer might be an older or different implementation with // a different signature key, etc. - pfrom->Misbehaving(10); + Misbehaving(pfrom->GetId(), 10); } } } @@ -3662,7 +3856,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (!filter.IsWithinSizeConstraints()) // There is no excuse for sending a too-large filter - pfrom->Misbehaving(100); + Misbehaving(pfrom->GetId(), 100); else { LOCK(pfrom->cs_filter); @@ -3683,13 +3877,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // and thus, the maximum size any matched object can have) in a filteradd message if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) { - pfrom->Misbehaving(100); + Misbehaving(pfrom->GetId(), 100); } else { LOCK(pfrom->cs_filter); if (pfrom->pfilter) pfrom->pfilter->insert(vData); else - pfrom->Misbehaving(100); + Misbehaving(pfrom->GetId(), 100); } } @@ -3722,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)); } } @@ -3795,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(); @@ -3811,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; } @@ -3828,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 { @@ -3850,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; } @@ -3954,6 +4148,21 @@ bool SendMessages(CNode* pto, bool fSendTrickle) if (!lockMain) return true; + 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.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; @@ -4028,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) { @@ -4062,7 +4271,7 @@ public: mapBlockIndex.clear(); // orphan blocks - std::map<uint256, CBlock*>::iterator it2 = mapOrphanBlocks.begin(); + std::map<uint256, COrphanBlock*>::iterator it2 = mapOrphanBlocks.begin(); for (; it2 != mapOrphanBlocks.end(); it2++) delete (*it2).second; mapOrphanBlocks.clear(); |