aboutsummaryrefslogtreecommitdiff
path: root/src/validation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/validation.cpp')
-rw-r--r--src/validation.cpp527
1 files changed, 303 insertions, 224 deletions
diff --git a/src/validation.cpp b/src/validation.cpp
index 8066e6c7a..f6257ffae 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2017 The Bitcoin Core developers
+// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -16,7 +16,7 @@
#include <consensus/validation.h>
#include <cuckoocache.h>
#include <hash.h>
-#include <init.h>
+#include <index/txindex.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/rbf.h>
@@ -28,6 +28,7 @@
#include <script/script.h>
#include <script/sigcache.h>
#include <script/standard.h>
+#include <shutdown.h>
#include <timedata.h>
#include <tinyformat.h>
#include <txdb.h>
@@ -44,7 +45,6 @@
#include <sstream>
#include <boost/algorithm/string/replace.hpp>
-#include <boost/algorithm/string/join.hpp>
#include <boost/thread.hpp>
#if defined(NDEBUG)
@@ -142,7 +142,13 @@ private:
* ahead and mark descendants of invalid blocks as FAILED_CHILD at that time,
* instead of putting things in this set.
*/
- std::set<CBlockIndex*> g_failed_blocks;
+ std::set<CBlockIndex*> m_failed_blocks;
+
+ /**
+ * the ChainState CriticalSection
+ * A lock that must be held when modifying this ChainState - held in ActivateBestChain()
+ */
+ CCriticalSection m_cs_chainstate;
public:
CChain chainActive;
@@ -150,25 +156,29 @@ public:
std::multimap<CBlockIndex*, CBlockIndex*> mapBlocksUnlinked;
CBlockIndex *pindexBestInvalid = nullptr;
- bool LoadBlockIndex(const Consensus::Params& consensus_params, CBlockTreeDB& blocktree);
+ bool LoadBlockIndex(const Consensus::Params& consensus_params, CBlockTreeDB& blocktree) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock);
- bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex);
- bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock);
+ /**
+ * If a block header hasn't already been seen, call CheckBlockHeader on it, ensure
+ * that it doesn't descend from an invalid block, and then add it to mapBlockIndex.
+ */
+ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Block (dis)connection on a given view:
DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view);
bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex,
- CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false);
+ CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// Block disconnection on our pcoinsTip:
bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions *disconnectpool);
// Manual block validity manipulation:
- bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex);
- bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex);
- bool ResetBlockFailureFlags(CBlockIndex *pindex);
+ bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
+ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
bool ReplayBlocks(const CChainParams& params, CCoinsView* view);
bool RewindBlockIndex(const CChainParams& params);
@@ -179,20 +189,25 @@ public:
void UnloadBlockIndex();
private:
- bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace);
- bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool);
+ bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- CBlockIndex* AddToBlockIndex(const CBlockHeader& block);
+ CBlockIndex* AddToBlockIndex(const CBlockHeader& block) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
/** Create a new block index entry for a given block hash */
- CBlockIndex * InsertBlockIndex(const uint256& hash);
+ CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ /**
+ * Make various assertions about the state of the block index.
+ *
+ * By default this only executes fully when using the Regtest chain; see: fCheckBlockIndex.
+ */
void CheckBlockIndex(const Consensus::Params& consensusParams);
- void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state);
- CBlockIndex* FindMostWorkChain();
- bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams);
+ void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
+ void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
- bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params);
+ bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
} g_chainstate;
@@ -202,12 +217,12 @@ CCriticalSection cs_main;
BlockMap& mapBlockIndex = g_chainstate.mapBlockIndex;
CChain& chainActive = g_chainstate.chainActive;
CBlockIndex *pindexBestHeader = nullptr;
-CWaitableCriticalSection csBestBlock;
-CConditionVariable cvBlockChange;
+CWaitableCriticalSection g_best_block_mutex;
+CConditionVariable g_best_block_cv;
+uint256 g_best_block;
int nScriptCheckThreads = 0;
std::atomic_bool fImporting(false);
std::atomic_bool fReindex(false);
-bool fTxIndex = false;
bool fHavePruned = false;
bool fPruneMode = false;
bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
@@ -263,7 +278,8 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc
{
AssertLockHeld(cs_main);
- // Find the first block the caller has in the main chain
+ // Find the latest block common to locator and chain - we expect that
+ // locator.vHave is sorted descending by height.
for (const uint256& hash : locator.vHave) {
CBlockIndex* pindex = LookupBlockIndex(hash);
if (pindex) {
@@ -352,7 +368,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool
CBlockIndex* tip = chainActive.Tip();
assert(tip != nullptr);
-
+
CBlockIndex index;
index.pprev = tip;
// CheckSequenceLocks() uses chainActive.Height()+1 to evaluate
@@ -441,7 +457,7 @@ std::string FormatStateMessage(const CValidationState &state)
state.GetRejectCode());
}
-static bool IsCurrentForFeeEstimation()
+static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
if (IsInitialBlockDownload())
@@ -466,7 +482,7 @@ static bool IsCurrentForFeeEstimation()
* and instead just erase from the mempool as needed.
*/
-void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool fAddToMempool)
+static void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
std::vector<uint256> vHashUpdate;
@@ -508,7 +524,7 @@ void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool f
// Used to avoid mempool polluting consensus critical paths if CCoinsViewMempool
// were somehow broken and returning the wrong scriptPubKeys
static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& view, const CTxMemPool& pool,
- unsigned int flags, bool cacheSigStore, PrecomputedTransactionData& txdata) {
+ unsigned int flags, bool cacheSigStore, PrecomputedTransactionData& txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
AssertLockHeld(cs_main);
// pool.cs should be locked already, but go ahead and re-take the lock here
@@ -543,7 +559,7 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt
static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx,
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
- bool bypass_limits, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache, bool test_accept)
+ bool bypass_limits, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
const CTransaction& tx = *ptx;
const uint256 hash = tx.GetHash();
@@ -560,17 +576,17 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
if (tx.IsCoinBase())
return state.DoS(100, false, REJECT_INVALID, "coinbase");
- // Reject transactions with witness before segregated witness activates (override with -prematurewitness)
- bool witnessEnabled = IsWitnessEnabled(chainActive.Tip(), chainparams.GetConsensus());
- if (!gArgs.GetBoolArg("-prematurewitness", false) && tx.HasWitness() && !witnessEnabled) {
- return state.DoS(0, false, REJECT_NONSTANDARD, "no-witness-yet", true);
- }
-
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
std::string reason;
- if (fRequireStandard && !IsStandardTx(tx, reason, witnessEnabled))
+ if (fRequireStandard && !IsStandardTx(tx, reason))
return state.DoS(0, false, REJECT_NONSTANDARD, reason);
+ // Do not work on transactions that are too small.
+ // A transaction with 1 segwit input and 1 P2WPHK output has non-witness size of 82 bytes.
+ // Transactions smaller than this are not relayed to reduce unnecessary malloc overhead.
+ if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) < MIN_STANDARD_TX_NONWITNESS_SIZE)
+ return state.DoS(0, false, REJECT_NONSTANDARD, "tx-size-small");
+
// Only accept nLockTime-using transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't
// be mined yet.
@@ -634,7 +650,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
view.SetBackend(viewMemPool);
// do all inputs exist?
- for (const CTxIn txin : tx.vin) {
+ for (const CTxIn& txin : tx.vin) {
if (!pcoinsTip->HaveCoinInCache(txin.prevout)) {
coins_to_uncache.push_back(txin.prevout);
}
@@ -718,7 +734,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// No transactions are allowed below minRelayTxFee except from disconnected blocks
if (!bypass_limits && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) {
- return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "min relay fee not met");
+ return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "min relay fee not met", false, strprintf("%d < %d", nModifiedFees, ::minRelayTxFee.GetFee(nSize)));
}
if (nAbsurdFee && nFees > nAbsurdFee)
@@ -789,13 +805,11 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// be increased is also an easy-to-reason about way to prevent
// DoS attacks via replacements.
//
- // The mining code doesn't (currently) take children into
- // account (CPFP) so we only consider the feerates of
- // transactions being directly replaced, not their indirect
- // descendants. While that does mean high feerate children are
- // ignored when deciding whether or not to replace, we do
- // require the replacement to pay more overall fees too,
- // mitigating most cases.
+ // We only consider the feerates of transactions being directly
+ // replaced, not their indirect descendants. While that does
+ // mean high feerate children are ignored when deciding whether
+ // or not to replace, we do require the replacement to pay more
+ // overall fees too, mitigating most cases.
CFeeRate oldFeeRate(mi->GetModifiedFee(), mi->GetTxSize());
if (newFeeRate <= oldFeeRate)
{
@@ -880,10 +894,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
}
}
- unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
- if (!chainparams.RequireStandard()) {
- scriptVerifyFlags = gArgs.GetArg("-promiscuousmempoolflags", scriptVerifyFlags);
- }
+ constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
@@ -918,20 +929,8 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// transactions into the mempool can be exploited as a DoS attack.
unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(chainActive.Tip(), Params().GetConsensus());
if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, currentBlockScriptVerifyFlags, true, txdata)) {
- // If we're using promiscuousmempoolflags, we may hit this normally
- // Check if current block has some flags that scriptVerifyFlags
- // does not before printing an ominous warning
- if (!(~scriptVerifyFlags & currentBlockScriptVerifyFlags)) {
- return error("%s: BUG! PLEASE REPORT THIS! ConnectInputs failed against latest-block but not STANDARD flags %s, %s",
+ return error("%s: BUG! PLEASE REPORT THIS! CheckInputs failed against latest-block but not STANDARD flags %s, %s",
__func__, hash.ToString(), FormatStateMessage(state));
- } else {
- if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, false, txdata)) {
- return error("%s: ConnectInputs failed against MANDATORY but not STANDARD flags due to promiscuous mempool %s, %s",
- __func__, hash.ToString(), FormatStateMessage(state));
- } else {
- LogPrintf("Warning: -promiscuousmempool flags set to not include currently enforced soft forks, this may break mining or otherwise cause instability!\n");
- }
- }
}
if (test_accept) {
@@ -940,7 +939,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
}
// Remove conflicting transactions from the mempool
- for (const CTxMemPool::txiter it : allConflicting)
+ for (CTxMemPool::txiter it : allConflicting)
{
LogPrint(BCLog::MEMPOOL, "replacing tx %s with %s for %s BTC additional fees, %d delta bytes\n",
it->GetTx().GetHash().ToString(),
@@ -960,7 +959,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
bool validForFeeEstimation = !fReplacementTransaction && !bypass_limits && IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx);
// Store transaction in memory
- pool.addUnchecked(hash, entry, setAncestors, validForFeeEstimation);
+ pool.addUnchecked(entry, setAncestors, validForFeeEstimation);
// trim mempool and check if tx was trimmed
if (!bypass_limits) {
@@ -978,7 +977,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
/** (try to) add transaction to memory pool with a specified acceptance time **/
static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx,
bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
- bool bypass_limits, const CAmount nAbsurdFee, bool test_accept)
+ bool bypass_limits, const CAmount nAbsurdFee, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
std::vector<COutPoint> coins_to_uncache;
bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, pfMissingInputs, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept);
@@ -1017,28 +1016,8 @@ bool GetTransaction(const uint256& hash, CTransactionRef& txOut, const Consensus
return true;
}
- if (fTxIndex) {
- CDiskTxPos postx;
- if (pblocktree->ReadTxIndex(hash, postx)) {
- CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
- if (file.IsNull())
- return error("%s: OpenBlockFile failed", __func__);
- CBlockHeader header;
- try {
- file >> header;
- fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
- file >> txOut;
- } catch (const std::exception& e) {
- return error("%s: Deserialize or I/O error - %s", __func__, e.what());
- }
- hashBlock = header.GetHash();
- if (txOut->GetHash() != hash)
- return error("%s: txid mismatch", __func__);
- return true;
- }
-
- // transaction not found in index, nothing more can be done
- return false;
+ if (g_txindex) {
+ return g_txindex->FindTx(hash, hashBlock, txOut);
}
if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
@@ -1134,6 +1113,52 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus
return true;
}
+bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& message_start)
+{
+ CDiskBlockPos hpos = pos;
+ hpos.nPos -= 8; // Seek back 8 bytes for meta header
+ CAutoFile filein(OpenBlockFile(hpos, true), SER_DISK, CLIENT_VERSION);
+ if (filein.IsNull()) {
+ return error("%s: OpenBlockFile failed for %s", __func__, pos.ToString());
+ }
+
+ try {
+ CMessageHeader::MessageStartChars blk_start;
+ unsigned int blk_size;
+
+ filein >> blk_start >> blk_size;
+
+ if (memcmp(blk_start, message_start, CMessageHeader::MESSAGE_START_SIZE)) {
+ return error("%s: Block magic mismatch for %s: %s versus expected %s", __func__, pos.ToString(),
+ HexStr(blk_start, blk_start + CMessageHeader::MESSAGE_START_SIZE),
+ HexStr(message_start, message_start + CMessageHeader::MESSAGE_START_SIZE));
+ }
+
+ if (blk_size > MAX_SIZE) {
+ return error("%s: Block data is larger than maximum deserialization size for %s: %s versus %s", __func__, pos.ToString(),
+ blk_size, MAX_SIZE);
+ }
+
+ block.resize(blk_size); // Zeroing of memory is intentional here
+ filein.read((char*)block.data(), blk_size);
+ } catch(const std::exception& e) {
+ return error("%s: Read from block file failed: %s for %s", __func__, e.what(), pos.ToString());
+ }
+
+ return true;
+}
+
+bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start)
+{
+ CDiskBlockPos block_pos;
+ {
+ LOCK(cs_main);
+ block_pos = pindex->GetBlockPos();
+ }
+
+ return ReadRawBlockFromDisk(block, block_pos, message_start);
+}
+
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
{
int halvings = nHeight / consensusParams.nSubsidyHalvingInterval;
@@ -1191,7 +1216,7 @@ static void AlertNotify(const std::string& strMessage)
t.detach(); // thread runs free
}
-static void CheckForkWarningConditions()
+static void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
// Before we get past initial download, we cannot reliably alert about forks
@@ -1232,7 +1257,7 @@ static void CheckForkWarningConditions()
}
}
-static void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip)
+static void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
AssertLockHeld(cs_main);
// If we are on a fork that is sufficiently large, set a warning flag
@@ -1265,7 +1290,7 @@ static void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip)
CheckForkWarningConditions();
}
-void static InvalidChainFound(CBlockIndex* pindexNew)
+void static InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork)
pindexBestInvalid = pindexNew;
@@ -1284,7 +1309,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
void CChainState::InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) {
if (!state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
- g_failed_blocks.insert(pindex);
+ m_failed_blocks.insert(pindex);
setDirtyBlockIndex.insert(pindex);
setBlockIndexCandidates.erase(pindex);
InvalidChainFound(pindex);
@@ -1352,7 +1377,7 @@ void InitScriptExecutionCache() {
*
* Non-static (and re-declared) in src/test/txvalidationcache_tests.cpp
*/
-bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks)
+bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
if (!tx.IsCoinBase())
{
@@ -1496,7 +1521,7 @@ static bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex *pindex)
}
/** Abort with a message */
-bool AbortNode(const std::string& strMessage, const std::string& userMessage="")
+static bool AbortNode(const std::string& strMessage, const std::string& userMessage="")
{
SetMiscWarning(strMessage);
LogPrintf("*** %s\n", strMessage);
@@ -1507,7 +1532,7 @@ bool AbortNode(const std::string& strMessage, const std::string& userMessage="")
return false;
}
-bool AbortNode(CValidationState& state, const std::string& strMessage, const std::string& userMessage="")
+static bool AbortNode(CValidationState& state, const std::string& strMessage, const std::string& userMessage="")
{
AbortNode(strMessage, userMessage);
return state.Error(strMessage);
@@ -1613,22 +1638,27 @@ void static FlushBlockFile(bool fFinalize = false)
LOCK(cs_LastBlockFile);
CDiskBlockPos posOld(nLastBlockFile, 0);
+ bool status = true;
FILE *fileOld = OpenBlockFile(posOld);
if (fileOld) {
if (fFinalize)
- TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize);
- FileCommit(fileOld);
+ status &= TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize);
+ status &= FileCommit(fileOld);
fclose(fileOld);
}
fileOld = OpenUndoFile(posOld);
if (fileOld) {
if (fFinalize)
- TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize);
- FileCommit(fileOld);
+ status &= TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize);
+ status &= FileCommit(fileOld);
fclose(fileOld);
}
+
+ if (!status) {
+ AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error.");
+ }
}
static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
@@ -1652,26 +1682,6 @@ static bool WriteUndoDataForBlock(const CBlockUndo& blockundo, CValidationState&
return true;
}
-static bool WriteTxIndexDataForBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex)
-{
- if (!fTxIndex) return true;
-
- CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size()));
- std::vector<std::pair<uint256, CDiskTxPos> > vPos;
- vPos.reserve(block.vtx.size());
- for (const CTransactionRef& tx : block.vtx)
- {
- vPos.push_back(std::make_pair(tx->GetHash(), pos));
- pos.nTxOffset += ::GetSerializeSize(*tx, SER_DISK, CLIENT_VERSION);
- }
-
- if (!pblocktree->WriteTxIndex(vPos)) {
- return AbortNode(state, "Failed to write transaction index");
- }
-
- return true;
-}
-
static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
void ThreadScriptCheck() {
@@ -1724,16 +1734,38 @@ public:
// Protected by cs_main
static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS];
-static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams) {
+// 0.13.0 was shipped with a segwit deployment defined for testnet, but not for
+// mainnet. We no longer need to support disabling the segwit deployment
+// except for testing purposes, due to limitations of the functional test
+// environment. See test/functional/p2p-segwit.py.
+static bool IsScriptWitnessEnabled(const Consensus::Params& params)
+{
+ return params.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0;
+}
+
+static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
AssertLockHeld(cs_main);
unsigned int flags = SCRIPT_VERIFY_NONE;
- // Start enforcing P2SH (BIP16)
- if (pindex->nHeight >= consensusparams.BIP16Height) {
+ // BIP16 didn't become active until Apr 1 2012 (on mainnet, and
+ // retroactively applied to testnet)
+ // However, only one historical block violated the P2SH rules (on both
+ // mainnet and testnet), so for simplicity, always leave P2SH
+ // on except for the one violating block.
+ if (consensusparams.BIP16Exception.IsNull() || // no bip16 exception on this chain
+ pindex->phashBlock == nullptr || // this is a new candidate block, eg from TestBlockValidity()
+ *pindex->phashBlock != consensusparams.BIP16Exception) // this block isn't the historical exception
+ {
flags |= SCRIPT_VERIFY_P2SH;
}
+ // Enforce WITNESS rules whenever P2SH is in effect (and the segwit
+ // deployment is defined).
+ if (flags & SCRIPT_VERIFY_P2SH && IsScriptWitnessEnabled(consensusparams)) {
+ flags |= SCRIPT_VERIFY_WITNESS;
+ }
+
// Start enforcing the DERSIG (BIP66) rule
if (pindex->nHeight >= consensusparams.BIP66Height) {
flags |= SCRIPT_VERIFY_DERSIG;
@@ -1749,9 +1781,7 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens
flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
}
- // Start enforcing WITNESS rules using versionbits logic.
- if (IsWitnessEnabled(pindex->pprev, consensusparams)) {
- flags |= SCRIPT_VERIFY_WITNESS;
+ if (IsNullDummyEnabled(pindex->pprev, consensusparams)) {
flags |= SCRIPT_VERIFY_NULLDUMMY;
}
@@ -2043,9 +2073,6 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
setDirtyBlockIndex.insert(pindex);
}
- if (!WriteTxIndexDataForBlock(block, state, pindex))
- return false;
-
assert(pindex->phashBlock);
// add this block to the view's block chain
view.SetBestBlock(pindex->GetBlockHash());
@@ -2073,13 +2100,12 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
LOCK(cs_main);
static int64_t nLastWrite = 0;
static int64_t nLastFlush = 0;
- static int64_t nLastSetChain = 0;
std::set<int> setFilesToPrune;
- bool fFlushForPrune = false;
- bool fDoFullFlush = false;
- int64_t nNow = 0;
+ bool full_flush_completed = false;
try {
{
+ bool fFlushForPrune = false;
+ bool fDoFullFlush = false;
LOCK(cs_LastBlockFile);
if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) {
if (nManualPruneHeight > 0) {
@@ -2096,7 +2122,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
}
}
}
- nNow = GetTimeMicros();
+ int64_t nNow = GetTimeMicros();
// Avoid writing/flushing immediately after startup.
if (nLastWrite == 0) {
nLastWrite = nNow;
@@ -2104,9 +2130,6 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
if (nLastFlush == 0) {
nLastFlush = nNow;
}
- if (nLastSetChain == 0) {
- nLastSetChain = nNow;
- }
int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
int64_t cacheSize = pcoinsTip->DynamicMemoryUsage();
int64_t nTotalSpace = nCoinCacheUsage + std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0);
@@ -2163,12 +2186,12 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
if (!pcoinsTip->Flush())
return AbortNode(state, "Failed to write to coin database");
nLastFlush = nNow;
+ full_flush_completed = true;
}
}
- if (fDoFullFlush || ((mode == FlushStateMode::ALWAYS || mode == FlushStateMode::PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000)) {
+ if (full_flush_completed) {
// Update best block in wallet (so we can detect restored wallets).
- GetMainSignals().SetBestChain(chainActive.GetLocator());
- nLastSetChain = nNow;
+ GetMainSignals().ChainStateFlushed(chainActive.GetLocator());
}
} catch (const std::runtime_error& e) {
return AbortNode(state, std::string("System error while flushing: ") + e.what());
@@ -2179,14 +2202,18 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
void FlushStateToDisk() {
CValidationState state;
const CChainParams& chainparams = Params();
- FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS);
+ if (!FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) {
+ LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state));
+ }
}
void PruneAndFlush() {
CValidationState state;
fCheckForPruning = true;
const CChainParams& chainparams = Params();
- FlushStateToDisk(chainparams, state, FlushStateMode::NONE);
+ if (!FlushStateToDisk(chainparams, state, FlushStateMode::NONE)) {
+ LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state));
+ }
}
static void DoWarning(const std::string& strWarning)
@@ -2199,14 +2226,25 @@ static void DoWarning(const std::string& strWarning)
}
}
+/** Private helper function that concatenates warning messages. */
+static void AppendWarning(std::string& res, const std::string& warn)
+{
+ if (!res.empty()) res += ", ";
+ res += warn;
+}
+
/** Check warning conditions and do some notifications on new chain tip set. */
void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainParams) {
// New best block
mempool.AddTransactionsUpdated(1);
- cvBlockChange.notify_all();
+ {
+ WaitableLock lock(g_best_block_mutex);
+ g_best_block = pindexNew->GetBlockHash();
+ g_best_block_cv.notify_all();
+ }
- std::vector<std::string> warningMessages;
+ std::string warningMessages;
if (!IsInitialBlockDownload())
{
int nUpgraded = 0;
@@ -2219,7 +2257,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
if (state == ThresholdState::ACTIVE) {
DoWarning(strWarning);
} else {
- warningMessages.push_back(strWarning);
+ AppendWarning(warningMessages, strWarning);
}
}
}
@@ -2232,7 +2270,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
pindex = pindex->pprev;
}
if (nUpgraded > 0)
- warningMessages.push_back(strprintf(_("%d of last 100 blocks have unexpected version"), nUpgraded));
+ AppendWarning(warningMessages, strprintf(_("%d of last 100 blocks have unexpected version"), nUpgraded));
if (nUpgraded > 100/2)
{
std::string strWarning = _("Warning: Unknown block versions being mined! It's possible unknown rules are in effect");
@@ -2246,7 +2284,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
FormatISO8601DateTime(pindexNew->GetBlockTime()),
GuessVerificationProgress(chainParams.TxData(), pindexNew), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize());
if (!warningMessages.empty())
- LogPrintf(" warning='%s'", boost::algorithm::join(warningMessages, ", ")); /* Continued */
+ LogPrintf(" warning='%s'", warningMessages); /* Continued */
LogPrintf("\n");
}
@@ -2410,7 +2448,7 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
if (!rv) {
if (state.IsInvalid())
InvalidBlockFound(pindexNew, state);
- return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString());
+ return error("%s: ConnectBlock %s failed, %s", __func__, pindexNew->GetBlockHash().ToString(), FormatStateMessage(state));
}
nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2;
LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime3 - nTime2) * MILLI, nTimeConnectTotal * MICRO, nTimeConnectTotal * MILLI / nBlocksTotal);
@@ -2516,6 +2554,7 @@ void CChainState::PruneBlockIndexCandidates() {
bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
{
AssertLockHeld(cs_main);
+
const CBlockIndex *pindexOldTip = chainActive.Tip();
const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork);
@@ -2554,8 +2593,9 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) {
if (state.IsInvalid()) {
// The block violates a consensus rule.
- if (!state.CorruptionPossible())
- InvalidChainFound(vpindexToConnect.back());
+ if (!state.CorruptionPossible()) {
+ InvalidChainFound(vpindexToConnect.front());
+ }
state = CValidationState();
fInvalidFound = true;
fContinue = false;
@@ -2594,7 +2634,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
return true;
}
-static void NotifyHeaderTip() {
+static void NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) {
bool fNotify = false;
bool fInitialBlockDownload = false;
static CBlockIndex* pindexHeaderOld = nullptr;
@@ -2619,6 +2659,10 @@ static void NotifyHeaderTip() {
* Make the best chain active, in multiple steps. The result is either failure
* or an activated best chain. pblock is either nullptr or a pointer to a block
* that is already loaded (to avoid loading it again from disk).
+ *
+ * ActivateBestChain is split into steps (see ActivateBestChainStep) so that
+ * we avoid holding cs_main for an extended period of time; the length of this
+ * call may be quite long during reindexing or a substantial reorg.
*/
bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) {
// Note that while we're often called here from ProcessNewBlock, this is
@@ -2627,6 +2671,12 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
// sanely for performance or correctness!
AssertLockNotHeld(cs_main);
+ // ABC maintains a fair degree of expensive-to-calculate internal state
+ // because this function periodically releases cs_main so that it does not lock up other threads for too long
+ // during large connects - and to allow for e.g. the callback queue to drain
+ // we use m_cs_chainstate to enforce mutual exclusion so that only one caller may execute this function at a time
+ LOCK(m_cs_chainstate);
+
CBlockIndex *pindexMostWork = nullptr;
CBlockIndex *pindexNewTip = nullptr;
int nStopAtHeight = gArgs.GetArg("-stopatheight", DEFAULT_STOPATHEIGHT);
@@ -2637,53 +2687,63 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
// Block until the validation queue drains. This should largely
// never happen in normal operation, however may happen during
// reindex, causing memory blowup if we run too far ahead.
+ // Note that if a validationinterface callback ends up calling
+ // ActivateBestChain this may lead to a deadlock! We should
+ // probably have a DEBUG_LOCKORDER test for this in the future.
SyncWithValidationInterfaceQueue();
}
- const CBlockIndex *pindexFork;
- bool fInitialDownload;
{
LOCK(cs_main);
- ConnectTrace connectTrace(mempool); // Destructed before cs_main is unlocked
-
- CBlockIndex *pindexOldTip = chainActive.Tip();
- if (pindexMostWork == nullptr) {
- pindexMostWork = FindMostWorkChain();
- }
+ CBlockIndex* starting_tip = chainActive.Tip();
+ bool blocks_connected = false;
+ do {
+ // We absolutely may not unlock cs_main until we've made forward progress
+ // (with the exception of shutdown due to hardware issues, low disk space, etc).
+ ConnectTrace connectTrace(mempool); // Destructed before cs_main is unlocked
+
+ if (pindexMostWork == nullptr) {
+ pindexMostWork = FindMostWorkChain();
+ }
- // Whether we have anything to do at all.
- if (pindexMostWork == nullptr || pindexMostWork == chainActive.Tip())
- return true;
+ // Whether we have anything to do at all.
+ if (pindexMostWork == nullptr || pindexMostWork == chainActive.Tip()) {
+ break;
+ }
- bool fInvalidFound = false;
- std::shared_ptr<const CBlock> nullBlockPtr;
- if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace))
- return false;
+ bool fInvalidFound = false;
+ std::shared_ptr<const CBlock> nullBlockPtr;
+ if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace))
+ return false;
+ blocks_connected = true;
- if (fInvalidFound) {
- // Wipe cache, we may need another branch now.
- pindexMostWork = nullptr;
- }
- pindexNewTip = chainActive.Tip();
- pindexFork = chainActive.FindFork(pindexOldTip);
- fInitialDownload = IsInitialBlockDownload();
+ if (fInvalidFound) {
+ // Wipe cache, we may need another branch now.
+ pindexMostWork = nullptr;
+ }
+ pindexNewTip = chainActive.Tip();
- for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected()) {
- assert(trace.pblock && trace.pindex);
- GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs);
- }
- }
- // When we reach this point, we switched to a new tip (stored in pindexNewTip).
+ for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected()) {
+ assert(trace.pblock && trace.pindex);
+ GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs);
+ }
+ } while (!chainActive.Tip() || (starting_tip && CBlockIndexWorkComparator()(chainActive.Tip(), starting_tip)));
+ if (!blocks_connected) return true;
- // Notifications/callbacks that can run without cs_main
+ const CBlockIndex* pindexFork = chainActive.FindFork(starting_tip);
+ bool fInitialDownload = IsInitialBlockDownload();
- // Notify external listeners about the new tip.
- GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);
+ // Notify external listeners about the new tip.
+ // Enqueue while holding cs_main to ensure that UpdatedBlockTip is called in the order in which blocks are connected
+ if (pindexFork != pindexNewTip) {
+ // Notify ValidationInterface subscribers
+ GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);
- // Always notify the UI if a new block tip was connected
- if (pindexFork != pindexNewTip) {
- uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip);
+ // Always notify the UI if a new block tip was connected
+ uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip);
+ }
}
+ // When we reach this point, we switched to a new tip (stored in pindexNewTip).
if (nStopAtHeight && pindexNewTip && pindexNewTip->nHeight >= nStopAtHeight) StartShutdown();
@@ -2703,6 +2763,7 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
return true;
}
+
bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) {
return g_chainstate.ActivateBestChain(state, chainparams, std::move(pblock));
}
@@ -2778,7 +2839,7 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
pindex->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(pindex);
setBlockIndexCandidates.erase(pindex);
- g_failed_blocks.insert(pindex);
+ m_failed_blocks.insert(pindex);
// DisconnectTip will add transactions to disconnectpool; try to add these
// back to the mempool.
@@ -2802,11 +2863,12 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
}
return true;
}
+
bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) {
return g_chainstate.InvalidateBlock(state, chainparams, pindex);
}
-bool CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
+void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
AssertLockHeld(cs_main);
int nHeight = pindex->nHeight;
@@ -2824,7 +2886,7 @@ bool CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
// Reset invalid block marker if it was pointing to one of those.
pindexBestInvalid = nullptr;
}
- g_failed_blocks.erase(it->second);
+ m_failed_blocks.erase(it->second);
}
it++;
}
@@ -2834,12 +2896,13 @@ bool CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
if (pindex->nStatus & BLOCK_FAILED_MASK) {
pindex->nStatus &= ~BLOCK_FAILED_MASK;
setDirtyBlockIndex.insert(pindex);
+ m_failed_blocks.erase(pindex);
}
pindex = pindex->pprev;
}
- return true;
}
-bool ResetBlockFailureFlags(CBlockIndex *pindex) {
+
+void ResetBlockFailureFlags(CBlockIndex *pindex) {
return g_chainstate.ResetBlockFailureFlags(pindex);
}
@@ -2880,7 +2943,7 @@ CBlockIndex* CChainState::AddToBlockIndex(const CBlockHeader& block)
}
/** Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). */
-bool CChainState::ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
+void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams)
{
pindexNew->nTx = block.vtx.size();
pindexNew->nChainTx = 0;
@@ -2924,8 +2987,6 @@ bool CChainState::ReceivedBlockTransactions(const CBlock &block, CValidationStat
mapBlocksUnlinked.insert(std::make_pair(pindexNew->pprev, pindexNew));
}
}
-
- return true;
}
static bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false)
@@ -3094,6 +3155,12 @@ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& pa
return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == ThresholdState::ACTIVE);
}
+bool IsNullDummyEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params)
+{
+ LOCK(cs_main);
+ return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == ThresholdState::ACTIVE);
+}
+
// Compute at which vout of the block's coinbase transaction the witness
// commitment occurs, or -1 if not found.
static int GetWitnessCommitmentIndex(const CBlock& block)
@@ -3317,8 +3384,11 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState&
if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime()))
return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));
+ // If the previous block index isn't valid, determine if it descends from any block which
+ // has been found invalid (m_failed_blocks), then mark pindexPrev and any blocks
+ // between them as failed.
if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) {
- for (const CBlockIndex* failedit : g_failed_blocks) {
+ for (const CBlockIndex* failedit : m_failed_blocks) {
if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) {
assert(failedit->nStatus & BLOCK_FAILED_VALID);
CBlockIndex* invalid_walk = pindexPrev;
@@ -3428,7 +3498,6 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali
// request; don't process these.
if (pindex->nChainWork < nMinimumChainWork) return true;
}
- if (fNewBlock) *fNewBlock = true;
if (!CheckBlock(block, state, chainparams.GetConsensus()) ||
!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindex->pprev)) {
@@ -3445,14 +3514,14 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CVali
GetMainSignals().NewPoWValidBlock(pindex, pblock);
// Write block to history file
+ if (fNewBlock) *fNewBlock = true;
try {
CDiskBlockPos blockPos = SaveBlockToDisk(block, pindex->nHeight, chainparams, dbp);
if (blockPos.IsNull()) {
state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__));
return false;
}
- if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus()))
- return error("AcceptBlock(): ReceivedBlockTransactions failed");
+ ReceivedBlockTransactions(block, pindex, blockPos, chainparams.GetConsensus());
} catch (const std::runtime_error& e) {
return AbortNode(state, std::string("System error: ") + e.what());
}
@@ -3484,7 +3553,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
}
if (!ret) {
GetMainSignals().BlockChecked(*pblock, state);
- return error("%s: AcceptBlock FAILED (%s)", __func__, state.GetDebugMessage());
+ return error("%s: AcceptBlock FAILED (%s)", __func__, FormatStateMessage(state));
}
}
@@ -3492,7 +3561,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
CValidationState state; // Only used to report errors, not invalidity - ignore it
if (!g_chainstate.ActivateBestChain(state, chainparams, pblock))
- return error("%s: ActivateBestChain failed", __func__);
+ return error("%s: ActivateBestChain failed (%s)", __func__, FormatStateMessage(state));
return true;
}
@@ -3610,7 +3679,9 @@ void PruneBlockFilesManual(int nManualPruneHeight)
{
CValidationState state;
const CChainParams& chainparams = Params();
- FlushStateToDisk(chainparams, state, FlushStateMode::NONE, nManualPruneHeight);
+ if (!FlushStateToDisk(chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) {
+ LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state));
+ }
}
/**
@@ -3648,6 +3719,15 @@ static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfte
int count=0;
if (nCurrentUsage + nBuffer >= nPruneTarget) {
+ // On a prune event, the chainstate DB is flushed.
+ // To avoid excessive prune events negating the benefit of high dbcache
+ // values, we should not prune too rapidly.
+ // So when pruning in IBD, increase the buffer a bit to avoid a re-prune too soon.
+ if (IsInitialBlockDownload()) {
+ // Since this is only relevant during IBD, we use a fixed 10%
+ nBuffer += nPruneTarget / 10;
+ }
+
for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
nBytesToPrune = vinfoBlockFile[fileNumber].nSize + vinfoBlockFile[fileNumber].nUndoSize;
@@ -3745,7 +3825,7 @@ CBlockIndex * CChainState::InsertBlockIndex(const uint256& hash)
bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlockTreeDB& blocktree)
{
- if (!blocktree.LoadBlockIndexGuts(consensus_params, [this](const uint256& hash){ return this->InsertBlockIndex(hash); }))
+ if (!blocktree.LoadBlockIndexGuts(consensus_params, [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); }))
return false;
boost::this_thread::interruption_point();
@@ -3753,7 +3833,7 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
// Calculate nChainWork
std::vector<std::pair<int, CBlockIndex*> > vSortedByHeight;
vSortedByHeight.reserve(mapBlockIndex.size());
- for (const std::pair<uint256, CBlockIndex*>& item : mapBlockIndex)
+ for (const std::pair<const uint256, CBlockIndex*>& item : mapBlockIndex)
{
CBlockIndex* pindex = item.second;
vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex));
@@ -3795,7 +3875,7 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
return true;
}
-bool static LoadBlockIndexDB(const CChainParams& chainparams)
+bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
if (!g_chainstate.LoadBlockIndex(chainparams.GetConsensus(), *pblocktree))
return false;
@@ -3820,7 +3900,7 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams)
// Check presence of blk files
LogPrintf("Checking all blk files are present...\n");
std::set<int> setBlkDataFiles;
- for (const std::pair<uint256, CBlockIndex*>& item : mapBlockIndex)
+ for (const std::pair<const uint256, CBlockIndex*>& item : mapBlockIndex)
{
CBlockIndex* pindex = item.second;
if (pindex->nStatus & BLOCK_HAVE_DATA) {
@@ -3845,10 +3925,6 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams)
pblocktree->ReadReindexing(fReindexing);
if(fReindexing) fReindex = true;
- // Check whether we have a transaction index
- pblocktree->ReadFlag("txindex", fTxIndex);
- LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled");
-
return true;
}
@@ -3864,6 +3940,7 @@ bool LoadChainTip(const CChainParams& chainparams)
LogPrintf("%s: Connecting genesis block...\n", __func__);
CValidationState state;
if (!ActivateBestChain(state, chainparams)) {
+ LogPrintf("%s: failed to activate chain (%s)\n", __func__, FormatStateMessage(state));
return false;
}
}
@@ -3906,14 +3983,13 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
nCheckLevel = std::max(0, std::min(4, nCheckLevel));
LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel);
CCoinsViewCache coins(coinsview);
- CBlockIndex* pindexState = chainActive.Tip();
+ CBlockIndex* pindex;
CBlockIndex* pindexFailure = nullptr;
int nGoodTransactions = 0;
CValidationState state;
int reportDone = 0;
LogPrintf("[0%%]..."); /* Continued */
- for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev)
- {
+ for (pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
boost::this_thread::interruption_point();
int percentageDone = std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));
if (reportDone < percentageDone/10) {
@@ -3922,7 +3998,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
reportDone = percentageDone/10;
}
uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone, false);
- if (pindex->nHeight < chainActive.Height()-nCheckDepth)
+ if (pindex->nHeight <= chainActive.Height()-nCheckDepth)
break;
if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) {
// If pruning, only go back as far as we have data.
@@ -3947,13 +4023,12 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
}
}
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
- if (nCheckLevel >= 3 && pindex == pindexState && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) {
+ if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) {
assert(coins.GetBestBlock() == pindex->GetBlockHash());
DisconnectResult res = g_chainstate.DisconnectBlock(block, pindex, coins);
if (res == DISCONNECT_FAILED) {
return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
}
- pindexState = pindex->pprev;
if (res == DISCONNECT_UNCLEAN) {
nGoodTransactions = 0;
pindexFailure = pindex;
@@ -3967,9 +4042,11 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
if (pindexFailure)
return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", chainActive.Height() - pindexFailure->nHeight + 1, nGoodTransactions);
+ // store block count as we move pindex at check level >= 4
+ int block_count = chainActive.Height() - pindex->nHeight;
+
// check level 4: try reconnecting blocks
if (nCheckLevel >= 4) {
- CBlockIndex *pindex = pindexState;
while (pindex != chainActive.Tip()) {
boost::this_thread::interruption_point();
uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50))), false);
@@ -3978,12 +4055,12 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
if (!g_chainstate.ConnectBlock(block, state, pindex, coins, chainparams))
- return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
+ return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state));
}
}
LogPrintf("[DONE].\n");
- LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", chainActive.Height() - pindexState->nHeight, nGoodTransactions);
+ LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", block_count, nGoodTransactions);
return true;
}
@@ -4086,6 +4163,9 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
int nHeight = 1;
while (nHeight <= chainActive.Height()) {
+ // Although SCRIPT_VERIFY_WITNESS is now generally enforced on all
+ // blocks in ConnectBlock, we don't need to go back and
+ // re-download/re-verify blocks from before segwit actually activated.
if (IsWitnessEnabled(chainActive[nHeight - 1], params.GetConsensus()) && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) {
break;
}
@@ -4105,11 +4185,13 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
break;
}
if (!DisconnectTip(state, params, nullptr)) {
- return error("RewindBlockIndex: unable to disconnect block at height %i", pindex->nHeight);
+ return error("RewindBlockIndex: unable to disconnect block at height %i (%s)", pindex->nHeight, FormatStateMessage(state));
}
// Occasionally flush state to disk.
- if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC))
+ if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) {
+ LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state));
return false;
+ }
}
// Reduce validity flag and have-data flags.
@@ -4175,6 +4257,7 @@ bool RewindBlockIndex(const CChainParams& params) {
// it'll get called a bunch real soon.
CValidationState state;
if (!FlushStateToDisk(params, state, FlushStateMode::ALWAYS)) {
+ LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state));
return false;
}
}
@@ -4184,7 +4267,7 @@ bool RewindBlockIndex(const CChainParams& params) {
void CChainState::UnloadBlockIndex() {
nBlockSequenceId = 1;
- g_failed_blocks.clear();
+ m_failed_blocks.clear();
setBlockIndexCandidates.clear();
}
@@ -4235,9 +4318,6 @@ bool LoadBlockIndex(const CChainParams& chainparams)
// needs_init.
LogPrintf("Initializing databases...\n");
- // Use the provided setting for -txindex in the new database
- fTxIndex = gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX);
- pblocktree->WriteFlag("txindex", fTxIndex);
}
return true;
}
@@ -4259,9 +4339,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
if (blockPos.IsNull())
return error("%s: writing genesis block to disk failed", __func__);
CBlockIndex *pindex = AddToBlockIndex(block);
- CValidationState state;
- if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus()))
- return error("%s: genesis block not accepted", __func__);
+ ReceivedBlockTransactions(block, pindex, blockPos, chainparams.GetConsensus());
} catch (const std::runtime_error& e) {
return error("%s: failed to write genesis block: %s", __func__, e.what());
}
@@ -4726,7 +4804,8 @@ bool DumpMempool(void)
}
file << mapDeltas;
- FileCommit(file.Get());
+ if (!FileCommit(file.Get()))
+ throw std::runtime_error("FileCommit failed");
file.fclose();
RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat");
int64_t last = GetTimeMicros();