aboutsummaryrefslogtreecommitdiff
path: root/src/validation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/validation.cpp')
-rw-r--r--src/validation.cpp341
1 files changed, 98 insertions, 243 deletions
diff --git a/src/validation.cpp b/src/validation.cpp
index a7cdf63a2a..d276cea2f7 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -5,6 +5,9 @@
#include <validation.h>
+#include <kernel/coinstats.h>
+#include <kernel/mempool_persist.h>
+
#include <arith_uint256.h>
#include <chain.h>
#include <chainparams.h>
@@ -17,12 +20,12 @@
#include <consensus/validation.h>
#include <cuckoocache.h>
#include <flatfile.h>
+#include <fs.h>
#include <hash.h>
#include <logging.h>
#include <logging/timer.h>
#include <node/blockstorage.h>
-#include <node/coinstats.h>
-#include <node/ui_interface.h>
+#include <node/interface_ui.h>
#include <node/utxo_snapshot.h>
#include <policy/policy.h>
#include <policy/rbf.h>
@@ -36,7 +39,6 @@
#include <script/sigcache.h>
#include <shutdown.h>
#include <signet.h>
-#include <timedata.h>
#include <tinyformat.h>
#include <txdb.h>
#include <txmempool.h>
@@ -48,45 +50,41 @@
#include <util/rbf.h>
#include <util/strencodings.h>
#include <util/system.h>
+#include <util/time.h>
#include <util/trace.h>
#include <util/translation.h>
#include <validationinterface.h>
#include <warnings.h>
#include <algorithm>
+#include <cassert>
+#include <chrono>
#include <deque>
#include <numeric>
#include <optional>
#include <string>
-using node::BLOCKFILE_CHUNK_SIZE;
+using kernel::CCoinsStats;
+using kernel::CoinStatsHashType;
+using kernel::ComputeUTXOStats;
+using kernel::LoadMempool;
+
+using fsbridge::FopenFn;
using node::BlockManager;
using node::BlockMap;
using node::CBlockIndexHeightOnlyComparator;
using node::CBlockIndexWorkComparator;
-using node::CCoinsStats;
-using node::CoinStatsHashType;
using node::fImporting;
using node::fPruneMode;
using node::fReindex;
-using node::GetUTXOStats;
-using node::nPruneTarget;
-using node::OpenBlockFile;
using node::ReadBlockFromDisk;
using node::SnapshotMetadata;
-using node::UNDOFILE_CHUNK_SIZE;
using node::UndoReadFromDisk;
using node::UnlinkPrunedFiles;
#define MICRO 0.000001
#define MILLI 0.001
-/**
- * An extra transaction can be added to a package, as long as it only has one
- * ancestor and is no larger than this. Not really any reason to make this
- * configurable as it doesn't materially change DoS parameters.
- */
-static const unsigned int EXTRA_DESCENDANT_TX_SIZE_LIMIT = 10000;
/** Maximum kilobytes for transactions to store for processing during reorg */
static const unsigned int MAX_DISCONNECTED_TX_POOL_SIZE = 20000;
/** Time to wait between writing blocks/block index to disk. */
@@ -122,11 +120,10 @@ static constexpr int PRUNE_LOCK_BUFFER{10};
*/
RecursiveMutex cs_main;
-Mutex g_best_block_mutex;
+GlobalMutex g_best_block_mutex;
std::condition_variable g_best_block_cv;
uint256 g_best_block;
bool g_parallel_script_checks{false};
-bool fRequireStandard = true;
bool fCheckBlockIndex = false;
bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED;
int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE;
@@ -134,8 +131,6 @@ int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE;
uint256 hashAssumeValid;
arith_uint256 nMinimumChainWork;
-CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
-
const CBlockIndex* CChainState::FindForkInGlobalIndex(const CBlockLocator& locator) const
{
AssertLockHeld(cs_main);
@@ -162,10 +157,9 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
std::vector<CScriptCheck>* pvChecks = nullptr)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-bool CheckFinalTxAtTip(const CBlockIndex* active_chain_tip, const CTransaction& tx)
+bool CheckFinalTxAtTip(const CBlockIndex& active_chain_tip, const CTransaction& tx)
{
AssertLockHeld(cs_main);
- assert(active_chain_tip); // TODO: Make active_chain_tip a reference
// CheckFinalTxAtTip() uses active_chain_tip.Height()+1 to evaluate
// nLockTime because when IsFinalTx() is called within
@@ -173,14 +167,14 @@ bool CheckFinalTxAtTip(const CBlockIndex* active_chain_tip, const CTransaction&
// evaluated is what is used. Thus if we want to know if a
// transaction can be part of the *next* block, we need to call
// IsFinalTx() with one more than active_chain_tip.Height().
- const int nBlockHeight = active_chain_tip->nHeight + 1;
+ const int nBlockHeight = active_chain_tip.nHeight + 1;
// BIP113 requires that time-locked transactions have nLockTime set to
// less than the median time of the previous block they're contained in.
// When the next block is created its previous block will be the current
// chain tip, so we use that to calculate the median time passed to
// IsFinalTx().
- const int64_t nBlockTime{active_chain_tip->GetMedianTimePast()};
+ const int64_t nBlockTime{active_chain_tip.GetMedianTimePast()};
return IsFinalTx(tx, nBlockHeight, nBlockTime);
}
@@ -263,18 +257,18 @@ bool CheckSequenceLocksAtTip(CBlockIndex* tip,
// Returns the script flags which should be checked for a given block
static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const ChainstateManager& chainman);
-static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache, size_t limit, std::chrono::seconds age)
+static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main, pool.cs)
{
AssertLockHeld(::cs_main);
AssertLockHeld(pool.cs);
- int expired = pool.Expire(GetTime<std::chrono::seconds>() - age);
+ int expired = pool.Expire(GetTime<std::chrono::seconds>() - pool.m_expiry);
if (expired != 0) {
LogPrint(BCLog::MEMPOOL, "Expired %i transactions from the memory pool\n", expired);
}
std::vector<COutPoint> vNoSpendsRemaining;
- pool.TrimToSize(limit, &vNoSpendsRemaining);
+ pool.TrimToSize(pool.m_max_size_bytes, &vNoSpendsRemaining);
for (const COutPoint& removed : vNoSpendsRemaining)
coins_cache.Uncache(removed);
}
@@ -328,9 +322,7 @@ 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.
- 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);
+ m_mempool->UpdateTransactionsFromBlock(vHashUpdate);
// Predicate to use for filtering transactions in removeForReorg.
// Checks whether the transaction is still final and, if it spends a coinbase output, mature.
@@ -344,7 +336,7 @@ void CChainState::MaybeUpdateMempoolForReorg(
const CTransaction& tx = it->GetTx();
// The transaction must be final.
- if (!CheckFinalTxAtTip(m_chain.Tip(), tx)) return true;
+ if (!CheckFinalTxAtTip(*Assert(m_chain.Tip()), tx)) return true;
LockPoints lp = it->GetLockPoints();
const bool validLP{TestLockPointValidity(m_chain, lp)};
CCoinsViewMemPool view_mempool(&CoinsTip(), *m_mempool);
@@ -382,11 +374,7 @@ void CChainState::MaybeUpdateMempoolForReorg(
// We also need to remove any now-immature transactions
m_mempool->removeForReorg(m_chain, filter_final_and_mature);
// Re-limit mempool size, in case we added any transactions
- LimitMempoolSize(
- *m_mempool,
- this->CoinsTip(),
- gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000,
- std::chrono::hours{gArgs.GetIntArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
+ LimitMempoolSize(*m_mempool, this->CoinsTip());
}
/**
@@ -437,10 +425,10 @@ class MemPoolAccept
{
public:
explicit MemPoolAccept(CTxMemPool& mempool, CChainState& active_chainstate) : m_pool(mempool), m_view(&m_dummy), m_viewmempool(&active_chainstate.CoinsTip(), m_pool), m_active_chainstate(active_chainstate),
- m_limit_ancestors(gArgs.GetIntArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT)),
- m_limit_ancestor_size(gArgs.GetIntArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000),
- m_limit_descendants(gArgs.GetIntArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)),
- m_limit_descendant_size(gArgs.GetIntArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000) {
+ m_limit_ancestors(m_pool.m_limits.ancestor_count),
+ m_limit_ancestor_size(m_pool.m_limits.ancestor_size_vbytes),
+ m_limit_descendants(m_pool.m_limits.descendant_count),
+ m_limit_descendant_size(m_pool.m_limits.descendant_size_vbytes) {
}
// We put the arguments we're handed into a struct, so we can pass them
@@ -652,13 +640,14 @@ private:
{
AssertLockHeld(::cs_main);
AssertLockHeld(m_pool.cs);
- CAmount mempoolRejectFee = m_pool.GetMinFee(gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(package_size);
+ CAmount mempoolRejectFee = m_pool.GetMinFee().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));
}
- if (package_fee < ::minRelayTxFee.GetFee(package_size)) {
- return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "min relay fee not met", strprintf("%d < %d", package_fee, ::minRelayTxFee.GetFee(package_size)));
+ if (package_fee < m_pool.m_min_relay_feerate.GetFee(package_size)) {
+ return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "min relay fee not met",
+ strprintf("%d < %d", package_fee, m_pool.m_min_relay_feerate.GetFee(package_size)));
}
return true;
}
@@ -710,8 +699,9 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
std::string reason;
- if (fRequireStandard && !IsStandardTx(tx, reason))
+ if (m_pool.m_require_standard && !IsStandardTx(tx, m_pool.m_max_datacarrier_bytes, m_pool.m_permit_bare_multisig, m_pool.m_dust_relay_feerate, reason)) {
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, 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.
@@ -723,7 +713,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// 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.
- if (!CheckFinalTxAtTip(m_active_chainstate.m_chain.Tip(), tx)) {
+ if (!CheckFinalTxAtTip(*Assert(m_active_chainstate.m_chain.Tip()), tx)) {
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-final");
}
@@ -754,7 +744,10 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// Applications relying on first-seen mempool behavior should
// check all unconfirmed ancestors; otherwise an opt-in ancestor
// might be replaced, causing removal of this descendant.
- if (!SignalsOptInRBF(*ptxConflicting)) {
+ //
+ // If replaceability signaling is ignored due to node setting,
+ // replacement is always allowed.
+ if (!m_pool.m_full_rbf && !SignalsOptInRBF(*ptxConflicting)) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "txn-mempool-conflict");
}
@@ -814,14 +807,14 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
return false; // state filled in by CheckTxInputs
}
- // Check for non-standard pay-to-script-hash in inputs
- if (fRequireStandard && !AreInputsStandard(tx, m_view)) {
+ if (m_pool.m_require_standard && !AreInputsStandard(tx, m_view)) {
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
}
// Check for non-standard witnesses.
- if (tx.HasWitness() && fRequireStandard && !IsWitnessStandard(tx, m_view))
+ if (tx.HasWitness() && m_pool.m_require_standard && !IsWitnessStandard(tx, m_view)) {
return state.Invalid(TxValidationResult::TX_WITNESS_MUTATED, "bad-witness-nonstandard");
+ }
int64_t nSigOpsCost = GetTransactionSigOpCost(tx, m_view, STANDARD_SCRIPT_VERIFY_FLAGS);
@@ -848,7 +841,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "bad-txns-too-many-sigops",
strprintf("%d", nSigOpsCost));
- // No individual transactions are allowed below minRelayTxFee and mempool min fee except from
+ // No individual transactions are allowed below the min relay feerate and mempool min feerate except from
// disconnected blocks and transactions in a package. Package transactions will be checked using
// package feerate later.
if (!bypass_limits && !args.m_package_feerates && !CheckFeeRate(ws.m_vsize, ws.m_modified_fees, state)) return false;
@@ -966,7 +959,7 @@ bool MemPoolAccept::ReplacementChecks(Workspace& ws)
ws.m_conflicting_size += it->GetTxSize();
}
if (const auto err_string{PaysForRBF(ws.m_conflicting_fees, ws.m_modified_fees, ws.m_vsize,
- ::incrementalRelayFee, hash)}) {
+ m_pool.m_incremental_relay_feerate, hash)}) {
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", *err_string);
}
return true;
@@ -1091,7 +1084,7 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
// in the package. LimitMempoolSize() should be called at the very end to make sure the mempool
// is still within limits and package submission happens atomically.
if (!args.m_package_submission && !bypass_limits) {
- LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip(), gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetIntArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
+ LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip());
if (!m_pool.exists(GenTxid::Txid(hash)))
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full");
}
@@ -1156,9 +1149,7 @@ bool MemPoolAccept::SubmitPackage(const ATMPArgs& args, std::vector<Workspace>&
// It may or may not be the case that all the transactions made it into the mempool. Regardless,
// make sure we haven't exceeded max mempool size.
- LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip(),
- gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000,
- std::chrono::hours{gArgs.GetIntArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)});
+ LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip());
// 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.
@@ -1664,7 +1655,8 @@ bool CScriptCheck::operator()() {
static CuckooCache::cache<uint256, SignatureCacheHasher> g_scriptExecutionCache;
static CSHA256 g_scriptExecutionCacheHasher;
-void InitScriptExecutionCache() {
+bool InitScriptExecutionCache(size_t max_size_bytes)
+{
// Setup the salted hasher
uint256 nonce = GetRandHash();
// We want the nonce to be 64 bytes long to force the hasher to process
@@ -1672,12 +1664,14 @@ void InitScriptExecutionCache() {
// just write our 32-byte entropy twice to fill the 64 bytes.
g_scriptExecutionCacheHasher.Write(nonce.begin(), 32);
g_scriptExecutionCacheHasher.Write(nonce.begin(), 32);
- // nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero,
- // setup_bytes creates the minimum possible cache (2 elements).
- size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetIntArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
- size_t nElems = g_scriptExecutionCache.setup_bytes(nMaxCacheSize);
- LogPrintf("Using %zu MiB out of %zu/2 requested for script execution cache, able to store %zu elements\n",
- (nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems);
+
+ auto setup_results = g_scriptExecutionCache.setup_bytes(max_size_bytes);
+ if (!setup_results) return false;
+
+ const auto [num_elems, approx_size_bytes] = *setup_results;
+ LogPrintf("Using %zu MiB out of %zu MiB requested for script execution cache, able to store %zu elements\n",
+ approx_size_bytes >> 20, max_size_bytes >> 20, num_elems);
+ return true;
}
/**
@@ -2006,7 +2000,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
// Also, currently the rule against blocks more than 2 hours in the future
// is enforced in ContextualCheckBlockHeader(); we wouldn't want to
// re-enforce that rule here (at least until we make it impossible for
- // GetAdjustedTime() to go backward).
+ // m_adjusted_time_callback() to go backward).
if (!CheckBlock(block, state, m_params.GetConsensus(), !fJustCheck, !fJustCheck)) {
if (state.GetResult() == BlockValidationResult::BLOCK_MUTATED) {
// We don't write down blocks to disk if they may have been
@@ -2277,7 +2271,6 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
m_blockman.m_dirty_blockindex.insert(pindex);
}
- assert(pindex->phashBlock);
// add this block to the view's block chain
view.SetBestBlock(pindex->GetBlockHash());
@@ -2301,7 +2294,7 @@ CoinsCacheSizeState CChainState::GetCoinsCacheSizeState()
AssertLockHeld(::cs_main);
return this->GetCoinsCacheSizeState(
m_coinstip_cache_size_bytes,
- gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
+ m_mempool ? m_mempool->m_max_size_bytes : 0);
}
CoinsCacheSizeState CChainState::GetCoinsCacheSizeState(
@@ -2589,6 +2582,7 @@ bool CChainState::DisconnectTip(BlockValidationState& state, DisconnectedBlockTr
CBlockIndex *pindexDelete = m_chain.Tip();
assert(pindexDelete);
+ assert(pindexDelete->pprev);
// Read block from disk.
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
CBlock& block = *pblock;
@@ -2636,7 +2630,7 @@ bool CChainState::DisconnectTip(BlockValidationState& state, DisconnectedBlockTr
}
}
- m_chain.SetTip(pindexDelete->pprev);
+ m_chain.SetTip(*pindexDelete->pprev);
UpdateTip(pindexDelete->pprev);
// Let wallets know transactions went from 1-confirmed to
@@ -2750,7 +2744,7 @@ bool CChainState::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew
disconnectpool.removeForBlock(blockConnecting.vtx);
}
// Update m_chain & related variables.
- m_chain.SetTip(pindexNew);
+ m_chain.SetTip(*pindexNew);
UpdateTip(pindexNew);
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
@@ -3500,15 +3494,15 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat
const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
// Enforce BIP113 (Median Time Past).
- int nLockTimeFlags = 0;
+ bool enforce_locktime_median_time_past{false};
if (DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_CSV)) {
assert(pindexPrev != nullptr);
- nLockTimeFlags |= LOCKTIME_MEDIAN_TIME_PAST;
+ enforce_locktime_median_time_past = true;
}
- int64_t nLockTimeCutoff = (nLockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST)
- ? pindexPrev->GetMedianTimePast()
- : block.GetBlockTime();
+ const int64_t nLockTimeCutoff{enforce_locktime_median_time_past ?
+ pindexPrev->GetMedianTimePast() :
+ block.GetBlockTime()};
// Check that all transactions are finalized
for (const auto& tx : block.vtx) {
@@ -3613,7 +3607,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
LogPrint(BCLog::VALIDATION, "%s: %s prev block invalid\n", __func__, hash.ToString());
return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk");
}
- if (!ContextualCheckBlockHeader(block, state, m_blockman, *this, pindexPrev, GetAdjustedTime())) {
+ if (!ContextualCheckBlockHeader(block, state, m_blockman, *this, pindexPrev, m_adjusted_time_callback())) {
LogPrint(BCLog::VALIDATION, "%s: Consensus::ContextualCheckBlockHeader: %s, %s\n", __func__, hash.ToString(), state.ToString());
return false;
}
@@ -3837,6 +3831,7 @@ bool TestBlockValidity(BlockValidationState& state,
CChainState& chainstate,
const CBlock& block,
CBlockIndex* pindexPrev,
+ const std::function<int64_t()>& adjusted_time_callback,
bool fCheckPOW,
bool fCheckMerkleRoot)
{
@@ -3850,7 +3845,7 @@ bool TestBlockValidity(BlockValidationState& state,
indexDummy.phashBlock = &block_hash;
// NOTE: CheckBlockHeader is called by CheckBlock
- if (!ContextualCheckBlockHeader(block, state, chainstate.m_blockman, chainstate.m_chainman, pindexPrev, GetAdjustedTime()))
+ if (!ContextualCheckBlockHeader(block, state, chainstate.m_blockman, chainstate.m_chainman, pindexPrev, adjusted_time_callback()))
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, state.ToString());
if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot))
return error("%s: Consensus::CheckBlock: %s", __func__, state.ToString());
@@ -3874,13 +3869,11 @@ void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeigh
}
}
-void CChainState::LoadMempool(const ArgsManager& args)
+void CChainState::LoadMempool(const fs::path& load_path, FopenFn mockable_fopen_function)
{
if (!m_mempool) return;
- if (args.GetBoolArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
- ::LoadMempool(*m_mempool, *this);
- }
- m_mempool->SetIsLoaded(!ShutdownRequested());
+ ::LoadMempool(*m_mempool, load_path, *this, mockable_fopen_function);
+ m_mempool->SetLoadTried(!ShutdownRequested());
}
bool CChainState::LoadChainTip()
@@ -3899,7 +3892,7 @@ bool CChainState::LoadChainTip()
if (!pindex) {
return false;
}
- m_chain.SetTip(pindex);
+ m_chain.SetTip(*pindex);
PruneBlockIndexCandidates();
tip = m_chain.Tip();
@@ -4158,7 +4151,7 @@ bool ChainstateManager::LoadBlockIndex()
// Load block index from databases
bool needs_init = fReindex;
if (!fReindex) {
- bool ret = m_blockman.LoadBlockIndexDB();
+ bool ret = m_blockman.LoadBlockIndexDB(GetConsensus());
if (!ret) return false;
std::vector<CBlockIndex*> vSortedByHeight{m_blockman.GetAllBlockIndices()};
@@ -4269,11 +4262,16 @@ bool CChainState::LoadGenesisBlock()
return true;
}
-void CChainState::LoadExternalBlockFile(FILE* fileIn, FlatFilePos* dbp)
+void CChainState::LoadExternalBlockFile(
+ FILE* fileIn,
+ FlatFilePos* dbp,
+ std::multimap<uint256, FlatFilePos>* blocks_with_unknown_parent)
{
AssertLockNotHeld(m_chainstate_mutex);
- // Map of disk positions for blocks with unknown parent (only used for reindex)
- static std::multimap<uint256, FlatFilePos> mapBlocksUnknownParent;
+
+ // Either both should be specified (-reindex), or neither (-loadblock).
+ assert(!dbp == !blocks_with_unknown_parent);
+
int64_t nStart = GetTimeMillis();
int nLoaded = 0;
@@ -4323,8 +4321,9 @@ void CChainState::LoadExternalBlockFile(FILE* fileIn, FlatFilePos* dbp)
if (hash != m_params.GetConsensus().hashGenesisBlock && !m_blockman.LookupBlockIndex(block.hashPrevBlock)) {
LogPrint(BCLog::REINDEX, "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(),
block.hashPrevBlock.ToString());
- if (dbp)
- mapBlocksUnknownParent.insert(std::make_pair(block.hashPrevBlock, *dbp));
+ if (dbp && blocks_with_unknown_parent) {
+ blocks_with_unknown_parent->emplace(block.hashPrevBlock, *dbp);
+ }
continue;
}
@@ -4353,13 +4352,15 @@ void CChainState::LoadExternalBlockFile(FILE* fileIn, FlatFilePos* dbp)
NotifyHeaderTip(*this);
+ if (!blocks_with_unknown_parent) continue;
+
// Recursively process earlier encountered successors of this block
std::deque<uint256> queue;
queue.push_back(hash);
while (!queue.empty()) {
uint256 head = queue.front();
queue.pop_front();
- std::pair<std::multimap<uint256, FlatFilePos>::iterator, std::multimap<uint256, FlatFilePos>::iterator> range = mapBlocksUnknownParent.equal_range(head);
+ auto range = blocks_with_unknown_parent->equal_range(head);
while (range.first != range.second) {
std::multimap<uint256, FlatFilePos>::iterator it = range.first;
std::shared_ptr<CBlock> pblockrecursive = std::make_shared<CBlock>();
@@ -4374,7 +4375,7 @@ void CChainState::LoadExternalBlockFile(FILE* fileIn, FlatFilePos* dbp)
}
}
range.first++;
- mapBlocksUnknownParent.erase(it);
+ blocks_with_unknown_parent->erase(it);
NotifyHeaderTip(*this);
}
}
@@ -4651,153 +4652,6 @@ bool CChainState::ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size)
return ret;
}
-static const uint64_t MEMPOOL_DUMP_VERSION = 1;
-
-bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mockable_fopen_function)
-{
- int64_t nExpiryTimeout = gArgs.GetIntArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
- FILE* filestr{mockable_fopen_function(gArgs.GetDataDirNet() / "mempool.dat", "rb")};
- CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
- if (file.IsNull()) {
- LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n");
- return false;
- }
-
- int64_t count = 0;
- int64_t expired = 0;
- int64_t failed = 0;
- int64_t already_there = 0;
- int64_t unbroadcast = 0;
- int64_t nNow = GetTime();
-
- try {
- uint64_t version;
- file >> version;
- if (version != MEMPOOL_DUMP_VERSION) {
- return false;
- }
- uint64_t num;
- file >> num;
- while (num) {
- --num;
- CTransactionRef tx;
- int64_t nTime;
- int64_t nFeeDelta;
- file >> tx;
- file >> nTime;
- file >> nFeeDelta;
-
- CAmount amountdelta = nFeeDelta;
- if (amountdelta) {
- pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
- }
- if (nTime > nNow - nExpiryTimeout) {
- LOCK(cs_main);
- const auto& accepted = AcceptToMemoryPool(active_chainstate, tx, nTime, /*bypass_limits=*/false, /*test_accept=*/false);
- if (accepted.m_result_type == MempoolAcceptResult::ResultType::VALID) {
- ++count;
- } else {
- // mempool may contain the transaction already, e.g. from
- // wallet(s) having loaded it while we were processing
- // mempool transactions; consider these as valid, instead of
- // failed, but mark them as 'already there'
- if (pool.exists(GenTxid::Txid(tx->GetHash()))) {
- ++already_there;
- } else {
- ++failed;
- }
- }
- } else {
- ++expired;
- }
- if (ShutdownRequested())
- return false;
- }
- std::map<uint256, CAmount> mapDeltas;
- file >> mapDeltas;
-
- for (const auto& i : mapDeltas) {
- pool.PrioritiseTransaction(i.first, i.second);
- }
-
- std::set<uint256> unbroadcast_txids;
- file >> unbroadcast_txids;
- unbroadcast = unbroadcast_txids.size();
- for (const auto& txid : unbroadcast_txids) {
- // Ensure transactions were accepted to mempool then add to
- // unbroadcast set.
- if (pool.get(txid) != nullptr) pool.AddUnbroadcastTx(txid);
- }
- } catch (const std::exception& e) {
- LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what());
- return false;
- }
-
- LogPrintf("Imported mempool transactions from disk: %i succeeded, %i failed, %i expired, %i already there, %i waiting for initial broadcast\n", count, failed, expired, already_there, unbroadcast);
- return true;
-}
-
-bool DumpMempool(const CTxMemPool& pool, FopenFn mockable_fopen_function, bool skip_file_commit)
-{
- int64_t start = GetTimeMicros();
-
- std::map<uint256, CAmount> mapDeltas;
- std::vector<TxMempoolInfo> vinfo;
- std::set<uint256> unbroadcast_txids;
-
- static Mutex dump_mutex;
- LOCK(dump_mutex);
-
- {
- LOCK(pool.cs);
- for (const auto &i : pool.mapDeltas) {
- mapDeltas[i.first] = i.second;
- }
- vinfo = pool.infoAll();
- unbroadcast_txids = pool.GetUnbroadcastTxs();
- }
-
- int64_t mid = GetTimeMicros();
-
- try {
- FILE* filestr{mockable_fopen_function(gArgs.GetDataDirNet() / "mempool.dat.new", "wb")};
- if (!filestr) {
- return false;
- }
-
- CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
-
- uint64_t version = MEMPOOL_DUMP_VERSION;
- file << version;
-
- file << (uint64_t)vinfo.size();
- for (const auto& i : vinfo) {
- file << *(i.tx);
- file << int64_t{count_seconds(i.m_time)};
- file << int64_t{i.nFeeDelta};
- mapDeltas.erase(i.tx->GetHash());
- }
-
- file << mapDeltas;
-
- LogPrintf("Writing %d unbroadcast transactions to disk.\n", unbroadcast_txids.size());
- file << unbroadcast_txids;
-
- if (!skip_file_commit && !FileCommit(file.Get()))
- throw std::runtime_error("FileCommit failed");
- file.fclose();
- if (!RenameOver(gArgs.GetDataDirNet() / "mempool.dat.new", gArgs.GetDataDirNet() / "mempool.dat")) {
- throw std::runtime_error("Rename failed");
- }
- int64_t last = GetTimeMicros();
- LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n", (mid-start)*MICRO, (last-mid)*MICRO);
- } catch (const std::exception& e) {
- LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
- return false;
- }
- return true;
-}
-
//! Guess how far we are in the verification process at the given block index
//! require cs_main if pindex has not been validated yet (because nChainTx might be unset)
double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pindex) {
@@ -4880,7 +4734,7 @@ const AssumeutxoData* ExpectedAssumeutxo(
}
bool ChainstateManager::ActivateSnapshot(
- CAutoFile& coins_file,
+ AutoFile& coins_file,
const SnapshotMetadata& metadata,
bool in_memory)
{
@@ -4975,7 +4829,7 @@ static void FlushSnapshotToDisk(CCoinsViewCache& coins_cache, bool snapshot_load
bool ChainstateManager::PopulateAndValidateSnapshot(
CChainState& snapshot_chainstate,
- CAutoFile& coins_file,
+ AutoFile& coins_file,
const SnapshotMetadata& metadata)
{
// It's okay to release cs_main before we're done using `coins_cache` because we know
@@ -4987,7 +4841,8 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
CBlockIndex* snapshot_start_block = WITH_LOCK(::cs_main, return m_blockman.LookupBlockIndex(base_blockhash));
if (!snapshot_start_block) {
- // Needed for GetUTXOStats and ExpectedAssumeutxo to determine the height and to avoid a crash when base_blockhash.IsNull()
+ // Needed for ComputeUTXOStats and ExpectedAssumeutxo to determine the
+ // height and to avoid a crash when base_blockhash.IsNull()
LogPrintf("[snapshot] Did not find snapshot start blockheader %s\n",
base_blockhash.ToString());
return false;
@@ -5095,26 +4950,26 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
assert(coins_cache.GetBestBlock() == base_blockhash);
- CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
auto breakpoint_fnc = [] { /* TODO insert breakpoint here? */ };
// As above, okay to immediately release cs_main here since no other context knows
// about the snapshot_chainstate.
CCoinsViewDB* snapshot_coinsdb = WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsDB());
- if (!GetUTXOStats(snapshot_coinsdb, m_blockman, stats, breakpoint_fnc)) {
+ const std::optional<CCoinsStats> maybe_stats = ComputeUTXOStats(CoinStatsHashType::HASH_SERIALIZED, snapshot_coinsdb, m_blockman, breakpoint_fnc);
+ if (!maybe_stats.has_value()) {
LogPrintf("[snapshot] failed to generate coins stats\n");
return false;
}
// Assert that the deserialized chainstate contents match the expected assumeutxo value.
- if (AssumeutxoHash{stats.hashSerialized} != au_data.hash_serialized) {
+ if (AssumeutxoHash{maybe_stats->hashSerialized} != au_data.hash_serialized) {
LogPrintf("[snapshot] bad snapshot content hash: expected %s, got %s\n",
- au_data.hash_serialized.ToString(), stats.hashSerialized.ToString());
+ au_data.hash_serialized.ToString(), maybe_stats->hashSerialized.ToString());
return false;
}
- snapshot_chainstate.m_chain.SetTip(snapshot_start_block);
+ snapshot_chainstate.m_chain.SetTip(*snapshot_start_block);
// The remainder of this function requires modifying data protected by cs_main.
LOCK(::cs_main);