aboutsummaryrefslogtreecommitdiff
path: root/src/validation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/validation.cpp')
-rw-r--r--src/validation.cpp675
1 files changed, 191 insertions, 484 deletions
diff --git a/src/validation.cpp b/src/validation.cpp
index eb46741ba0..2813b62462 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2020 The Bitcoin Core developers
+// Copyright (c) 2009-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -62,6 +62,25 @@
#include <boost/algorithm/string/replace.hpp>
+using node::BLOCKFILE_CHUNK_SIZE;
+using node::BlockManager;
+using node::BlockMap;
+using node::CBlockIndexWorkComparator;
+using node::CCoinsStats;
+using node::CoinStatsHashType;
+using node::GetUTXOStats;
+using node::OpenBlockFile;
+using node::ReadBlockFromDisk;
+using node::SnapshotMetadata;
+using node::UNDOFILE_CHUNK_SIZE;
+using node::UndoReadFromDisk;
+using node::UnlinkPrunedFiles;
+using node::fHavePruned;
+using node::fImporting;
+using node::fPruneMode;
+using node::fReindex;
+using node::nPruneTarget;
+
#define MICRO 0.000001
#define MILLI 0.001
@@ -133,23 +152,6 @@ arith_uint256 nMinimumChainWork;
CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
-// Internal stuff from blockstorage ...
-extern RecursiveMutex cs_LastBlockFile;
-extern std::vector<CBlockFileInfo> vinfoBlockFile;
-extern int nLastBlockFile;
-extern bool fCheckForPruning;
-extern std::set<CBlockIndex*> setDirtyBlockIndex;
-extern std::set<int> setDirtyFileInfo;
-void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false);
-// ... TODO move fully to blockstorage
-
-CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash) const
-{
- AssertLockHeld(cs_main);
- BlockMap::const_iterator it = m_block_index.find(hash);
- return it == m_block_index.end() ? nullptr : it->second;
-}
-
CBlockIndex* CChainState::FindForkInGlobalIndex(const CBlockLocator& locator) const
{
AssertLockHeld(cs_main);
@@ -284,8 +286,10 @@ bool CheckSequenceLocks(CBlockIndex* tip,
static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& chainparams);
static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache, size_t limit, std::chrono::seconds age)
- EXCLUSIVE_LOCKS_REQUIRED(pool.cs, ::cs_main)
+ EXCLUSIVE_LOCKS_REQUIRED(::cs_main, pool.cs)
{
+ AssertLockHeld(::cs_main);
+ AssertLockHeld(pool.cs);
int expired = pool.Expire(GetTime<std::chrono::seconds>() - age);
if (expired != 0) {
LogPrint(BCLog::MEMPOOL, "Expired %i transactions from the memory pool\n", expired);
@@ -345,41 +349,59 @@ void CChainState::MaybeUpdateMempoolForReorg(
// previously-confirmed transactions back to the mempool.
// UpdateTransactionsFromBlock finds descendants of any transactions in
// the disconnectpool that were added back and cleans up the mempool state.
- m_mempool->UpdateTransactionsFromBlock(vHashUpdate);
-
- const auto check_final_and_mature = [this, flags=STANDARD_LOCKTIME_VERIFY_FLAGS](CTxMemPool::txiter it)
+ const uint64_t ancestor_count_limit = gArgs.GetIntArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
+ const uint64_t ancestor_size_limit = gArgs.GetIntArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000;
+ m_mempool->UpdateTransactionsFromBlock(vHashUpdate, ancestor_size_limit, ancestor_count_limit);
+
+ // Predicate to use for filtering transactions in removeForReorg.
+ // Checks whether the transaction is still final and, if it spends a coinbase output, mature.
+ // Also updates valid entries' cached LockPoints if needed.
+ // If false, the tx is still valid and its lockpoints are updated.
+ // If true, the tx would be invalid in the next block; remove this entry and all of its descendants.
+ const auto filter_final_and_mature = [this, flags=STANDARD_LOCKTIME_VERIFY_FLAGS](CTxMemPool::txiter it)
EXCLUSIVE_LOCKS_REQUIRED(m_mempool->cs, ::cs_main) {
- bool should_remove = false;
AssertLockHeld(m_mempool->cs);
AssertLockHeld(::cs_main);
const CTransaction& tx = it->GetTx();
+
+ // The transaction must be final.
+ if (!CheckFinalTx(m_chain.Tip(), tx, flags)) return true;
LockPoints lp = it->GetLockPoints();
const bool validLP{TestLockPointValidity(m_chain, lp)};
CCoinsViewMemPool view_mempool(&CoinsTip(), *m_mempool);
- if (!CheckFinalTx(m_chain.Tip(), tx, flags)
- || !CheckSequenceLocks(m_chain.Tip(), view_mempool, tx, flags, &lp, validLP)) {
- // Note if CheckSequenceLocks fails the LockPoints may still be invalid
- // So it's critical that we remove the tx and not depend on the LockPoints.
- should_remove = true;
- } else if (it->GetSpendsCoinbase()) {
+ // CheckSequenceLocks checks if the transaction will be final in the next block to be
+ // created on top of the new chain. We use useExistingLockPoints=false so that, instead of
+ // using the information in lp (which might now refer to a block that no longer exists in
+ // the chain), it will update lp to contain LockPoints relevant to the new chain.
+ if (!CheckSequenceLocks(m_chain.Tip(), view_mempool, tx, flags, &lp, validLP)) {
+ // If CheckSequenceLocks fails, remove the tx and don't depend on the LockPoints.
+ return true;
+ } else if (!validLP) {
+ // If CheckSequenceLocks succeeded, it also updated the LockPoints.
+ // Now update the mempool entry lockpoints as well.
+ m_mempool->mapTx.modify(it, [&lp](CTxMemPoolEntry& e) { e.UpdateLockPoints(lp); });
+ }
+
+ // If the transaction spends any coinbase outputs, it must be mature.
+ if (it->GetSpendsCoinbase()) {
for (const CTxIn& txin : tx.vin) {
auto it2 = m_mempool->mapTx.find(txin.prevout.hash);
if (it2 != m_mempool->mapTx.end())
continue;
- const Coin &coin = CoinsTip().AccessCoin(txin.prevout);
+ const Coin& coin{CoinsTip().AccessCoin(txin.prevout)};
assert(!coin.IsSpent());
const auto mempool_spend_height{m_chain.Tip()->nHeight + 1};
- if (coin.IsSpent() || (coin.IsCoinBase() && mempool_spend_height - coin.nHeight < COINBASE_MATURITY)) {
- should_remove = true;
- break;
+ if (coin.IsCoinBase() && mempool_spend_height - coin.nHeight < COINBASE_MATURITY) {
+ return true;
}
}
}
- return should_remove;
+ // Transaction is still valid and cached LockPoints are updated.
+ return false;
};
// We also need to remove any now-immature transactions
- m_mempool->removeForReorg(m_chain, check_final_and_mature);
+ m_mempool->removeForReorg(m_chain, filter_final_and_mature);
// Re-limit mempool size, in case we added any transactions
LimitMempoolSize(
*m_mempool,
@@ -601,15 +623,17 @@ private:
// Submit all transactions to the mempool and call ConsensusScriptChecks to add to the script
// cache - should only be called after successful validation of all transactions in the package.
- // The package may end up partially-submitted after size limitting; returns true if all
+ // The package may end up partially-submitted after size limiting; returns true if all
// transactions are successfully added to the mempool, false otherwise.
- bool FinalizePackage(const ATMPArgs& args, std::vector<Workspace>& workspaces, PackageValidationState& package_state,
- std::map<const uint256, const MempoolAcceptResult>& results)
+ bool SubmitPackage(const ATMPArgs& args, std::vector<Workspace>& workspaces, PackageValidationState& package_state,
+ std::map<const uint256, const MempoolAcceptResult>& results)
EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
// Compare a package's feerate against minimum allowed.
- bool CheckFeeRate(size_t package_size, CAmount package_fee, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs)
+ bool CheckFeeRate(size_t package_size, CAmount package_fee, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_pool.cs)
{
+ AssertLockHeld(::cs_main);
+ AssertLockHeld(m_pool.cs);
CAmount mempoolRejectFee = m_pool.GetMinFee(gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(package_size);
if (mempoolRejectFee > 0 && package_fee < mempoolRejectFee) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool min fee not met", strprintf("%d < %d", package_fee, mempoolRejectFee));
@@ -643,6 +667,8 @@ private:
bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
{
+ AssertLockHeld(cs_main);
+ AssertLockHeld(m_pool.cs);
const CTransactionRef& ptx = ws.m_ptx;
const CTransaction& tx = *ws.m_ptx;
const uint256& hash = ws.m_hash;
@@ -943,6 +969,8 @@ bool MemPoolAccept::PackageMempoolChecks(const std::vector<CTransactionRef>& txn
bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws)
{
+ AssertLockHeld(cs_main);
+ AssertLockHeld(m_pool.cs);
const CTransaction& tx = *ws.m_ptx;
TxValidationState& state = ws.m_state;
@@ -969,6 +997,8 @@ bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws)
bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws)
{
+ AssertLockHeld(cs_main);
+ AssertLockHeld(m_pool.cs);
const CTransaction& tx = *ws.m_ptx;
const uint256& hash = ws.m_hash;
TxValidationState& state = ws.m_state;
@@ -1001,6 +1031,8 @@ bool MemPoolAccept::ConsensusScriptChecks(const ATMPArgs& args, Workspace& ws)
bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
{
+ AssertLockHeld(cs_main);
+ AssertLockHeld(m_pool.cs);
const CTransaction& tx = *ws.m_ptx;
const uint256& hash = ws.m_hash;
TxValidationState& state = ws.m_state;
@@ -1043,12 +1075,17 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
return true;
}
-bool MemPoolAccept::FinalizePackage(const ATMPArgs& args, std::vector<Workspace>& workspaces,
- PackageValidationState& package_state,
- std::map<const uint256, const MempoolAcceptResult>& results)
+bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>& workspaces,
+ PackageValidationState& package_state,
+ std::map<const uint256, const MempoolAcceptResult>& results)
{
AssertLockHeld(cs_main);
AssertLockHeld(m_pool.cs);
+ // Sanity check: none of the transactions should be in the mempool, and none of the transactions
+ // should have a same-txid-different-witness equivalent in the mempool.
+ assert(std::all_of(workspaces.cbegin(), workspaces.cend(), [this](const auto& ws){
+ return !m_pool.exists(GenTxid::Txid(ws.m_ptx->GetHash())); }));
+
bool all_submitted = true;
// ConsensusScriptChecks adds to the script cache and is therefore consensus-critical;
// CheckInputsFromMempoolAndCache asserts that transactions only spend coins available from the
@@ -1058,18 +1095,24 @@ bool MemPoolAccept::FinalizePackage(const ATMPArgs& args, std::vector<Workspace>
if (!ConsensusScriptChecks(args, ws)) {
results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
// Since PolicyScriptChecks() passed, this should never fail.
- all_submitted = Assume(false);
+ all_submitted = false;
+ package_state.Invalid(PackageValidationResult::PCKG_MEMPOOL_ERROR,
+ strprintf("BUG! PolicyScriptChecks succeeded but ConsensusScriptChecks failed: %s",
+ ws.m_ptx->GetHash().ToString()));
}
// Re-calculate mempool ancestors to call addUnchecked(). They may have changed since the
// last calculation done in PreChecks, since package ancestors have already been submitted.
- std::string err_string;
+ std::string unused_err_string;
if(!m_pool.CalculateMemPoolAncestors(*ws.m_entry, ws.m_ancestors, m_limit_ancestors,
m_limit_ancestor_size, m_limit_descendants,
- m_limit_descendant_size, err_string)) {
+ m_limit_descendant_size, unused_err_string)) {
results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
// Since PreChecks() and PackageMempoolChecks() both enforce limits, this should never fail.
- all_submitted = Assume(false);
+ all_submitted = false;
+ package_state.Invalid(PackageValidationResult::PCKG_MEMPOOL_ERROR,
+ strprintf("BUG! Mempool ancestors or descendants were underestimated: %s",
+ ws.m_ptx->GetHash().ToString()));
}
// If we call LimitMempoolSize() for each individual Finalize(), the mempool will not take
// the transaction's descendant feerate into account because it hasn't seen them yet. Also,
@@ -1079,7 +1122,9 @@ bool MemPoolAccept::FinalizePackage(const ATMPArgs& args, std::vector<Workspace>
if (!Finalize(args, ws)) {
results.emplace(ws.m_ptx->GetWitnessHash(), MempoolAcceptResult::Failure(ws.m_state));
// Since LimitMempoolSize() won't be called, this should never fail.
- all_submitted = Assume(false);
+ all_submitted = false;
+ package_state.Invalid(PackageValidationResult::PCKG_MEMPOOL_ERROR,
+ strprintf("BUG! Adding to mempool failed: %s", ws.m_ptx->GetHash().ToString()));
}
}
@@ -1088,7 +1133,6 @@ bool MemPoolAccept::FinalizePackage(const ATMPArgs& args, std::vector<Workspace>
LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip(),
gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000,
std::chrono::hours{gArgs.GetIntArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
- if (!all_submitted) return false;
// Find the wtxids of the transactions that made it into the mempool. Allow partial submission,
// but don't report success unless they all made it into the mempool.
@@ -1193,8 +1237,8 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
if (args.m_test_accept) return PackageMempoolAcceptResult(package_state, std::move(results));
- if (!FinalizePackage(args, workspaces, package_state, results)) {
- package_state.Invalid(PackageValidationResult::PCKG_TX, "submission failed");
+ if (!SubmitPackage(args, workspaces, package_state, results)) {
+ // PackageValidationState filled in by SubmitPackage().
return PackageMempoolAcceptResult(package_state, std::move(results));
}
@@ -1219,11 +1263,13 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
return PackageMempoolAcceptResult(package_state, {});
}
- const auto& child = package[package.size() - 1];
+ // IsChildWithParents() guarantees the package is > 1 transactions.
+ assert(package.size() > 1);
// The package must be 1 child with all of its unconfirmed parents. The package is expected to
// be sorted, so the last transaction is the child.
+ const auto& child = package.back();
std::unordered_set<uint256, SaltedTxidHasher> unconfirmed_parent_txids;
- std::transform(package.cbegin(), package.end() - 1,
+ std::transform(package.cbegin(), package.cend() - 1,
std::inserter(unconfirmed_parent_txids, unconfirmed_parent_txids.end()),
[](const auto& tx) { return tx->GetHash(); });
@@ -1255,10 +1301,14 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
LOCK(m_pool.cs);
std::map<const uint256, const MempoolAcceptResult> results;
- // As node operators are free to set their mempool policies however they please, it's possible
- // for package transaction(s) to already be in the mempool, and we don't want to reject the
- // entire package in that case (as that could be a censorship vector). Filter the transactions
- // that are already in mempool and add their information to results, since we already have them.
+ // Node operators are free to set their mempool policies however they please, nodes may receive
+ // transactions in different orders, and malicious counterparties may try to take advantage of
+ // policy differences to pin or delay propagation of transactions. As such, it's possible for
+ // some package transaction(s) to already be in the mempool, and we don't want to reject the
+ // entire package in that case (as that could be a censorship vector). De-duplicate the
+ // transactions that are already in the mempool, and only call AcceptMultipleTransactions() with
+ // the new transactions. This ensures we don't double-count transaction counts and sizes when
+ // checking ancestor/descendant limits, or double-count transaction fees for fee-related policy.
std::vector<CTransactionRef> txns_new;
for (const auto& tx : package) {
const auto& wtxid = tx->GetWitnessHash();
@@ -1279,9 +1329,10 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
// transaction for the mempool one. Note that we are ignoring the validity of the
// package transaction passed in.
// TODO: allow witness replacement in packages.
- auto iter = m_pool.GetIter(wtxid);
+ auto iter = m_pool.GetIter(txid);
assert(iter != std::nullopt);
- results.emplace(txid, MempoolAcceptResult::MempoolTx(iter.value()->GetTxSize(), iter.value()->GetFee()));
+ // Provide the wtxid of the mempool tx so that the caller can look it up in the mempool.
+ results.emplace(wtxid, MempoolAcceptResult::MempoolTxDifferentWitness(iter.value()->GetTx().GetWitnessHash()));
} else {
// Transaction does not already exist in the mempool.
txns_new.push_back(tx);
@@ -1303,8 +1354,9 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, const CTransactionRef& tx,
int64_t accept_time, bool bypass_limits, bool test_accept)
- EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+ EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
+ AssertLockHeld(::cs_main);
const CChainParams& chainparams{active_chainstate.m_params};
assert(active_chainstate.GetMempool() != nullptr);
CTxMemPool& pool{*active_chainstate.GetMempool()};
@@ -1348,12 +1400,12 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
}();
// Uncache coins pertaining to transactions that were not submitted to the mempool.
- // Ensure the coins cache is still within limits.
if (test_accept || result.m_state.IsInvalid()) {
for (const COutPoint& hashTx : coins_to_uncache) {
active_chainstate.CoinsTip().Uncache(hashTx);
}
}
+ // Ensure the coins cache is still within limits.
BlockValidationState state_dummy;
active_chainstate.FlushStateToDisk(state_dummy, FlushStateMode::PERIODIC);
return result;
@@ -1382,6 +1434,7 @@ CoinsViews::CoinsViews(
void CoinsViews::InitCache()
{
+ AssertLockHeld(::cs_main);
m_cacheview = std::make_unique<CCoinsViewCache>(&m_catcherview);
}
@@ -1412,6 +1465,7 @@ void CChainState::InitCoinsDB(
void CChainState::InitCoinsCache(size_t cache_size_bytes)
{
+ AssertLockHeld(::cs_main);
assert(m_coins_views != nullptr);
m_coinstip_cache_size_bytes = cache_size_bytes;
m_coins_views->InitCache();
@@ -1485,6 +1539,7 @@ void CChainState::CheckForkWarningConditions()
// Called both upon regular invalid block discovery *and* InvalidateBlock
void CChainState::InvalidChainFound(CBlockIndex* pindexNew)
{
+ AssertLockHeld(cs_main);
if (!m_chainman.m_best_invalid || pindexNew->nChainWork > m_chainman.m_best_invalid->nChainWork) {
m_chainman.m_best_invalid = pindexNew;
}
@@ -1507,10 +1562,11 @@ void CChainState::InvalidChainFound(CBlockIndex* pindexNew)
// which does its own setBlockIndexCandidates management.
void CChainState::InvalidBlockFound(CBlockIndex* pindex, const BlockValidationState& state)
{
+ AssertLockHeld(cs_main);
if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
pindex->nStatus |= BLOCK_FAILED_VALID;
m_chainman.m_failed_blocks.insert(pindex);
- setDirtyBlockIndex.insert(pindex);
+ m_blockman.m_dirty_blockindex.insert(pindex);
setBlockIndexCandidates.erase(pindex);
InvalidChainFound(pindex);
}
@@ -1748,8 +1804,9 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
error("DisconnectBlock(): transaction and undo data inconsistent");
return DISCONNECT_FAILED;
}
- for (unsigned int j = tx.vin.size(); j-- > 0;) {
- const COutPoint &out = tx.vin[j].prevout;
+ for (unsigned int j = tx.vin.size(); j > 0;) {
+ --j;
+ const COutPoint& out = tx.vin[j].prevout;
int res = ApplyTxInUndo(std::move(txundo.vprevout[j]), view, out);
if (res == DISCONNECT_FAILED) return DISCONNECT_FAILED;
fClean = fClean && res != DISCONNECT_UNCLEAN;
@@ -1866,7 +1923,10 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
{
AssertLockHeld(cs_main);
assert(pindex);
- assert(*pindex->phashBlock == block.GetHash());
+
+ uint256 block_hash{block.GetHash()};
+ assert(*pindex->phashBlock == block_hash);
+
int64_t nTimeStart = GetTimeMicros();
// Check it again in case a previous version let a bad block in
@@ -1900,7 +1960,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// Special case for the genesis block, skipping connection of its transactions
// (its coinbase is unspendable)
- if (block.GetHash() == m_params.GetConsensus().hashGenesisBlock) {
+ if (block_hash == m_params.GetConsensus().hashGenesisBlock) {
if (!fJustCheck)
view.SetBestBlock(pindex->GetBlockHash());
return true;
@@ -2006,9 +2066,8 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// another edge case to deal with.
// testnet3 has no blocks before the BIP34 height with indicated heights
- // post BIP34 before approximately height 486,000,000 and presumably will
- // be reset before it reaches block 1,983,702 and starts doing unnecessary
- // BIP30 checking again.
+ // post BIP34 before approximately height 486,000,000. After block
+ // 1,983,702 testnet3 starts doing unnecessary BIP30 checking again.
assert(pindex->pprev);
CBlockIndex* pindexBIP34height = pindex->pprev->GetAncestor(m_params.GetConsensus().BIP34Height);
//Only continue to enforce if we're below BIP34 activation height or the block hash at that height doesn't correspond.
@@ -2141,13 +2200,13 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
if (fJustCheck)
return true;
- if (!WriteUndoDataForBlock(blockundo, state, pindex, m_params)) {
+ if (!m_blockman.WriteUndoDataForBlock(blockundo, state, pindex, m_params)) {
return false;
}
if (!pindex->IsValid(BLOCK_VALID_SCRIPTS)) {
pindex->RaiseValidity(BLOCK_VALID_SCRIPTS);
- setDirtyBlockIndex.insert(pindex);
+ m_blockman.m_dirty_blockindex.insert(pindex);
}
assert(pindex->phashBlock);
@@ -2158,12 +2217,12 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime5 - nTime4), nTimeIndex * MICRO, nTimeIndex * MILLI / nBlocksTotal);
TRACE6(validation, block_connected,
- block.GetHash().data(),
+ block_hash.data(),
pindex->nHeight,
block.vtx.size(),
nInputs,
nSigOpsCost,
- GetTimeMicros() - nTimeStart // in microseconds (µs)
+ nTime5 - nTimeStart // in microseconds (µs)
);
return true;
@@ -2171,6 +2230,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
CoinsCacheSizeState CChainState::GetCoinsCacheSizeState()
{
+ AssertLockHeld(::cs_main);
return this->GetCoinsCacheSizeState(
m_coinstip_cache_size_bytes,
gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
@@ -2180,10 +2240,11 @@ CoinsCacheSizeState CChainState::GetCoinsCacheSizeState(
size_t max_coins_cache_size_bytes,
size_t max_mempool_size_bytes)
{
+ AssertLockHeld(::cs_main);
const int64_t nMempoolUsage = m_mempool ? m_mempool->DynamicMemoryUsage() : 0;
int64_t cacheSize = CoinsTip().DynamicMemoryUsage();
int64_t nTotalSpace =
- max_coins_cache_size_bytes + std::max<int64_t>(max_mempool_size_bytes - nMempoolUsage, 0);
+ max_coins_cache_size_bytes + std::max<int64_t>(int64_t(max_mempool_size_bytes) - nMempoolUsage, 0);
//! No need to periodic flush if at least this much space still available.
static constexpr int64_t MAX_BLOCK_COINSDB_USAGE_BYTES = 10 * 1024 * 1024; // 10MB
@@ -2220,8 +2281,8 @@ bool CChainState::FlushStateToDisk(
bool fDoFullFlush = false;
CoinsCacheSizeState cache_state = GetCoinsCacheSizeState();
- LOCK(cs_LastBlockFile);
- if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) {
+ LOCK(m_blockman.cs_LastBlockFile);
+ if (fPruneMode && (m_blockman.m_check_for_pruning || nManualPruneHeight > 0) && !fReindex) {
// make sure we don't prune above the blockfilterindexes bestblocks
// pruning is height-based
int last_prune = m_chain.Height(); // last height we can prune
@@ -2237,7 +2298,7 @@ bool CChainState::FlushStateToDisk(
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune", BCLog::BENCH);
m_blockman.FindFilesToPrune(setFilesToPrune, m_params.PruneAfterHeight(), m_chain.Height(), last_prune, IsInitialBlockDownload());
- fCheckForPruning = false;
+ m_blockman.m_check_for_pruning = false;
}
if (!setFilesToPrune.empty()) {
fFlushForPrune = true;
@@ -2275,26 +2336,14 @@ bool CChainState::FlushStateToDisk(
LOG_TIME_MILLIS_WITH_CATEGORY("write block and undo data to disk", BCLog::BENCH);
// First make sure all block and undo data is flushed to disk.
- FlushBlockFile();
+ m_blockman.FlushBlockFile();
}
// Then update all block file information (which may refer to block and undo files).
{
LOG_TIME_MILLIS_WITH_CATEGORY("write block index to disk", BCLog::BENCH);
- std::vector<std::pair<int, const CBlockFileInfo*> > vFiles;
- vFiles.reserve(setDirtyFileInfo.size());
- for (std::set<int>::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) {
- vFiles.push_back(std::make_pair(*it, &vinfoBlockFile[*it]));
- setDirtyFileInfo.erase(it++);
- }
- std::vector<const CBlockIndex*> vBlocks;
- vBlocks.reserve(setDirtyBlockIndex.size());
- for (std::set<CBlockIndex*>::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) {
- vBlocks.push_back(*it);
- setDirtyBlockIndex.erase(it++);
- }
- if (!m_blockman.m_block_tree_db->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) {
+ if (!m_blockman.WriteBlockIndexDB()) {
return AbortNode(state, "Failed to write to block index database");
}
}
@@ -2353,7 +2402,7 @@ void CChainState::ForceFlushStateToDisk()
void CChainState::PruneAndFlush()
{
BlockValidationState state;
- fCheckForPruning = true;
+ m_blockman.m_check_for_pruning = true;
if (!this->FlushStateToDisk(state, FlushStateMode::NONE)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString());
}
@@ -2399,6 +2448,7 @@ static void UpdateTipLog(
void CChainState::UpdateTip(const CBlockIndex* pindexNew)
{
+ AssertLockHeld(::cs_main);
const auto& coins_tip = this->CoinsTip();
// The remainder of the function isn't relevant if we are not acting on
@@ -2622,7 +2672,9 @@ bool CChainState::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew
* 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).
*/
-CBlockIndex* CChainState::FindMostWorkChain() {
+CBlockIndex* CChainState::FindMostWorkChain()
+{
+ AssertLockHeld(::cs_main);
do {
CBlockIndex *pindexNew = nullptr;
@@ -2820,17 +2872,19 @@ static void LimitValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main) {
bool CChainState::ActivateBestChain(BlockValidationState& state, std::shared_ptr<const CBlock> pblock)
{
+ AssertLockNotHeld(m_chainstate_mutex);
+
// Note that while we're often called here from ProcessNewBlock, this is
// far from a guarantee. Things in the P2P/RPC will often end up calling
// us in the middle of ProcessNewBlock - do not assume pblock is set
// sanely for performance or correctness!
- AssertLockNotHeld(cs_main);
+ 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);
+ // we use m_chainstate_mutex to enforce mutual exclusion so that only one caller may execute this function at a time
+ LOCK(m_chainstate_mutex);
CBlockIndex *pindexMostWork = nullptr;
CBlockIndex *pindexNewTip = nullptr;
@@ -2920,6 +2974,8 @@ bool CChainState::ActivateBestChain(BlockValidationState& state, std::shared_ptr
bool CChainState::PreciousBlock(BlockValidationState& state, CBlockIndex* pindex)
{
+ AssertLockNotHeld(m_chainstate_mutex);
+ AssertLockNotHeld(::cs_main);
{
LOCK(cs_main);
if (pindex->nChainWork < m_chain.Tip()->nChainWork) {
@@ -2949,6 +3005,9 @@ bool CChainState::PreciousBlock(BlockValidationState& state, CBlockIndex* pindex
bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pindex)
{
+ AssertLockNotHeld(m_chainstate_mutex);
+ AssertLockNotHeld(::cs_main);
+
// Genesis block can't be invalidated
assert(pindex);
if (pindex->nHeight == 0) return false;
@@ -2960,7 +3019,7 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
// We do not allow ActivateBestChain() to run while InvalidateBlock() is
// running, as that could cause the tip to change while we disconnect
// blocks.
- LOCK(m_cs_chainstate);
+ LOCK(m_chainstate_mutex);
// We'll be acquiring and releasing cs_main below, to allow the validation
// callbacks to run. However, we should keep the block index in a
@@ -3023,14 +3082,14 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
// are no blocks that meet the "have data and are not invalid per
// nStatus" criteria for inclusion in setBlockIndexCandidates).
invalid_walk_tip->nStatus |= BLOCK_FAILED_VALID;
- setDirtyBlockIndex.insert(invalid_walk_tip);
+ m_blockman.m_dirty_blockindex.insert(invalid_walk_tip);
setBlockIndexCandidates.erase(invalid_walk_tip);
setBlockIndexCandidates.insert(invalid_walk_tip->pprev);
if (invalid_walk_tip->pprev == to_mark_failed && (to_mark_failed->nStatus & BLOCK_FAILED_VALID)) {
// We only want to mark the last disconnected block as BLOCK_FAILED_VALID; its children
// need to be BLOCK_FAILED_CHILD instead.
to_mark_failed->nStatus = (to_mark_failed->nStatus ^ BLOCK_FAILED_VALID) | BLOCK_FAILED_CHILD;
- setDirtyBlockIndex.insert(to_mark_failed);
+ m_blockman.m_dirty_blockindex.insert(to_mark_failed);
}
// Add any equal or more work headers to setBlockIndexCandidates
@@ -3060,7 +3119,7 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
// Mark pindex (or the last disconnected block) as invalid, even when it never was in the main chain
to_mark_failed->nStatus |= BLOCK_FAILED_VALID;
- setDirtyBlockIndex.insert(to_mark_failed);
+ m_blockman.m_dirty_blockindex.insert(to_mark_failed);
setBlockIndexCandidates.erase(to_mark_failed);
m_chainman.m_failed_blocks.insert(to_mark_failed);
@@ -3099,7 +3158,7 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
while (it != m_blockman.m_block_index.end()) {
if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) {
it->second->nStatus &= ~BLOCK_FAILED_MASK;
- setDirtyBlockIndex.insert(it->second);
+ m_blockman.m_dirty_blockindex.insert(it->second);
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->HaveTxsDownloaded() && setBlockIndexCandidates.value_comp()(m_chain.Tip(), it->second)) {
setBlockIndexCandidates.insert(it->second);
}
@@ -3116,52 +3175,17 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
while (pindex != nullptr) {
if (pindex->nStatus & BLOCK_FAILED_MASK) {
pindex->nStatus &= ~BLOCK_FAILED_MASK;
- setDirtyBlockIndex.insert(pindex);
+ m_blockman.m_dirty_blockindex.insert(pindex);
m_chainman.m_failed_blocks.erase(pindex);
}
pindex = pindex->pprev;
}
}
-CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block)
-{
- AssertLockHeld(cs_main);
-
- // Check for duplicate
- uint256 hash = block.GetHash();
- BlockMap::iterator it = m_block_index.find(hash);
- if (it != m_block_index.end())
- return it->second;
-
- // Construct new block index object
- CBlockIndex* pindexNew = new CBlockIndex(block);
- // We assign the sequence id to blocks only when the full data is available,
- // to avoid miners withholding blocks but broadcasting headers, to get a
- // competitive advantage.
- pindexNew->nSequenceId = 0;
- BlockMap::iterator mi = m_block_index.insert(std::make_pair(hash, pindexNew)).first;
- pindexNew->phashBlock = &((*mi).first);
- BlockMap::iterator miPrev = m_block_index.find(block.hashPrevBlock);
- if (miPrev != m_block_index.end())
- {
- pindexNew->pprev = (*miPrev).second;
- pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
- pindexNew->BuildSkip();
- }
- pindexNew->nTimeMax = (pindexNew->pprev ? std::max(pindexNew->pprev->nTimeMax, pindexNew->nTime) : pindexNew->nTime);
- pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew);
- pindexNew->RaiseValidity(BLOCK_VALID_TREE);
- if (pindexBestHeader == nullptr || pindexBestHeader->nChainWork < pindexNew->nChainWork)
- pindexBestHeader = pindexNew;
-
- setDirtyBlockIndex.insert(pindexNew);
-
- return pindexNew;
-}
-
/** Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS). */
void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos)
{
+ AssertLockHeld(cs_main);
pindexNew->nTx = block.vtx.size();
pindexNew->nChainTx = 0;
pindexNew->nFile = pos.nFile;
@@ -3172,7 +3196,7 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi
pindexNew->nStatus |= BLOCK_OPT_WITNESS;
}
pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS);
- setDirtyBlockIndex.insert(pindexNew);
+ m_blockman.m_dirty_blockindex.insert(pindexNew);
if (pindexNew->pprev == nullptr || pindexNew->pprev->HaveTxsDownloaded()) {
// If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS.
@@ -3325,21 +3349,6 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
return commitment;
}
-CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
-{
- const MapCheckpoints& checkpoints = data.mapCheckpoints;
-
- for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints))
- {
- const uint256& hash = i.second;
- CBlockIndex* pindex = LookupBlockIndex(hash);
- if (pindex) {
- return pindex;
- }
- }
- return nullptr;
-}
-
/** Context-dependent validity checks.
* By "context", we mean only the previous block headers, but not the UTXO
* set; UTXO-related validity checks are done in ConnectBlock().
@@ -3349,8 +3358,9 @@ CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
* in ConnectBlock().
* Note that -reindex-chainstate skips the validation that happens here!
*/
-static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidationState& state, BlockManager& blockman, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
+static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidationState& state, BlockManager& blockman, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
+ AssertLockHeld(::cs_main);
assert(pindexPrev != nullptr);
const int nHeight = pindexPrev->nHeight + 1;
@@ -3549,7 +3559,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
CBlockIndex* invalid_walk = pindexPrev;
while (invalid_walk != failedit) {
invalid_walk->nStatus |= BLOCK_FAILED_CHILD;
- setDirtyBlockIndex.insert(invalid_walk);
+ m_blockman.m_dirty_blockindex.insert(invalid_walk);
invalid_walk = invalid_walk->pprev;
}
LogPrint(BCLog::VALIDATION, "%s: %s prev block invalid\n", __func__, hash.ToString());
@@ -3587,7 +3597,10 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>&
}
if (NotifyHeaderTip(ActiveChainstate())) {
if (ActiveChainstate().IsInitialBlockDownload() && ppindex && *ppindex) {
- LogPrintf("Synchronizing blockheaders, height: %d (~%.2f%%)\n", (*ppindex)->nHeight, 100.0/((*ppindex)->nHeight+(GetAdjustedTime() - (*ppindex)->GetBlockTime()) / Params().GetConsensus().nPowTargetSpacing) * (*ppindex)->nHeight);
+ const CBlockIndex& last_accepted{**ppindex};
+ const int64_t blocks_left{(GetTime() - last_accepted.GetBlockTime()) / chainparams.GetConsensus().nPowTargetSpacing};
+ const double progress{100.0 * last_accepted.nHeight / (last_accepted.nHeight + blocks_left)};
+ LogPrintf("Synchronizing blockheaders, height: %d (~%.2f%%)\n", last_accepted.nHeight, progress);
}
}
return true;
@@ -3620,7 +3633,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
// blocks which are too close in height to the tip. Apply this test
// regardless of whether pruning is enabled; it should generally be safe to
// not process unrequested blocks.
- bool fTooFarAhead = (pindex->nHeight > int(m_chain.Height() + MIN_BLOCKS_TO_KEEP));
+ bool fTooFarAhead{pindex->nHeight > m_chain.Height() + int(MIN_BLOCKS_TO_KEEP)};
// TODO: Decouple this function from the block download logic by removing fRequested
// This requires some new chain data structure to efficiently look up if a
@@ -3647,7 +3660,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
!ContextualCheckBlock(block, state, m_params.GetConsensus(), pindex->pprev)) {
if (state.IsInvalid() && state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
pindex->nStatus |= BLOCK_FAILED_VALID;
- setDirtyBlockIndex.insert(pindex);
+ m_blockman.m_dirty_blockindex.insert(pindex);
}
return error("%s: %s", __func__, state.ToString());
}
@@ -3660,7 +3673,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
// Write block to history file
if (fNewBlock) *fNewBlock = true;
try {
- FlatFilePos blockPos = SaveBlockToDisk(block, pindex->nHeight, m_chain, m_params, dbp);
+ FlatFilePos blockPos{m_blockman.SaveBlockToDisk(block, pindex->nHeight, m_chain, m_params, dbp)};
if (blockPos.IsNull()) {
state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__));
return false;
@@ -3718,6 +3731,7 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s
MempoolAcceptResult ChainstateManager::ProcessTransaction(const CTransactionRef& tx, bool test_accept)
{
+ AssertLockHeld(cs_main);
CChainState& active_chainstate = ActiveChainstate();
if (!active_chainstate.GetMempool()) {
TxValidationState state;
@@ -3761,67 +3775,6 @@ bool TestBlockValidity(BlockValidationState& state,
return true;
}
-/**
- * BLOCK PRUNING CODE
- */
-
-void BlockManager::PruneOneBlockFile(const int fileNumber)
-{
- AssertLockHeld(cs_main);
- LOCK(cs_LastBlockFile);
-
- for (const auto& entry : m_block_index) {
- CBlockIndex* pindex = entry.second;
- if (pindex->nFile == fileNumber) {
- pindex->nStatus &= ~BLOCK_HAVE_DATA;
- pindex->nStatus &= ~BLOCK_HAVE_UNDO;
- pindex->nFile = 0;
- pindex->nDataPos = 0;
- pindex->nUndoPos = 0;
- setDirtyBlockIndex.insert(pindex);
-
- // Prune from m_blocks_unlinked -- any block we prune would have
- // to be downloaded again in order to consider its chain, at which
- // point it would be considered as a candidate for
- // m_blocks_unlinked or setBlockIndexCandidates.
- auto range = m_blocks_unlinked.equal_range(pindex->pprev);
- while (range.first != range.second) {
- std::multimap<CBlockIndex *, CBlockIndex *>::iterator _it = range.first;
- range.first++;
- if (_it->second == pindex) {
- m_blocks_unlinked.erase(_it);
- }
- }
- }
- }
-
- vinfoBlockFile[fileNumber].SetNull();
- setDirtyFileInfo.insert(fileNumber);
-}
-
-void BlockManager::FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height)
-{
- assert(fPruneMode && nManualPruneHeight > 0);
-
- LOCK2(cs_main, cs_LastBlockFile);
- if (chain_tip_height < 0) {
- return;
- }
-
- // last block to prune is the lesser of (user-specified height, MIN_BLOCKS_TO_KEEP from the tip)
- unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, chain_tip_height - MIN_BLOCKS_TO_KEEP);
- int count = 0;
- for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) {
- if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
- continue;
- }
- PruneOneBlockFile(fileNumber);
- setFilesToPrune.insert(fileNumber);
- count++;
- }
- LogPrintf("Prune (Manual): prune_height=%d removed %d blk/rev pairs\n", nLastBlockWeCanPrune, count);
-}
-
/* This function is called from the RPC code for pruneblockchain */
void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight)
{
@@ -3832,259 +3785,6 @@ void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeigh
}
}
-void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, int prune_height, bool is_ibd)
-{
- LOCK2(cs_main, cs_LastBlockFile);
- if (chain_tip_height < 0 || nPruneTarget == 0) {
- return;
- }
- if ((uint64_t)chain_tip_height <= nPruneAfterHeight) {
- return;
- }
-
- unsigned int nLastBlockWeCanPrune = std::min(prune_height, chain_tip_height - static_cast<int>(MIN_BLOCKS_TO_KEEP));
- uint64_t nCurrentUsage = CalculateCurrentUsage();
- // We don't check to prune until after we've allocated new space for files
- // So we should leave a buffer under our target to account for another allocation
- // before the next pruning.
- uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE;
- uint64_t nBytesToPrune;
- 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 (is_ibd) {
- // 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;
-
- if (vinfoBlockFile[fileNumber].nSize == 0) {
- continue;
- }
-
- if (nCurrentUsage + nBuffer < nPruneTarget) { // are we below our target?
- break;
- }
-
- // don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning
- if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
- continue;
- }
-
- PruneOneBlockFile(fileNumber);
- // Queue up the files for removal
- setFilesToPrune.insert(fileNumber);
- nCurrentUsage -= nBytesToPrune;
- count++;
- }
- }
-
- LogPrint(BCLog::PRUNE, "Prune: target=%dMiB actual=%dMiB diff=%dMiB max_prune_height=%d removed %d blk/rev pairs\n",
- nPruneTarget/1024/1024, nCurrentUsage/1024/1024,
- ((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024,
- nLastBlockWeCanPrune, count);
-}
-
-CBlockIndex * BlockManager::InsertBlockIndex(const uint256& hash)
-{
- AssertLockHeld(cs_main);
-
- if (hash.IsNull())
- return nullptr;
-
- // Return existing
- BlockMap::iterator mi = m_block_index.find(hash);
- if (mi != m_block_index.end())
- return (*mi).second;
-
- // Create new
- CBlockIndex* pindexNew = new CBlockIndex();
- mi = m_block_index.insert(std::make_pair(hash, pindexNew)).first;
- pindexNew->phashBlock = &((*mi).first);
-
- return pindexNew;
-}
-
-bool BlockManager::LoadBlockIndex(
- const Consensus::Params& consensus_params,
- ChainstateManager& chainman)
-{
- if (!m_block_tree_db->LoadBlockIndexGuts(consensus_params, [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); })) {
- return false;
- }
-
- // Calculate nChainWork
- std::vector<std::pair<int, CBlockIndex*> > vSortedByHeight;
- vSortedByHeight.reserve(m_block_index.size());
- for (const std::pair<const uint256, CBlockIndex*>& item : m_block_index)
- {
- CBlockIndex* pindex = item.second;
- vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex));
- }
- sort(vSortedByHeight.begin(), vSortedByHeight.end());
-
- // Find start of assumed-valid region.
- int first_assumed_valid_height = std::numeric_limits<int>::max();
-
- for (const auto& [height, block] : vSortedByHeight) {
- if (block->IsAssumedValid()) {
- auto chainstates = chainman.GetAll();
-
- // If we encounter an assumed-valid block index entry, ensure that we have
- // one chainstate that tolerates assumed-valid entries and another that does
- // not (i.e. the background validation chainstate), since assumed-valid
- // entries should always be pending validation by a fully-validated chainstate.
- auto any_chain = [&](auto fnc) { return std::any_of(chainstates.cbegin(), chainstates.cend(), fnc); };
- assert(any_chain([](auto chainstate) { return chainstate->reliesOnAssumedValid(); }));
- assert(any_chain([](auto chainstate) { return !chainstate->reliesOnAssumedValid(); }));
-
- first_assumed_valid_height = height;
- break;
- }
- }
-
- for (const std::pair<int, CBlockIndex*>& item : vSortedByHeight)
- {
- if (ShutdownRequested()) return false;
- CBlockIndex* pindex = item.second;
- pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex);
- pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime);
-
- // We can link the chain of blocks for which we've received transactions at some point, or
- // blocks that are assumed-valid on the basis of snapshot load (see
- // PopulateAndValidateSnapshot()).
- // Pruned nodes may have deleted the block.
- if (pindex->nTx > 0) {
- if (pindex->pprev) {
- if (pindex->pprev->nChainTx > 0) {
- pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx;
- } else {
- pindex->nChainTx = 0;
- m_blocks_unlinked.insert(std::make_pair(pindex->pprev, pindex));
- }
- } else {
- pindex->nChainTx = pindex->nTx;
- }
- }
- if (!(pindex->nStatus & BLOCK_FAILED_MASK) && pindex->pprev && (pindex->pprev->nStatus & BLOCK_FAILED_MASK)) {
- pindex->nStatus |= BLOCK_FAILED_CHILD;
- setDirtyBlockIndex.insert(pindex);
- }
- if (pindex->IsAssumedValid() ||
- (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) &&
- (pindex->HaveTxsDownloaded() || pindex->pprev == nullptr))) {
-
- // Fill each chainstate's block candidate set. Only add assumed-valid
- // blocks to the tip candidate set if the chainstate is allowed to rely on
- // assumed-valid blocks.
- //
- // If all setBlockIndexCandidates contained the assumed-valid blocks, the
- // background chainstate's ActivateBestChain() call would add assumed-valid
- // blocks to the chain (based on how FindMostWorkChain() works). Obviously
- // we don't want this since the purpose of the background validation chain
- // is to validate assued-valid blocks.
- //
- // Note: This is considering all blocks whose height is greater or equal to
- // the first assumed-valid block to be assumed-valid blocks, and excluding
- // them from the background chainstate's setBlockIndexCandidates set. This
- // does mean that some blocks which are not technically assumed-valid
- // (later blocks on a fork beginning before the first assumed-valid block)
- // might not get added to the the background chainstate, but this is ok,
- // because they will still be attached to the active chainstate if they
- // actually contain more work.
- //
- // Instad of this height-based approach, an earlier attempt was made at
- // detecting "holistically" whether the block index under consideration
- // relied on an assumed-valid ancestor, but this proved to be too slow to
- // be practical.
- for (CChainState* chainstate : chainman.GetAll()) {
- if (chainstate->reliesOnAssumedValid() ||
- pindex->nHeight < first_assumed_valid_height) {
- chainstate->setBlockIndexCandidates.insert(pindex);
- }
- }
- }
- if (pindex->nStatus & BLOCK_FAILED_MASK && (!chainman.m_best_invalid || pindex->nChainWork > chainman.m_best_invalid->nChainWork)) {
- chainman.m_best_invalid = pindex;
- }
- if (pindex->pprev)
- pindex->BuildSkip();
- if (pindex->IsValid(BLOCK_VALID_TREE) && (pindexBestHeader == nullptr || CBlockIndexWorkComparator()(pindexBestHeader, pindex)))
- pindexBestHeader = pindex;
- }
-
- return true;
-}
-
-void BlockManager::Unload() {
- m_blocks_unlinked.clear();
-
- for (const BlockMap::value_type& entry : m_block_index) {
- delete entry.second;
- }
-
- m_block_index.clear();
-}
-
-bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
-{
- if (!LoadBlockIndex(::Params().GetConsensus(), chainman)) {
- return false;
- }
-
- // Load block file info
- m_block_tree_db->ReadLastBlockFile(nLastBlockFile);
- vinfoBlockFile.resize(nLastBlockFile + 1);
- LogPrintf("%s: last block file = %i\n", __func__, nLastBlockFile);
- for (int nFile = 0; nFile <= nLastBlockFile; nFile++) {
- m_block_tree_db->ReadBlockFileInfo(nFile, vinfoBlockFile[nFile]);
- }
- LogPrintf("%s: last block file info: %s\n", __func__, vinfoBlockFile[nLastBlockFile].ToString());
- for (int nFile = nLastBlockFile + 1; true; nFile++) {
- CBlockFileInfo info;
- if (m_block_tree_db->ReadBlockFileInfo(nFile, info)) {
- vinfoBlockFile.push_back(info);
- } else {
- break;
- }
- }
-
- // Check presence of blk files
- LogPrintf("Checking all blk files are present...\n");
- std::set<int> setBlkDataFiles;
- for (const std::pair<const uint256, CBlockIndex*>& item : m_block_index) {
- CBlockIndex* pindex = item.second;
- if (pindex->nStatus & BLOCK_HAVE_DATA) {
- setBlkDataFiles.insert(pindex->nFile);
- }
- }
- for (std::set<int>::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++)
- {
- FlatFilePos pos(*it, 0);
- if (CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION).IsNull()) {
- return false;
- }
- }
-
- // Check whether we have ever pruned block & undo files
- m_block_tree_db->ReadFlag("prunedblockfiles", fHavePruned);
- if (fHavePruned)
- LogPrintf("LoadBlockIndexDB(): Block files have previously been pruned\n");
-
- // Check whether we need to continue reindexing
- bool fReindexing = false;
- m_block_tree_db->ReadReindexing(fReindexing);
- if(fReindexing) fReindex = true;
-
- return true;
-}
-
void CChainState::LoadMempool(const ArgsManager& args)
{
if (!m_mempool) return;
@@ -4245,6 +3945,7 @@ bool CVerifyDB::VerifyDB(
/** Apply the effects of a block on the utxo cache, ignoring that it may already have been applied. */
bool CChainState::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs)
{
+ AssertLockHeld(cs_main);
// TODO: merge with ConnectBlock
CBlock block;
if (!ReadBlockFromDisk(block, pindex, m_params.GetConsensus())) {
@@ -4348,7 +4049,9 @@ bool CChainState::NeedsRedownload() const
return false;
}
-void CChainState::UnloadBlockIndex() {
+void CChainState::UnloadBlockIndex()
+{
+ AssertLockHeld(::cs_main);
nBlockSequenceId = 1;
setBlockIndexCandidates.clear();
}
@@ -4362,10 +4065,6 @@ void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman)
chainman.Unload();
pindexBestHeader = nullptr;
if (mempool) mempool->clear();
- vinfoBlockFile.clear();
- nLastBlockFile = 0;
- setDirtyBlockIndex.clear();
- setDirtyFileInfo.clear();
g_versionbitscache.Clear();
for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) {
warningcache[b].clear();
@@ -4409,9 +4108,10 @@ bool CChainState::LoadGenesisBlock()
try {
const CBlock& block = m_params.GenesisBlock();
- FlatFilePos blockPos = SaveBlockToDisk(block, 0, m_chain, m_params, nullptr);
- if (blockPos.IsNull())
+ FlatFilePos blockPos{m_blockman.SaveBlockToDisk(block, 0, m_chain, m_params, nullptr)};
+ if (blockPos.IsNull()) {
return error("%s: writing genesis block to disk failed", __func__);
+ }
CBlockIndex *pindex = m_blockman.AddToBlockIndex(block);
ReceivedBlockTransactions(block, pindex, blockPos);
} catch (const std::runtime_error& e) {
@@ -4423,6 +4123,7 @@ bool CChainState::LoadGenesisBlock()
void CChainState::LoadExternalBlockFile(FILE* fileIn, FlatFilePos* dbp)
{
+ AssertLockNotHeld(m_chainstate_mutex);
// Map of disk positions for blocks with unknown parent (only used for reindex)
static std::multimap<uint256, FlatFilePos> mapBlocksUnknownParent;
int64_t nStart = GetTimeMillis();
@@ -4443,7 +4144,7 @@ void CChainState::LoadExternalBlockFile(FILE* fileIn, FlatFilePos* dbp)
// locate a header
unsigned char buf[CMessageHeader::MESSAGE_START_SIZE];
blkdat.FindByte(m_params.MessageStart()[0]);
- nRewind = blkdat.GetPos()+1;
+ nRewind = blkdat.GetPos() + 1;
blkdat >> buf;
if (memcmp(buf, m_params.MessageStart(), CMessageHeader::MESSAGE_START_SIZE)) {
continue;
@@ -4763,6 +4464,7 @@ void CChainState::CheckBlockIndex()
std::string CChainState::ToString()
{
+ AssertLockHeld(::cs_main);
CBlockIndex* tip = m_chain.Tip();
return strprintf("Chainstate [%s] @ height %d (%s)",
m_from_snapshot_blockhash ? "snapshot" : "ibd",
@@ -4771,6 +4473,7 @@ std::string CChainState::ToString()
bool CChainState::ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size)
{
+ AssertLockHeld(::cs_main);
if (coinstip_size == m_coinstip_cache_size_bytes &&
coinsdb_size == m_coinsdb_cache_size_bytes) {
// Cache sizes are unchanged, no need to continue.
@@ -4827,7 +4530,8 @@ bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mocka
}
uint64_t num;
file >> num;
- while (num--) {
+ while (num) {
+ --num;
CTransactionRef tx;
int64_t nTime;
int64_t nFeeDelta;
@@ -4994,6 +4698,7 @@ std::vector<CChainState*> ChainstateManager::GetAll()
CChainState& ChainstateManager::InitializeChainstate(
CTxMemPool* mempool, const std::optional<uint256>& snapshot_blockhash)
{
+ AssertLockHeld(::cs_main);
bool is_snapshot = snapshot_blockhash.has_value();
std::unique_ptr<CChainState>& to_modify =
is_snapshot ? m_snapshot_chainstate : m_ibd_chainstate;
@@ -5249,7 +4954,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
// about the snapshot_chainstate.
CCoinsViewDB* snapshot_coinsdb = WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsDB());
- if (!GetUTXOStats(snapshot_coinsdb, WITH_LOCK(::cs_main, return std::ref(m_blockman)), stats, breakpoint_fnc)) {
+ if (!GetUTXOStats(snapshot_coinsdb, m_blockman, stats, breakpoint_fnc)) {
LogPrintf("[snapshot] failed to generate coins stats\n");
return false;
}
@@ -5299,7 +5004,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
index->nStatus |= BLOCK_OPT_WITNESS;
}
- setDirtyBlockIndex.insert(index);
+ m_blockman.m_dirty_blockindex.insert(index);
// Changes to the block index will be flushed to disk after this call
// returns in `ActivateSnapshot()`, when `MaybeRebalanceCaches()` is
// called, since we've added a snapshot chainstate and therefore will
@@ -5331,6 +5036,7 @@ bool ChainstateManager::IsSnapshotActive() const
void ChainstateManager::Unload()
{
+ AssertLockHeld(::cs_main);
for (CChainState* chainstate : this->GetAll()) {
chainstate->m_chain.SetTip(nullptr);
chainstate->UnloadBlockIndex();
@@ -5352,6 +5058,7 @@ void ChainstateManager::Reset()
void ChainstateManager::MaybeRebalanceCaches()
{
+ AssertLockHeld(::cs_main);
if (m_ibd_chainstate && !m_snapshot_chainstate) {
LogPrintf("[snapshot] allocating all cache to the IBD chainstate\n");
// Allocate everything to the IBD chainstate.