aboutsummaryrefslogtreecommitdiff
path: root/src/validation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/validation.cpp')
-rw-r--r--src/validation.cpp86
1 files changed, 54 insertions, 32 deletions
diff --git a/src/validation.cpp b/src/validation.cpp
index 317e7d44e6..00d7a60cb7 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -35,9 +35,9 @@
#include <txmempool.h>
#include <ui_interface.h>
#include <undo.h>
-#include <util.h>
-#include <utilmoneystr.h>
-#include <utilstrencodings.h>
+#include <util/system.h>
+#include <util/moneystr.h>
+#include <util/strencodings.h>
#include <validationinterface.h>
#include <warnings.h>
@@ -361,10 +361,10 @@ bool TestLockPointValidity(const LockPoints* lp)
return true;
}
-bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool useExistingLockPoints)
+bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flags, LockPoints* lp, bool useExistingLockPoints)
{
AssertLockHeld(cs_main);
- AssertLockHeld(mempool.cs);
+ AssertLockHeld(pool.cs);
CBlockIndex* tip = chainActive.Tip();
assert(tip != nullptr);
@@ -387,7 +387,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool
}
else {
// pcoinsTip contains the UTXO set for chainActive.Tip()
- CCoinsViewMemPool viewMemPool(pcoinsTip.get(), mempool);
+ CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool);
std::vector<int> prevheights;
prevheights.resize(tx.vin.size());
for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
@@ -679,7 +679,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// be mined yet.
// Must keep pool.cs for this unless we change CheckSequenceLocks to take a
// CoinsViewCache instead of create its own
- if (!CheckSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
+ if (!CheckSequenceLocks(pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final");
CAmount nFees = 0;
@@ -918,7 +918,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// There is a similar check in CreateNewBlock() to prevent creating
// invalid blocks (using TestBlockValidity), however allowing such
// transactions into the mempool can be exploited as a DoS attack.
- unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(chainActive.Tip(), Params().GetConsensus());
+ unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(chainActive.Tip(), chainparams.GetConsensus());
if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, currentBlockScriptVerifyFlags, true, txdata)) {
return error("%s: BUG! PLEASE REPORT THIS! CheckInputs failed against latest-block but not STANDARD flags %s, %s",
__func__, hash.ToString(), FormatStateMessage(state));
@@ -1680,8 +1680,7 @@ void ThreadScriptCheck() {
scriptcheckqueue.Thread();
}
-// Protected by cs_main
-VersionBitsCache versionbitscache;
+VersionBitsCache versionbitscache GUARDED_BY(cs_main);
int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params)
{
@@ -1722,8 +1721,7 @@ public:
}
};
-// Protected by cs_main
-static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS];
+static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS] GUARDED_BY(cs_main);
// 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
@@ -2486,7 +2484,7 @@ CBlockIndex* CChainState::FindMostWorkChain() {
CBlockIndex *pindexTest = pindexNew;
bool fInvalidAncestor = false;
while (pindexTest && !chainActive.Contains(pindexTest)) {
- assert(pindexTest->nChainTx || pindexTest->nHeight == 0);
+ assert(pindexTest->HaveTxsDownloaded() || pindexTest->nHeight == 0);
// Pruned nodes may have entries in setBlockIndexCandidates for
// which block files have been deleted. Remove those as candidates
@@ -2776,7 +2774,7 @@ bool CChainState::PreciousBlock(CValidationState& state, const CChainParams& par
// call preciousblock 2**31-1 times on the same set of tips...
nBlockReverseSequenceId--;
}
- if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && pindex->nChainTx) {
+ if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && pindex->HaveTxsDownloaded()) {
setBlockIndexCandidates.insert(pindex);
PruneBlockIndexCandidates();
}
@@ -2837,7 +2835,7 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
// add it again.
BlockMap::iterator it = mapBlockIndex.begin();
while (it != mapBlockIndex.end()) {
- if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) {
+ if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) {
setBlockIndexCandidates.insert(it->second);
}
it++;
@@ -2867,7 +2865,7 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
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)) {
+ if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) {
setBlockIndexCandidates.insert(it->second);
}
if (it->second == pindexBestInvalid) {
@@ -2945,7 +2943,7 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi
pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS);
setDirtyBlockIndex.insert(pindexNew);
- if (pindexNew->pprev == nullptr || pindexNew->pprev->nChainTx) {
+ if (pindexNew->pprev == nullptr || pindexNew->pprev->HaveTxsDownloaded()) {
// If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS.
std::deque<CBlockIndex*> queue;
queue.push_back(pindexNew);
@@ -3266,6 +3264,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c
// Start enforcing BIP113 (Median Time Past) using versionbits logic.
int nLockTimeFlags = 0;
if (VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_CSV, versionbitscache) == ThresholdState::ACTIVE) {
+ assert(pindexPrev != nullptr);
nLockTimeFlags |= LOCKTIME_MEDIAN_TIME_PAST;
}
@@ -3372,10 +3371,30 @@ 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.
+ /* Determine if this block descends from any block which has been found
+ * invalid (m_failed_blocks), then mark pindexPrev and any blocks between
+ * them as failed. For example:
+ *
+ * D3
+ * /
+ * B2 - C2
+ * / \
+ * A D2 - E2 - F2
+ * \
+ * B1 - C1 - D1 - E1
+ *
+ * In the case that we attempted to reorg from E1 to F2, only to find
+ * C2 to be invalid, we would mark D2, E2, and F2 as BLOCK_FAILED_CHILD
+ * but NOT D3 (it was not in any of our candidate sets at the time).
+ *
+ * In any case D3 will also be marked as BLOCK_FAILED_CHILD at restart
+ * in LoadBlockIndex.
+ */
if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) {
+ // The above does not mean "invalid": it checks if the previous block
+ // hasn't been validated up to BLOCK_VALID_SCRIPTS. This is a performance
+ // optimization, in the common case of adding a new block to the tip,
+ // we don't need to iterate over the failed blocks list.
for (const CBlockIndex* failedit : m_failed_blocks) {
if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) {
assert(failedit->nStatus & BLOCK_FAILED_VALID);
@@ -3529,12 +3548,14 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
CBlockIndex *pindex = nullptr;
if (fNewBlock) *fNewBlock = false;
CValidationState state;
- // Ensure that CheckBlock() passes before calling AcceptBlock, as
- // belt-and-suspenders.
- bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus());
+ // CheckBlock() does not support multi-threaded block validation because CBlock::fChecked can cause data race.
+ // Therefore, the following critical section must include the CheckBlock() call as well.
LOCK(cs_main);
+ // Ensure that CheckBlock() passes before calling AcceptBlock, as
+ // belt-and-suspenders.
+ bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus());
if (ret) {
// Store to disk
ret = g_chainstate.AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock);
@@ -3816,8 +3837,6 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
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();
-
// Calculate nChainWork
std::vector<std::pair<int, CBlockIndex*> > vSortedByHeight;
vSortedByHeight.reserve(mapBlockIndex.size());
@@ -3836,7 +3855,7 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
// Pruned nodes may have deleted the block.
if (pindex->nTx > 0) {
if (pindex->pprev) {
- if (pindex->pprev->nChainTx) {
+ if (pindex->pprev->HaveTxsDownloaded()) {
pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx;
} else {
pindex->nChainTx = 0;
@@ -3850,7 +3869,7 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
pindex->nStatus |= BLOCK_FAILED_CHILD;
setDirtyBlockIndex.insert(pindex);
}
- if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == nullptr))
+ if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))
setBlockIndexCandidates.insert(pindex);
if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork))
pindexBestInvalid = pindex;
@@ -4219,7 +4238,7 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
++ret.first;
}
}
- } else if (pindexIter->IsValid(BLOCK_VALID_TRANSACTIONS) && pindexIter->nChainTx) {
+ } else if (pindexIter->IsValid(BLOCK_VALID_TRANSACTIONS) && pindexIter->HaveTxsDownloaded()) {
setBlockIndexCandidates.insert(pindexIter);
}
}
@@ -4520,7 +4539,7 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
assert(pindex->GetBlockHash() == consensusParams.hashGenesisBlock); // Genesis block's hash must match.
assert(pindex == chainActive.Genesis()); // The current active chain's genesis block must be this block.
}
- if (pindex->nChainTx == 0) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
+ if (!pindex->HaveTxsDownloaded()) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
// VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred).
// HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred.
if (!fHavePruned) {
@@ -4533,9 +4552,9 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
}
if (pindex->nStatus & BLOCK_HAVE_UNDO) assert(pindex->nStatus & BLOCK_HAVE_DATA);
assert(((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS) == (pindex->nTx > 0)); // This is pruning-independent.
- // All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to nChainTx being set.
- assert((pindexFirstNeverProcessed != nullptr) == (pindex->nChainTx == 0)); // nChainTx != 0 is used to signal that all parent blocks have been processed (but may have been pruned).
- assert((pindexFirstNotTransactionsValid != nullptr) == (pindex->nChainTx == 0));
+ // All parents having had data (at some point) is equivalent to all parents being VALID_TRANSACTIONS, which is equivalent to HaveTxsDownloaded().
+ assert((pindexFirstNeverProcessed == nullptr) == pindex->HaveTxsDownloaded());
+ assert((pindexFirstNotTransactionsValid == nullptr) == pindex->HaveTxsDownloaded());
assert(pindex->nHeight == nHeight); // nHeight must be consistent.
assert(pindex->pprev == nullptr || pindex->nChainWork >= pindex->pprev->nChainWork); // For every block except the genesis block, the chainwork must be larger than the parent's.
assert(nHeight < 2 || (pindex->pskip && (pindex->pskip->nHeight < nHeight))); // The pskip pointer must point back for all but the first 2 blocks.
@@ -4763,6 +4782,9 @@ bool DumpMempool()
std::map<uint256, CAmount> mapDeltas;
std::vector<TxMempoolInfo> vinfo;
+ static Mutex dump_mutex;
+ LOCK(dump_mutex);
+
{
LOCK(mempool.cs);
for (const auto &i : mempool.mapDeltas) {