aboutsummaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp1023
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();