aboutsummaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp673
1 files changed, 382 insertions, 291 deletions
diff --git a/src/main.cpp b/src/main.cpp
index 82d52913a0..60e1db6647 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,6 +1,6 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin developers
-// Distributed under the MIT/X11 software license, see the accompanying
+// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "main.h"
@@ -33,9 +33,9 @@ using namespace std;
# error "Bitcoin cannot be compiled without assertions."
#endif
-//
-// Global state
-//
+/**
+ * Global state
+ */
CCriticalSection cs_main;
@@ -66,7 +66,7 @@ map<uint256, COrphanTx> mapOrphanTransactions;
map<uint256, set<uint256> > mapOrphanTransactionsByPrev;
void EraseOrphansFor(NodeId peer);
-// Constant stuff for coinbase transactions we create:
+/** Constant stuff for coinbase transactions we create: */
CScript COINBASE_FLAGS;
const string strMessageMagic = "Bitcoin Signed Message:\n";
@@ -97,39 +97,50 @@ namespace {
CBlockIndex *pindexBestInvalid;
- // The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS or better that are at least
- // as good as our current tip. Entries may be failed, though.
+ /**
+ * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS or better that are at least
+ * as good as our current tip. Entries may be failed, though.
+ */
set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
- // Number of nodes with fSyncStarted.
+ /** Number of nodes with fSyncStarted. */
int nSyncStarted = 0;
- // All pairs A->B, where A (or one if its ancestors) misses transactions, but B has transactions.
+ /** All pairs A->B, where A (or one if its ancestors) misses transactions, but B has transactions. */
multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked;
CCriticalSection cs_LastBlockFile;
std::vector<CBlockFileInfo> vinfoBlockFile;
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.
+ /**
+ * 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.
+ /** 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.
+ /**
+ * 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;
- // Blocks that are in flight, and that are in the queue to be downloaded.
- // Protected by cs_main.
+ /** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */
struct QueuedBlock {
uint256 hash;
- CBlockIndex *pindex; // Optional.
- int64_t nTime; // Time of "getdata" request in microseconds.
+ CBlockIndex *pindex; //! Optional.
+ int64_t nTime; //! Time of "getdata" request in microseconds.
};
map<uint256, pair<NodeId, list<QueuedBlock>::iterator> > mapBlocksInFlight;
- // Number of preferrable block download peers.
+ /** Number of preferable block download peers. */
int nPreferredDownload = 0;
+
+ /** Dirty block index entries. */
+ set<CBlockIndex*> setDirtyBlockIndex;
+
+ /** Dirty block file entries. */
+ set<int> setDirtyFileInfo;
} // anon namespace
//////////////////////////////////////////////////////////////////////////////
@@ -142,19 +153,19 @@ namespace {
namespace {
struct CMainSignals {
- // Notifies listeners of updated transaction data (transaction, and optionally the block it is found in.
+ /** Notifies listeners of updated transaction data (transaction, and optionally the block it is found in. */
boost::signals2::signal<void (const CTransaction &, const CBlock *)> SyncTransaction;
- // Notifies listeners of an erased transaction (currently disabled, requires transaction replacement).
+ /** Notifies listeners of an erased transaction (currently disabled, requires transaction replacement). */
boost::signals2::signal<void (const uint256 &)> EraseTransaction;
- // Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible).
+ /** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */
boost::signals2::signal<void (const uint256 &)> UpdatedTransaction;
- // Notifies listeners of a new active block chain.
+ /** Notifies listeners of a new active block chain. */
boost::signals2::signal<void (const CBlockLocator &)> SetBestChain;
- // Notifies listeners about an inventory item being seen on the network.
+ /** Notifies listeners about an inventory item being seen on the network. */
boost::signals2::signal<void (const uint256 &)> Inventory;
- // Tells listeners to broadcast their data.
+ /** Tells listeners to broadcast their data. */
boost::signals2::signal<void ()> Broadcast;
- // Notifies listeners of a block validation result
+ /** Notifies listeners of a block validation result */
boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
} g_signals;
@@ -207,32 +218,34 @@ struct CBlockReject {
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.
+/**
+ * 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.
+ //! Accumulated misbehaviour score for this peer.
int nMisbehavior;
- // Whether this peer should be disconnected and banned (unless whitelisted).
+ //! Whether this peer should be disconnected and banned (unless whitelisted).
bool fShouldBan;
- // String name of this peer (debugging/logging purposes).
+ //! String name of this peer (debugging/logging purposes).
std::string name;
- // List of asynchronously-determined block rejections to notify this peer about.
+ //! List of asynchronously-determined block rejections to notify this peer about.
std::vector<CBlockReject> rejects;
- // The best known block we know this peer has announced.
+ //! The best known block we know this peer has announced.
CBlockIndex *pindexBestKnownBlock;
- // The hash of the last unknown block this peer has announced.
+ //! The hash of the last unknown block this peer has announced.
uint256 hashLastUnknownBlock;
- // The last full block we both have.
+ //! The last full block we both have.
CBlockIndex *pindexLastCommonBlock;
- // Whether we've started headers synchronization with this peer.
+ //! Whether we've started headers synchronization with this peer.
bool fSyncStarted;
- // Since when we're stalling block download progress (in microseconds), or 0.
+ //! Since when we're stalling block download progress (in microseconds), or 0.
int64_t nStallingSince;
list<QueuedBlock> vBlocksInFlight;
int nBlocksInFlight;
- // Whether we consider this a preferred download peer.
+ //! Whether we consider this a preferred download peer.
bool fPreferredDownload;
CNodeState() {
@@ -248,7 +261,7 @@ struct CNodeState {
}
};
-// Map maintaining per-node state. Requires cs_main.
+/** Map maintaining per-node state. Requires cs_main. */
map<NodeId, CNodeState> mapNodeState;
// Requires cs_main.
@@ -702,15 +715,15 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
return true;
}
-//
-// Check transaction inputs to mitigate two
-// potential denial-of-service attacks:
-//
-// 1. scriptSigs with extra data stuffed into them,
-// not consumed by scriptPubKey (or P2SH script)
-// 2. P2SH scripts with a crazy number of expensive
-// CHECKSIG/CHECKMULTISIG operations
-//
+/**
+ * Check transaction inputs to mitigate two
+ * potential denial-of-service attacks:
+ *
+ * 1. scriptSigs with extra data stuffed into them,
+ * not consumed by scriptPubKey (or P2SH script)
+ * 2. P2SH scripts with a crazy number of expensive
+ * CHECKSIG/CHECKMULTISIG operations
+ */
bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
{
if (tx.IsCoinBase())
@@ -1048,7 +1061,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
return true;
}
-// Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock
+/** Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock */
bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow)
{
CBlockIndex *pindexSlow = NULL;
@@ -1137,11 +1150,6 @@ bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos)
pos.nPos = (unsigned int)fileOutPos;
fileout << block;
- // Flush stdio buffers and commit to disk before returning
- fflush(fileout.Get());
- if (!IsInitialBlockDownload())
- FileCommit(fileout.Get());
-
return true;
}
@@ -1180,7 +1188,7 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex)
CAmount GetBlockValue(int nHeight, const CAmount& nFees)
{
- int64_t nSubsidy = 50 * COIN;
+ CAmount nSubsidy = 50 * COIN;
int halvings = nHeight / Params().SubsidyHalvingInterval();
// Force block reward to zero when right shift is undefined.
@@ -1198,15 +1206,14 @@ bool IsInitialBlockDownload()
LOCK(cs_main);
if (fImporting || fReindex || chainActive.Height() < Checkpoints::GetTotalBlocksEstimate())
return true;
- static int64_t nLastUpdate;
- static CBlockIndex* pindexLastBest;
- if (chainActive.Tip() != pindexLastBest)
- {
- pindexLastBest = chainActive.Tip();
- nLastUpdate = GetTime();
- }
- return (GetTime() - nLastUpdate < 10 &&
- chainActive.Tip()->GetBlockTime() < GetTime() - 24 * 60 * 60);
+ static bool lockIBDState = false;
+ if (lockIBDState)
+ return false;
+ bool state = (chainActive.Height() < pindexBestHeader->nHeight - 24 * 6 ||
+ pindexBestHeader->GetBlockTime() < GetTime() - 24 * 60 * 60);
+ if (!state)
+ lockIBDState = true;
+ return state;
}
bool fLargeWorkForkFound = false;
@@ -1226,15 +1233,15 @@ void CheckForkWarningConditions()
if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->nHeight >= 72)
pindexBestForkTip = NULL;
- if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > chainActive.Tip()->nChainWork + (chainActive.Tip()->GetBlockWork() * 6)))
+ if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > chainActive.Tip()->nChainWork + (GetBlockProof(*chainActive.Tip()) * 6)))
{
- if (!fLargeWorkForkFound)
+ if (!fLargeWorkForkFound && pindexBestForkBase)
{
std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") +
pindexBestForkBase->phashBlock->ToString() + std::string("'");
CAlert::Notify(warning, true);
}
- if (pindexBestForkTip)
+ if (pindexBestForkTip && pindexBestForkBase)
{
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(),
@@ -1277,7 +1284,7 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip)
// We define it this way because it allows us to only store the highest fork tip (+ base) which meets
// the 7-block condition and from this always have the most-likely-to-cause-warning fork
if (pfork && (!pindexBestForkTip || (pindexBestForkTip && pindexNewForkTip->nHeight > pindexBestForkTip->nHeight)) &&
- pindexNewForkTip->nChainWork - pfork->nChainWork > (pfork->GetBlockWork() * 7) &&
+ pindexNewForkTip->nChainWork - pfork->nChainWork > (GetBlockProof(*pfork) * 7) &&
chainActive.Height() - pindexNewForkTip->nHeight < 72)
{
pindexBestForkTip = pindexNewForkTip;
@@ -1327,7 +1334,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state
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()};
+ CBlockReject reject = {state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash()};
State(it->second)->rejects.push_back(reject);
if (nDoS > 0)
Misbehaving(it->second, nDoS);
@@ -1335,7 +1342,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state
}
if (!state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
- pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex));
+ setDirtyBlockIndex.insert(pindex);
setBlockIndexCandidates.erase(pindex);
InvalidChainFound(pindex);
}
@@ -1357,10 +1364,11 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach
inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight);
}
-bool CScriptCheck::operator()() const {
+bool CScriptCheck::operator()() {
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
- if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingSignatureChecker(*ptxTo, nIn, cacheStore)))
- return error("CScriptCheck() : %s:%d VerifySignature failed", ptxTo->GetHash().ToString(), nIn);
+ if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingSignatureChecker(*ptxTo, nIn, cacheStore), &error)) {
+ return ::error("CScriptCheck() : %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error));
+ }
return true;
}
@@ -1448,7 +1456,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
CScriptCheck check(*coins, tx, i,
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore);
if (check())
- return state.Invalid(false, REJECT_NONSTANDARD, "non-mandatory-script-verify-flag");
+ return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
}
// Failures of other flags indicate a transaction that is
// invalid in new blocks, e.g. a invalid P2SH. We DoS ban
@@ -1457,7 +1465,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
// as to the correct behavior - we may want to continue
// peering with non-upgraded nodes even after a soft-fork
// super-majority vote has passed.
- return state.DoS(100,false, REJECT_INVALID, "mandatory-script-verify-flag-failed");
+ return state.DoS(100,false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError())));
}
}
}
@@ -1593,7 +1601,7 @@ static int64_t nTimeIndex = 0;
static int64_t nTimeCallbacks = 0;
static int64_t nTimeTotal = 0;
-bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck)
+bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck)
{
AssertLockHeld(cs_main);
// Check it again in case a previous version let a bad block in
@@ -1732,10 +1740,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
}
pindex->RaiseValidity(BLOCK_VALID_SCRIPTS);
-
- CDiskBlockIndex blockindex(pindex);
- if (!pblocktree->WriteBlockIndex(blockindex))
- return state.Abort("Failed to write block index");
+ setDirtyBlockIndex.insert(pindex);
}
if (fTxIndex)
@@ -1759,10 +1764,23 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
return true;
}
-// Update the on-disk chain state.
-bool static WriteChainState(CValidationState &state) {
+enum FlushStateMode {
+ FLUSH_STATE_IF_NEEDED,
+ FLUSH_STATE_PERIODIC,
+ FLUSH_STATE_ALWAYS
+};
+
+/**
+ * Update the on-disk chain state.
+ * The caches and indexes are flushed if either they're too large, forceWrite is set, or
+ * fast is not set and it's been a while since the last write.
+ */
+bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) {
+ LOCK(cs_main);
static int64_t nLastWrite = 0;
- if (pcoinsTip->GetCacheSize() > nCoinCacheSize || (!IsInitialBlockDownload() && GetTimeMicros() > nLastWrite + 600*1000000)) {
+ if ((mode == FLUSH_STATE_ALWAYS) ||
+ ((mode == FLUSH_STATE_PERIODIC || mode == FLUSH_STATE_IF_NEEDED) && pcoinsTip->GetCacheSize() > nCoinCacheSize) ||
+ (mode == FLUSH_STATE_PERIODIC && GetTimeMicros() > nLastWrite + DATABASE_WRITE_INTERVAL * 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
@@ -1770,16 +1788,45 @@ bool static WriteChainState(CValidationState &state) {
// overwrite one. Still, use a conservative safety factor of 2.
if (!CheckDiskSpace(100 * 2 * 2 * pcoinsTip->GetCacheSize()))
return state.Error("out of disk space");
+ // First make sure all block and undo data is flushed to disk.
FlushBlockFile();
+ // Then update all block file information (which may refer to block and undo files).
+ bool fileschanged = false;
+ for (set<int>::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) {
+ if (!pblocktree->WriteBlockFileInfo(*it, vinfoBlockFile[*it])) {
+ return state.Abort("Failed to write to block index");
+ }
+ fileschanged = true;
+ setDirtyFileInfo.erase(it++);
+ }
+ if (fileschanged && !pblocktree->WriteLastBlockFile(nLastBlockFile)) {
+ return state.Abort("Failed to write to block index");
+ }
+ for (set<CBlockIndex*>::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) {
+ if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(*it))) {
+ return state.Abort("Failed to write to block index");
+ }
+ setDirtyBlockIndex.erase(it++);
+ }
pblocktree->Sync();
+ // Finally flush the chainstate (which may refer to block index entries).
if (!pcoinsTip->Flush())
return state.Abort("Failed to write to coin database");
+ // Update best block in wallet (so we can detect restored wallets).
+ if (mode != FLUSH_STATE_IF_NEEDED) {
+ g_signals.SetBestChain(chainActive.GetLocator());
+ }
nLastWrite = GetTimeMicros();
}
return true;
}
-// Update chainActive and related internal data structures.
+void FlushStateToDisk() {
+ CValidationState state;
+ FlushStateToDisk(state, FLUSH_STATE_ALWAYS);
+}
+
+/** Update chainActive and related internal data structures. */
void static UpdateTip(CBlockIndex *pindexNew) {
chainActive.SetTip(pindexNew);
@@ -1818,7 +1865,7 @@ void static UpdateTip(CBlockIndex *pindexNew) {
}
}
-// Disconnect chainActive's tip.
+/** Disconnect chainActive's tip. */
bool static DisconnectTip(CValidationState &state) {
CBlockIndex *pindexDelete = chainActive.Tip();
assert(pindexDelete);
@@ -1837,7 +1884,7 @@ bool static DisconnectTip(CValidationState &state) {
}
LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
// Write the chain state to disk, if necessary.
- if (!WriteChainState(state))
+ if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED))
return false;
// Resurrect mempool transactions from the disconnected block.
BOOST_FOREACH(const CTransaction &tx, block.vtx) {
@@ -1865,8 +1912,10 @@ static int64_t nTimeFlush = 0;
static int64_t nTimeChainState = 0;
static int64_t nTimePostConnect = 0;
-// Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
-// corresponding to pindexNew, to bypass loading it again from disk.
+/**
+ * Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
+ * corresponding to pindexNew, to bypass loading it again from disk.
+ */
bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *pblock) {
assert(pindexNew->pprev == chainActive.Tip());
mempool.check(pcoinsTip);
@@ -1900,7 +1949,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3;
LogPrint("bench", " - Flush: %.2fms [%.2fs]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001);
// Write the chain state to disk, if necessary.
- if (!WriteChainState(state))
+ if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED))
return false;
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001);
@@ -1919,10 +1968,6 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
BOOST_FOREACH(const CTransaction &tx, pblock->vtx) {
SyncWithWallets(tx, pblock);
}
- // Update best block in wallet (so we can detect restored wallets)
- // Emit this signal after the SyncWithWallets signals as the wallet relies on that everything up to this point has been synced
- if ((chainActive.Height() % 20160) == 0 || ((chainActive.Height() % 144) == 0 && !IsInitialBlockDownload()))
- g_signals.SetBestChain(chainActive.GetLocator());
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001);
@@ -1930,8 +1975,10 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
return true;
}
-// Return the tip of the chain with the most work in it, that isn't
-// known to be invalid (it's however far from certain to be valid).
+/**
+ * Return the tip of the chain with the most work in it, that isn't
+ * known to be invalid (it's however far from certain to be valid).
+ */
static CBlockIndex* FindMostWorkChain() {
do {
CBlockIndex *pindexNew = NULL;
@@ -1972,8 +2019,22 @@ static CBlockIndex* FindMostWorkChain() {
} while(true);
}
-// Try to make some progress towards making pindexMostWork the active block.
-// pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork.
+/** Delete all entries in setBlockIndexCandidates that are worse than the current tip. */
+static void PruneBlockIndexCandidates() {
+ // Note that we can't delete the current block itself, as we may need to return to it later in case a
+ // reorganization to a better block fails.
+ std::set<CBlockIndex*, CBlockIndexWorkComparator>::iterator it = setBlockIndexCandidates.begin();
+ while (it != setBlockIndexCandidates.end() && setBlockIndexCandidates.value_comp()(*it, chainActive.Tip())) {
+ setBlockIndexCandidates.erase(it++);
+ }
+ // Either the current tip or a successor of it we're working towards is left in setBlockIndexCandidates.
+ assert(!setBlockIndexCandidates.empty());
+}
+
+/**
+ * Try to make some progress towards making pindexMostWork the active block.
+ * pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork.
+ */
static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMostWork, CBlock *pblock) {
AssertLockHeld(cs_main);
bool fInvalidFound = false;
@@ -2019,15 +2080,7 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo
return false;
}
} else {
- // Delete all entries in setBlockIndexCandidates that are worse than our new current block.
- // Note that we can't delete the current block itself, as we may need to return to it later in case a
- // reorganization to a better block fails.
- std::set<CBlockIndex*, CBlockIndexWorkComparator>::iterator it = setBlockIndexCandidates.begin();
- while (setBlockIndexCandidates.value_comp()(*it, chainActive.Tip())) {
- setBlockIndexCandidates.erase(it++);
- }
- // Either the current tip or a successor of it we're working towards is left in setBlockIndexCandidates.
- assert(!setBlockIndexCandidates.empty());
+ PruneBlockIndexCandidates();
if (!pindexOldTip || chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) {
// We're in a better position than we were. Return temporarily to release the lock.
fContinue = false;
@@ -2043,15 +2096,14 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo
else
CheckForkWarningConditions();
- if (!pblocktree->Flush())
- return state.Abort("Failed to sync block index");
-
return true;
}
-// Make the best chain active, in multiple steps. The result is either failure
-// or an activated best chain. pblock is either NULL or a pointer to a block
-// that is already loaded (to avoid loading it again from disk).
+/**
+ * Make the best chain active, in multiple steps. The result is either failure
+ * or an activated best chain. pblock is either NULL or a pointer to a block
+ * that is already loaded (to avoid loading it again from disk).
+ */
bool ActivateBestChain(CValidationState &state, CBlock *pblock) {
CBlockIndex *pindexNewTip = NULL;
CBlockIndex *pindexMostWork = NULL;
@@ -2086,11 +2138,83 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) {
if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate))
pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip));
}
-
+ // Notify external listeners about the new tip.
uiInterface.NotifyBlockTip(hashNewTip);
}
} while(pindexMostWork != chainActive.Tip());
+ // Write changes periodically to disk, after relay.
+ if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) {
+ AssertLockHeld(cs_main);
+
+ // Mark the block itself as invalid.
+ pindex->nStatus |= BLOCK_FAILED_VALID;
+ setDirtyBlockIndex.insert(pindex);
+ setBlockIndexCandidates.erase(pindex);
+
+ while (chainActive.Contains(pindex)) {
+ CBlockIndex *pindexWalk = chainActive.Tip();
+ pindexWalk->nStatus |= BLOCK_FAILED_CHILD;
+ setDirtyBlockIndex.insert(pindexWalk);
+ setBlockIndexCandidates.erase(pindexWalk);
+ // ActivateBestChain considers blocks already in chainActive
+ // unconditionally valid already, so force disconnect away from it.
+ if (!DisconnectTip(state)) {
+ return false;
+ }
+ }
+
+ // The resulting new best tip may not be in setBlockIndexCandidates anymore, so
+ // add them again.
+ BlockMap::iterator it = mapBlockIndex.begin();
+ while (it != mapBlockIndex.end()) {
+ if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) {
+ setBlockIndexCandidates.insert(pindex);
+ }
+ it++;
+ }
+
+ InvalidChainFound(pindex);
+ return true;
+}
+
+bool ReconsiderBlock(CValidationState& state, CBlockIndex *pindex) {
+ AssertLockHeld(cs_main);
+
+ int nHeight = pindex->nHeight;
+
+ // Remove the invalidity flag from this block and all its descendants.
+ BlockMap::iterator it = mapBlockIndex.begin();
+ while (it != mapBlockIndex.end()) {
+ if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) {
+ it->second->nStatus &= ~BLOCK_FAILED_MASK;
+ setDirtyBlockIndex.insert(it->second);
+ if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) {
+ setBlockIndexCandidates.insert(it->second);
+ }
+ if (it->second == pindexBestInvalid) {
+ // Reset invalid block marker if it was pointing to one of those.
+ pindexBestInvalid = NULL;
+ }
+ }
+ it++;
+ }
+
+ // Remove the invalidity flag from all ancestors too.
+ while (pindex != NULL) {
+ if (pindex->nStatus & BLOCK_FAILED_MASK) {
+ pindex->nStatus &= ~BLOCK_FAILED_MASK;
+ setDirtyBlockIndex.insert(pindex);
+ }
+ pindex = pindex->pprev;
+ }
return true;
}
@@ -2118,18 +2242,17 @@ CBlockIndex* AddToBlockIndex(const CBlockHeader& block)
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
pindexNew->BuildSkip();
}
- pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + pindexNew->GetBlockWork();
+ pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew);
pindexNew->RaiseValidity(BLOCK_VALID_TREE);
if (pindexBestHeader == NULL || pindexBestHeader->nChainWork < pindexNew->nChainWork)
pindexBestHeader = pindexNew;
- // Ok if it fails, we'll download the header again next time.
- pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew));
+ setDirtyBlockIndex.insert(pindexNew);
return pindexNew;
}
-// Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS).
+/** Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). */
bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos)
{
pindexNew->nTx = block.vtx.size();
@@ -2143,6 +2266,7 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
LOCK(cs_nBlockSequenceId);
pindexNew->nSequenceId = nBlockSequenceId++;
}
+ setDirtyBlockIndex.insert(pindexNew);
if (pindexNew->pprev == NULL || pindexNew->pprev->nChainTx) {
// If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS.
@@ -2162,15 +2286,11 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
range.first++;
mapBlocksUnlinked.erase(it);
}
- if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)))
- return state.Abort("Failed to write block index");
}
} else {
if (pindexNew->pprev && pindexNew->pprev->IsValid(BLOCK_VALID_TREE)) {
mapBlocksUnlinked.insert(std::make_pair(pindexNew->pprev, pindexNew));
}
- if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew)))
- return state.Abort("Failed to write block index");
}
return true;
@@ -2178,8 +2298,6 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false)
{
- bool fUpdatedLast = false;
-
LOCK(cs_LastBlockFile);
unsigned int nFile = fKnown ? pos.nFile : nLastBlockFile;
@@ -2195,7 +2313,6 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd
if (vinfoBlockFile.size() <= nFile) {
vinfoBlockFile.resize(nFile + 1);
}
- fUpdatedLast = true;
}
pos.nFile = nFile;
pos.nPos = vinfoBlockFile[nFile].nSize;
@@ -2222,11 +2339,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd
}
}
- if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, vinfoBlockFile[nFile]))
- return state.Abort("Failed to write file info");
- if (fUpdatedLast)
- pblocktree->WriteLastBlockFile(nLastBlockFile);
-
+ setDirtyFileInfo.insert(nFile);
return true;
}
@@ -2239,9 +2352,7 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne
unsigned int nNewSize;
pos.nPos = vinfoBlockFile[nFile].nUndoSize;
nNewSize = vinfoBlockFile[nFile].nUndoSize += nAddSize;
- if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, vinfoBlockFile[nLastBlockFile])) {
- return state.Abort("Failed to write block info");
- }
+ setDirtyFileInfo.insert(nFile);
unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE;
unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE;
@@ -2280,6 +2391,8 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
{
// These are checks that are independent of context.
+ // Check that the header is valid (particularly PoW). This is mostly
+ // redundant with the call in AcceptBlockHeader.
if (!CheckBlockHeader(block, state, fCheckPOW))
return false;
@@ -2334,6 +2447,73 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
return true;
}
+bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex * const pindexPrev)
+{
+ uint256 hash = block.GetHash();
+ if (hash == Params().HashGenesisBlock())
+ return true;
+
+ assert(pindexPrev);
+
+ int nHeight = pindexPrev->nHeight+1;
+
+ // Check proof of work
+ if ((!Params().SkipProofOfWorkCheck()) &&
+ (block.nBits != GetNextWorkRequired(pindexPrev, &block)))
+ return state.DoS(100, error("%s : incorrect proof of work", __func__),
+ REJECT_INVALID, "bad-diffbits");
+
+ // Check timestamp against prev
+ if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast())
+ return state.Invalid(error("%s : block's timestamp is too early", __func__),
+ REJECT_INVALID, "time-too-old");
+
+ // Check that the block chain matches the known block chain up to a checkpoint
+ if (!Checkpoints::CheckBlock(nHeight, hash))
+ return state.DoS(100, error("%s : rejected by checkpoint lock-in at %d", __func__, nHeight),
+ REJECT_CHECKPOINT, "checkpoint mismatch");
+
+ // Don't accept any forks from the main chain prior to last checkpoint
+ CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint();
+ if (pcheckpoint && nHeight < pcheckpoint->nHeight)
+ return state.DoS(100, error("%s : forked chain older than last checkpoint (height %d)", __func__, nHeight));
+
+ // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded:
+ if (block.nVersion < 2 &&
+ CBlockIndex::IsSuperMajority(2, pindexPrev, Params().RejectBlockOutdatedMajority()))
+ {
+ return state.Invalid(error("%s : rejected nVersion=1 block", __func__),
+ REJECT_OBSOLETE, "bad-version");
+ }
+
+ return true;
+}
+
+bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex * const pindexPrev)
+{
+ const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1;
+
+ // Check that all transactions are finalized
+ BOOST_FOREACH(const CTransaction& tx, block.vtx)
+ if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) {
+ return state.DoS(10, error("%s : contains a non-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal");
+ }
+
+ // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height
+ // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet):
+ if (block.nVersion >= 2 &&
+ CBlockIndex::IsSuperMajority(2, pindexPrev, Params().EnforceBlockUpgradeMajority()))
+ {
+ CScript expect = CScript() << nHeight;
+ 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("%s : block height mismatch in coinbase", __func__), REJECT_INVALID, "bad-cb-height");
+ }
+ }
+
+ return true;
+}
+
bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex** ppindex)
{
AssertLockHeld(cs_main);
@@ -2351,46 +2531,21 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc
return true;
}
+ if (!CheckBlockHeader(block, state))
+ return false;
+
// Get prev block index
CBlockIndex* pindexPrev = NULL;
- int nHeight = 0;
if (hash != Params().HashGenesisBlock()) {
BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock);
if (mi == mapBlockIndex.end())
return state.DoS(10, error("%s : prev block not found", __func__), 0, "bad-prevblk");
pindexPrev = (*mi).second;
- nHeight = pindexPrev->nHeight+1;
-
- // Check proof of work
- if ((!Params().SkipProofOfWorkCheck()) &&
- (block.nBits != GetNextWorkRequired(pindexPrev, &block)))
- return state.DoS(100, error("%s : incorrect proof of work", __func__),
- REJECT_INVALID, "bad-diffbits");
-
- // Check timestamp against prev
- if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast())
- return state.Invalid(error("%s : block's timestamp is too early", __func__),
- REJECT_INVALID, "time-too-old");
-
- // Check that the block chain matches the known block chain up to a checkpoint
- if (!Checkpoints::CheckBlock(nHeight, hash))
- return state.DoS(100, error("%s : rejected by checkpoint lock-in at %d", __func__, nHeight),
- REJECT_CHECKPOINT, "checkpoint mismatch");
-
- // Don't accept any forks from the main chain prior to last checkpoint
- CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint();
- if (pcheckpoint && nHeight < pcheckpoint->nHeight)
- return state.DoS(100, error("%s : forked chain older than last checkpoint (height %d)", __func__, nHeight));
-
- // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded:
- if (block.nVersion < 2 &&
- CBlockIndex::IsSuperMajority(2, pindexPrev, Params().RejectBlockOutdatedMajority()))
- {
- return state.Invalid(error("%s : rejected nVersion=1 block", __func__),
- REJECT_OBSOLETE, "bad-version");
- }
}
+ if (!ContextualCheckBlockHeader(block, state, pindexPrev))
+ return false;
+
if (pindex == NULL)
pindex = AddToBlockIndex(block);
@@ -2415,36 +2570,16 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
return true;
}
- if (!CheckBlock(block, state)) {
+ if ((!CheckBlock(block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) {
if (state.IsInvalid() && !state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
+ setDirtyBlockIndex.insert(pindex);
}
return false;
}
int nHeight = pindex->nHeight;
- // Check that all transactions are finalized
- BOOST_FOREACH(const CTransaction& tx, block.vtx)
- if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) {
- pindex->nStatus |= BLOCK_FAILED_VALID;
- return state.DoS(10, error("AcceptBlock() : contains a non-final transaction"),
- REJECT_INVALID, "bad-txns-nonfinal");
- }
-
- // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height
- // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet):
- if (block.nVersion >= 2 &&
- CBlockIndex::IsSuperMajority(2, pindex->pprev, Params().EnforceBlockUpgradeMajority()))
- {
- CScript expect = CScript() << nHeight;
- if (block.vtx[0].vin[0].scriptSig.size() < expect.size() ||
- !std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) {
- pindex->nStatus |= BLOCK_FAILED_VALID;
- return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase"), REJECT_INVALID, "bad-cb-height");
- }
- }
-
// Write block to history file
try {
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
@@ -2555,6 +2690,30 @@ bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDis
return true;
}
+bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex * const pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot)
+{
+ AssertLockHeld(cs_main);
+ assert(pindexPrev == chainActive.Tip());
+
+ CCoinsViewCache viewNew(pcoinsTip);
+ CBlockIndex indexDummy(block);
+ indexDummy.pprev = pindexPrev;
+ indexDummy.nHeight = pindexPrev->nHeight + 1;
+
+ // NOTE: CheckBlockHeader is called by CheckBlock
+ if (!ContextualCheckBlockHeader(block, state, pindexPrev))
+ return false;
+ if (!CheckBlock(block, state, fCheckPOW, fCheckMerkleRoot))
+ return false;
+ if (!ContextualCheckBlock(block, state, pindexPrev))
+ return false;
+ if (!ConnectBlock(block, state, &indexDummy, viewNew, true))
+ return false;
+ assert(state.IsValid());
+
+ return true;
+}
+
@@ -2602,7 +2761,7 @@ uint256 CPartialMerkleTree::CalcHash(int height, unsigned int pos, const std::ve
} else {
// calculate left hash
uint256 left = CalcHash(height-1, pos*2, vTxid), right;
- // calculate right hash if not beyong the end of the array - copy left hash otherwise1
+ // calculate right hash if not beyond the end of the array - copy left hash otherwise1
if (pos*2+1 < CalcTreeWidth(height-1))
right = CalcHash(height-1, pos*2+1, vTxid);
else
@@ -2811,7 +2970,7 @@ bool static LoadBlockIndexDB()
BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight)
{
CBlockIndex* pindex = item.second;
- pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + pindex->GetBlockWork();
+ pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex);
if (pindex->nStatus & BLOCK_HAVE_DATA) {
if (pindex->pprev) {
if (pindex->pprev->nChainTx) {
@@ -2883,6 +3042,9 @@ bool static LoadBlockIndexDB()
if (it == mapBlockIndex.end())
return true;
chainActive.SetTip(it->second);
+
+ PruneBlockIndexCandidates();
+
LogPrintf("LoadBlockIndexDB(): hashBestChain=%s height=%d date=%s progress=%f\n",
chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(),
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()),
@@ -3022,6 +3184,8 @@ bool InitBlockIndex() {
return error("LoadBlockIndex() : genesis block not accepted");
if (!ActivateBestChain(state, &block))
return error("LoadBlockIndex() : genesis block cannot be activated");
+ // Force a chainstate write so that when we VerifyDB in a moment, it doesnt check stale data
+ return FlushStateToDisk(state, FLUSH_STATE_ALWAYS);
} catch(std::runtime_error &e) {
return error("LoadBlockIndex() : failed to initialize block database: %s", e.what());
}
@@ -3032,75 +3196,6 @@ bool InitBlockIndex() {
-void PrintBlockTree()
-{
- AssertLockHeld(cs_main);
- // pre-compute tree structure
- map<CBlockIndex*, vector<CBlockIndex*> > mapNext;
- for (BlockMap::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi)
- {
- CBlockIndex* pindex = (*mi).second;
- mapNext[pindex->pprev].push_back(pindex);
- // test
- //while (rand() % 3 == 0)
- // mapNext[pindex->pprev].push_back(pindex);
- }
-
- vector<pair<int, CBlockIndex*> > vStack;
- vStack.push_back(make_pair(0, chainActive.Genesis()));
-
- int nPrevCol = 0;
- while (!vStack.empty())
- {
- int nCol = vStack.back().first;
- CBlockIndex* pindex = vStack.back().second;
- vStack.pop_back();
-
- // print split or gap
- if (nCol > nPrevCol)
- {
- for (int i = 0; i < nCol-1; i++)
- LogPrintf("| ");
- LogPrintf("|\\\n");
- }
- else if (nCol < nPrevCol)
- {
- for (int i = 0; i < nCol; i++)
- LogPrintf("| ");
- LogPrintf("|\n");
- }
- nPrevCol = nCol;
-
- // print columns
- for (int i = 0; i < nCol; i++)
- LogPrintf("| ");
-
- // print item
- CBlock block;
- ReadBlockFromDisk(block, pindex);
- LogPrintf("%d (blk%05u.dat:0x%x) %s tx %u\n",
- pindex->nHeight,
- pindex->GetBlockPos().nFile, pindex->GetBlockPos().nPos,
- DateTimeStrFormat("%Y-%m-%d %H:%M:%S", block.GetBlockTime()),
- block.vtx.size());
-
- // put the main time-chain first
- vector<CBlockIndex*>& vNext = mapNext[pindex];
- for (unsigned int i = 0; i < vNext.size(); i++)
- {
- if (chainActive.Next(vNext[i]))
- {
- swap(vNext[0], vNext[i]);
- break;
- }
- }
-
- // iterate children
- for (unsigned int i = 0; i < vNext.size(); i++)
- vStack.push_back(make_pair(nCol+i, vNext[i]));
- }
-}
-
bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
{
// Map of disk positions for blocks with unknown parent (only used for reindex)
@@ -3157,12 +3252,14 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
}
// process in case the block isn't known yet
- if (mapBlockIndex.count(hash) == 0) {
+ if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) {
CValidationState state;
if (ProcessNewBlock(state, NULL, &block, dbp))
nLoaded++;
if (state.IsError())
break;
+ } else if (hash != Params().HashGenesisBlock() && mapBlockIndex[hash]->nHeight % 1000 == 0) {
+ LogPrintf("Block Import: already had block %s at height %d\n", hash.ToString(), mapBlockIndex[hash]->nHeight);
}
// Recursively process earlier encountered successors of this block
@@ -3212,12 +3309,12 @@ string GetWarnings(string strFor)
string strStatusBar;
string strRPC;
- if (GetBoolArg("-testsafemode", false))
- strRPC = "test";
-
if (!CLIENT_VERSION_IS_RELEASE)
strStatusBar = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications");
+ if (GetBoolArg("-testsafemode", false))
+ strStatusBar = strRPC = "testsafemode enabled";
+
// Misc warnings like out of disk space and clock is wrong
if (strMiscWarning != "")
{
@@ -3474,12 +3571,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
else
pfrom->fRelayTxes = true;
- if (pfrom->fInbound && addrMe.IsRoutable())
- {
- pfrom->addrLocal = addrMe;
- SeenLocal(addrMe);
- }
-
// Disconnect if we connected to ourself
if (nNonce == nLocalHostNonce && nNonce > 1)
{
@@ -3488,6 +3579,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
return true;
}
+ pfrom->addrLocal = addrMe;
+ if (pfrom->fInbound && addrMe.IsRoutable())
+ {
+ SeenLocal(addrMe);
+ }
+
// Be shy and don't send version until we hear
if (pfrom->fInbound)
pfrom->PushVersion();
@@ -3508,7 +3605,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
{
CAddress addr = GetLocalAddress(&pfrom->addr);
if (addr.IsRoutable())
+ {
+ pfrom->PushAddress(addr);
+ } else if (IsPeerAddrLocalGood(pfrom)) {
+ addr.SetIP(pfrom->addrLocal);
pfrom->PushAddress(addr);
+ }
}
// Get recent addresses
@@ -3889,7 +3991,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pfrom->id, pfrom->cleanSubVer,
state.GetRejectReason());
pfrom->PushMessage("reject", strCommand, state.GetRejectCode(),
- state.GetRejectReason(), inv.hash);
+ state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash);
if (nDoS > 0)
Misbehaving(pfrom->GetId(), nDoS);
}
@@ -3963,7 +4065,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
int nDoS;
if (state.IsInvalid(nDoS)) {
pfrom->PushMessage("reject", strCommand, state.GetRejectCode(),
- state.GetRejectReason(), inv.hash);
+ state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash);
if (nDoS > 0) {
LOCK(cs_main);
Misbehaving(pfrom->GetId(), nDoS);
@@ -4170,7 +4272,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if (fDebug) {
try {
string strMsg; unsigned char ccode; string strReason;
- vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, 111);
+ vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, MAX_REJECT_MESSAGE_LENGTH);
ostringstream ss;
ss << strMsg << " code " << itostr(ccode) << ": " << strReason;
@@ -4371,24 +4473,18 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
static int64_t nLastRebroadcast;
if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60))
{
+ LOCK(cs_vNodes);
+ BOOST_FOREACH(CNode* pnode, vNodes)
{
- LOCK(cs_vNodes);
- BOOST_FOREACH(CNode* pnode, vNodes)
- {
- // Periodically clear setAddrKnown to allow refresh broadcasts
- if (nLastRebroadcast)
- pnode->setAddrKnown.clear();
+ // Periodically clear setAddrKnown to allow refresh broadcasts
+ if (nLastRebroadcast)
+ pnode->setAddrKnown.clear();
- // Rebroadcast our address
- if (fListen)
- {
- CAddress addr = GetLocalAddress(&pnode->addr);
- if (addr.IsRoutable())
- pnode->PushAddress(addr);
- }
- }
+ // Rebroadcast our address
+ AdvertizeLocal(pnode);
}
- nLastRebroadcast = GetTime();
+ if (!vNodes.empty())
+ nLastRebroadcast = GetTime();
}
//
@@ -4591,11 +4687,6 @@ bool CBlockUndo::WriteToDisk(CDiskBlockPos &pos, const uint256 &hashBlock)
hasher << *this;
fileout << hasher.GetHash();
- // Flush stdio buffers and commit to disk before returning
- fflush(fileout.Get());
- if (!IsInitialBlockDownload())
- FileCommit(fileout.Get());
-
return true;
}