aboutsummaryrefslogtreecommitdiff
path: root/src/validation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/validation.cpp')
-rw-r--r--src/validation.cpp345
1 files changed, 208 insertions, 137 deletions
diff --git a/src/validation.cpp b/src/validation.cpp
index 396fc0a1b..cf2f9dde6 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -20,6 +20,7 @@
#include <index/txindex.h>
#include <logging.h>
#include <logging/timer.h>
+#include <node/ui_interface.h>
#include <optional.h>
#include <policy/fees.h>
#include <policy/policy.h>
@@ -36,9 +37,9 @@
#include <tinyformat.h>
#include <txdb.h>
#include <txmempool.h>
-#include <ui_interface.h>
#include <uint256.h>
#include <undo.h>
+#include <util/check.h> // For NDEBUG compile time check
#include <util/moneystr.h>
#include <util/rbf.h>
#include <util/strencodings.h>
@@ -50,11 +51,6 @@
#include <string>
#include <boost/algorithm/string/replace.hpp>
-#include <boost/thread.hpp>
-
-#if defined(NDEBUG)
-# error "Bitcoin cannot be compiled without assertions."
-#endif
#define MICRO 0.000001
#define MILLI 0.001
@@ -71,12 +67,20 @@ static const unsigned int MAX_DISCONNECTED_TX_POOL_SIZE = 20000;
static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB
/** The pre-allocation chunk size for rev?????.dat files (since 0.8) */
static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB
-/** Time to wait (in seconds) between writing blocks/block index to disk. */
-static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60;
-/** Time to wait (in seconds) between flushing chainstate to disk. */
-static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60;
-/** Maximum age of our tip in seconds for us to be considered current for fee estimation */
-static const int64_t MAX_FEE_ESTIMATION_TIP_AGE = 3 * 60 * 60;
+/** Time to wait between writing blocks/block index to disk. */
+static constexpr std::chrono::hours DATABASE_WRITE_INTERVAL{1};
+/** Time to wait between flushing chainstate to disk. */
+static constexpr std::chrono::hours DATABASE_FLUSH_INTERVAL{24};
+/** Maximum age of our tip for us to be considered current for fee estimation */
+static constexpr std::chrono::hours MAX_FEE_ESTIMATION_TIP_AGE{3};
+const std::vector<std::string> CHECKLEVEL_DOC {
+ "level 0 reads the blocks from disk",
+ "level 1 verifies block validity",
+ "level 2 verifies undo data",
+ "level 3 checks disconnection of tip blocks",
+ "level 4 tries to reconnect the blocks",
+ "each level includes the checks of the previous levels",
+};
bool CBlockIndexWorkComparator::operator()(const CBlockIndex *pa, const CBlockIndex *pb) const {
// First sort by most total work, ...
@@ -135,7 +139,6 @@ bool fPruneMode = false;
bool fRequireStandard = true;
bool fCheckBlockIndex = false;
bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED;
-size_t nCoinCacheUsage = 5000 * 300;
uint64_t nPruneTarget = 0;
int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE;
@@ -295,7 +298,7 @@ bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flag
prevheights[txinIndex] = coin.nHeight;
}
}
- lockPair = CalculateSequenceLocks(tx, flags, &prevheights, index);
+ lockPair = CalculateSequenceLocks(tx, flags, prevheights, index);
if (lp) {
lp->height = lockPair.first;
lp->time = lockPair.second;
@@ -347,7 +350,7 @@ static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
AssertLockHeld(cs_main);
if (::ChainstateActive().IsInitialBlockDownload())
return false;
- if (::ChainActive().Tip()->GetBlockTime() < (GetTime() - MAX_FEE_ESTIMATION_TIP_AGE))
+ if (::ChainActive().Tip()->GetBlockTime() < count_seconds(GetTime<std::chrono::seconds>() - MAX_FEE_ESTIMATION_TIP_AGE))
return false;
if (::ChainActive().Height() < pindexBestHeader->nHeight - 1)
return false;
@@ -569,8 +572,9 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
CAmount& nConflictingFees = ws.m_conflicting_fees;
size_t& nConflictingSize = ws.m_conflicting_size;
- if (!CheckTransaction(tx, state))
+ if (!CheckTransaction(tx, state)) {
return false; // state filled in by CheckTransaction
+ }
// Coinbase is only valid in a block, not as a loose transaction
if (tx.IsCoinBase())
@@ -680,12 +684,13 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
CAmount nFees = 0;
if (!Consensus::CheckTxInputs(tx, state, m_view, GetSpendHeight(m_view), nFees)) {
- return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), state.ToString());
+ return false; // state filled in by CheckTxInputs
}
// Check for non-standard pay-to-script-hash in inputs
- if (fRequireStandard && !AreInputsStandard(tx, m_view))
- return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "bad-txns-nonstandard-inputs");
+ if (fRequireStandard && !AreInputsStandard(tx, m_view)) {
+ return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
+ }
// Check for non-standard witness in P2WSH
if (tx.HasWitness() && fRequireStandard && !IsWitnessStandard(tx, m_view))
@@ -934,7 +939,7 @@ bool MemPoolAccept::PolicyScriptChecks(ATMPArgs& args, Workspace& ws, Precompute
if (!tx.HasWitness() && CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, txdata) &&
!CheckInputScripts(tx, state_dummy, m_view, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, txdata)) {
// Only the witness is missing, so the transaction itself may be fine.
- state.Invalid(TxValidationResult::TX_WITNESS_MUTATED,
+ state.Invalid(TxValidationResult::TX_WITNESS_STRIPPED,
state.GetRejectReason(), state.GetDebugMessage());
}
return false; // state filled in by CheckInputScripts
@@ -1084,45 +1089,33 @@ bool AcceptToMemoryPool(CTxMemPool& pool, TxValidationState &state, const CTrans
return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, GetTime(), plTxnReplaced, bypass_limits, nAbsurdFee, test_accept);
}
-/**
- * Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock.
- * If blockIndex is provided, the transaction is fetched from the corresponding block.
- */
-bool GetTransaction(const uint256& hash, CTransactionRef& txOut, const Consensus::Params& consensusParams, uint256& hashBlock, const CBlockIndex* const block_index)
+CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock)
{
LOCK(cs_main);
- if (!block_index) {
- CTransactionRef ptx = mempool.get(hash);
- if (ptx) {
- txOut = ptx;
- return true;
- }
-
- if (g_txindex) {
- return g_txindex->FindTx(hash, hashBlock, txOut);
- }
- } else {
+ if (block_index) {
CBlock block;
if (ReadBlockFromDisk(block, block_index, consensusParams)) {
for (const auto& tx : block.vtx) {
if (tx->GetHash() == hash) {
- txOut = tx;
hashBlock = block_index->GetBlockHash();
- return true;
+ return tx;
}
}
}
+ return nullptr;
}
-
- return false;
+ if (mempool) {
+ CTransactionRef ptx = mempool->get(hash);
+ if (ptx) return ptx;
+ }
+ if (g_txindex) {
+ CTransactionRef tx;
+ if (g_txindex->FindTx(hash, hashBlock, tx)) return tx;
+ }
+ return nullptr;
}
-
-
-
-
-
//////////////////////////////////////////////////////////////////////////////
//
// CBlock and CBlockIndex
@@ -1206,8 +1199,8 @@ bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, c
if (memcmp(blk_start, message_start, CMessageHeader::MESSAGE_START_SIZE)) {
return error("%s: Block magic mismatch for %s: %s versus expected %s", __func__, pos.ToString(),
- HexStr(blk_start, blk_start + CMessageHeader::MESSAGE_START_SIZE),
- HexStr(message_start, message_start + CMessageHeader::MESSAGE_START_SIZE));
+ HexStr(blk_start),
+ HexStr(message_start));
}
if (blk_size > MAX_SIZE) {
@@ -1279,9 +1272,10 @@ void CChainState::InitCoinsDB(
leveldb_name, cache_size_bytes, in_memory, should_wipe);
}
-void CChainState::InitCoinsCache()
+void CChainState::InitCoinsCache(size_t cache_size_bytes)
{
assert(m_coins_views != nullptr);
+ m_coinstip_cache_size_bytes = cache_size_bytes;
m_coins_views->InitCache();
}
@@ -1314,12 +1308,6 @@ bool CChainState::IsInitialBlockDownload() const
static CBlockIndex *pindexBestForkTip = nullptr, *pindexBestForkBase = nullptr;
-BlockMap& BlockIndex()
-{
- LOCK(::cs_main);
- return g_chainman.m_blockman.m_block_index;
-}
-
static void AlertNotify(const std::string& strMessage)
{
uiInterface.NotifyAlertChanged();
@@ -1423,12 +1411,12 @@ void static InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(c
pindexBestHeader = ::ChainActive().Tip();
}
- LogPrintf("%s: invalid block=%s height=%d log2_work=%.8g date=%s\n", __func__,
+ LogPrintf("%s: invalid block=%s height=%d log2_work=%f date=%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight,
log(pindexNew->nChainWork.getdouble())/log(2.0), FormatISO8601DateTime(pindexNew->GetBlockTime()));
CBlockIndex *tip = ::ChainActive().Tip();
assert (tip);
- LogPrintf("%s: current best=%s height=%d log2_work=%.8g date=%s\n", __func__,
+ LogPrintf("%s: current best=%s height=%d log2_work=%f date=%s\n", __func__,
tip->GetBlockHash().ToString(), ::ChainActive().Height(), log(tip->nChainWork.getdouble())/log(2.0),
FormatISO8601DateTime(tip->GetBlockTime()));
CheckForkWarningConditions();
@@ -1481,14 +1469,21 @@ int GetSpendHeight(const CCoinsViewCache& inputs)
}
-static CuckooCache::cache<uint256, SignatureCacheHasher> scriptExecutionCache;
-static uint256 scriptExecutionCacheNonce(GetRandHash());
+static CuckooCache::cache<uint256, SignatureCacheHasher> g_scriptExecutionCache;
+static CSHA256 g_scriptExecutionCacheHasher;
void InitScriptExecutionCache() {
+ // Setup the salted hasher
+ uint256 nonce = GetRandHash();
+ // We want the nonce to be 64 bytes long to force the hasher to process
+ // this chunk, which makes later hash computations more efficient. We
+ // 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.GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
- size_t nElems = scriptExecutionCache.setup_bytes(nMaxCacheSize);
+ 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);
}
@@ -1526,12 +1521,10 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
// properly commits to the scriptPubKey in the inputs view of that
// transaction).
uint256 hashCacheEntry;
- // We only use the first 19 bytes of nonce to avoid a second SHA
- // round - giving us 19 + 32 + 4 = 55 bytes (+ 8 + 1 = 64)
- static_assert(55 - sizeof(flags) - 32 >= 128/8, "Want at least 128 bits of nonce for script execution cache");
- CSHA256().Write(scriptExecutionCacheNonce.begin(), 55 - sizeof(flags) - 32).Write(tx.GetWitnessHash().begin(), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin());
+ CSHA256 hasher = g_scriptExecutionCacheHasher;
+ hasher.Write(tx.GetWitnessHash().begin(), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin());
AssertLockHeld(cs_main); //TODO: Remove this requirement by making CuckooCache not require external locks
- if (scriptExecutionCache.contains(hashCacheEntry, !cacheFullScriptStore)) {
+ if (g_scriptExecutionCache.contains(hashCacheEntry, !cacheFullScriptStore)) {
return true;
}
@@ -1586,7 +1579,7 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const C
if (cacheFullScriptStore && !pvChecks) {
// We executed all of the provided scripts, and were told to
// cache the result. Do so now.
- scriptExecutionCache.insert(hashCacheEntry);
+ g_scriptExecutionCache.insert(hashCacheEntry);
}
return true;
@@ -1651,23 +1644,21 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex)
}
/** Abort with a message */
-// TODO: AbortNode() should take bilingual_str userMessage parameter.
-static bool AbortNode(const std::string& strMessage, const std::string& userMessage = "", unsigned int prefix = 0)
+static bool AbortNode(const std::string& strMessage, bilingual_str user_message = bilingual_str())
{
- SetMiscWarning(strMessage);
+ SetMiscWarning(Untranslated(strMessage));
LogPrintf("*** %s\n", strMessage);
- if (!userMessage.empty()) {
- uiInterface.ThreadSafeMessageBox(Untranslated(userMessage), "", CClientUIInterface::MSG_ERROR | prefix);
- } else {
- uiInterface.ThreadSafeMessageBox(_("Error: A fatal internal error occurred, see debug.log for details"), "", CClientUIInterface::MSG_ERROR | CClientUIInterface::MSG_NOPREFIX);
+ if (user_message.empty()) {
+ user_message = _("A fatal internal error occurred, see debug.log for details");
}
+ AbortError(user_message);
StartShutdown();
return false;
}
-static bool AbortNode(BlockValidationState& state, const std::string& strMessage, const std::string& userMessage = "", unsigned int prefix = 0)
+static bool AbortNode(BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage = bilingual_str())
{
- AbortNode(strMessage, userMessage, prefix);
+ AbortNode(strMessage, userMessage);
return state.Error(strMessage);
}
@@ -1765,19 +1756,24 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
}
-void static FlushBlockFile(bool fFinalize = false)
+static void FlushUndoFile(int block_file, bool finalize = false)
{
- LOCK(cs_LastBlockFile);
+ FlatFilePos undo_pos_old(block_file, vinfoBlockFile[block_file].nUndoSize);
+ if (!UndoFileSeq().Flush(undo_pos_old, finalize)) {
+ AbortNode("Flushing undo file to disk failed. This is likely the result of an I/O error.");
+ }
+}
+static void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false)
+{
+ LOCK(cs_LastBlockFile);
FlatFilePos block_pos_old(nLastBlockFile, vinfoBlockFile[nLastBlockFile].nSize);
- FlatFilePos undo_pos_old(nLastBlockFile, vinfoBlockFile[nLastBlockFile].nUndoSize);
-
- bool status = true;
- status &= BlockFileSeq().Flush(block_pos_old, fFinalize);
- status &= UndoFileSeq().Flush(undo_pos_old, fFinalize);
- if (!status) {
+ if (!BlockFileSeq().Flush(block_pos_old, fFinalize)) {
AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error.");
}
+ // we do not always flush the undo file, as the chain tip may be lagging behind the incoming blocks,
+ // e.g. during IBD or a sync after a node going offline
+ if (!fFinalize || finalize_undo) FlushUndoFile(nLastBlockFile, finalize_undo);
}
static bool FindUndoPos(BlockValidationState &state, int nFile, FlatFilePos &pos, unsigned int nAddSize);
@@ -1791,6 +1787,14 @@ static bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationSt
return error("ConnectBlock(): FindUndoPos failed");
if (!UndoWriteToDisk(blockundo, _pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart()))
return AbortNode(state, "Failed to write undo data");
+ // rev files are written in block height order, whereas blk files are written as blocks come in (often out of order)
+ // we want to flush the rev (undo) file once we've written the last block, which is indicated by the last height
+ // in the block file info as below; note that this does not catch the case where the undo writes are keeping up
+ // with the block writes (usually when a synced up node is getting newly mined blocks) -- this case is caught in
+ // the FindBlockPos function
+ if (_pos.nFile < nLastBlockFile && static_cast<uint32_t>(pindex->nHeight) == vinfoBlockFile[_pos.nFile].nHeightLast) {
+ FlushUndoFile(_pos.nFile, true);
+ }
// update nUndoPos in block index
pindex->nUndoPos = _pos.nPos;
@@ -2147,7 +2151,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
prevheights[j] = view.AccessCoin(tx.vin[j].prevout).nHeight;
}
- if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) {
+ if (!SequenceLocks(tx, nLockTimeFlags, prevheights, *pindex)) {
LogPrintf("ERROR: %s: contains a non-BIP68-final transaction\n", __func__);
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-txns-nonfinal");
}
@@ -2224,20 +2228,20 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
return true;
}
-CoinsCacheSizeState CChainState::GetCoinsCacheSizeState(const CTxMemPool& tx_pool)
+CoinsCacheSizeState CChainState::GetCoinsCacheSizeState(const CTxMemPool* tx_pool)
{
return this->GetCoinsCacheSizeState(
tx_pool,
- nCoinCacheUsage,
+ m_coinstip_cache_size_bytes,
gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
}
CoinsCacheSizeState CChainState::GetCoinsCacheSizeState(
- const CTxMemPool& tx_pool,
+ const CTxMemPool* tx_pool,
size_t max_coins_cache_size_bytes,
size_t max_mempool_size_bytes)
{
- int64_t nMempoolUsage = tx_pool.DynamicMemoryUsage();
+ const int64_t nMempoolUsage = tx_pool ? tx_pool->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);
@@ -2264,8 +2268,8 @@ bool CChainState::FlushStateToDisk(
{
LOCK(cs_main);
assert(this->CanFlushToDisk());
- static int64_t nLastWrite = 0;
- static int64_t nLastFlush = 0;
+ static std::chrono::microseconds nLastWrite{0};
+ static std::chrono::microseconds nLastFlush{0};
std::set<int> setFilesToPrune;
bool full_flush_completed = false;
@@ -2276,7 +2280,7 @@ bool CChainState::FlushStateToDisk(
{
bool fFlushForPrune = false;
bool fDoFullFlush = false;
- CoinsCacheSizeState cache_state = GetCoinsCacheSizeState(::mempool);
+ CoinsCacheSizeState cache_state = GetCoinsCacheSizeState(&::mempool);
LOCK(cs_LastBlockFile);
if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) {
if (nManualPruneHeight > 0) {
@@ -2297,12 +2301,12 @@ bool CChainState::FlushStateToDisk(
}
}
}
- int64_t nNow = GetTimeMicros();
+ const auto nNow = GetTime<std::chrono::microseconds>();
// Avoid writing/flushing immediately after startup.
- if (nLastWrite == 0) {
+ if (nLastWrite.count() == 0) {
nLastWrite = nNow;
}
- if (nLastFlush == 0) {
+ if (nLastFlush.count() == 0) {
nLastFlush = nNow;
}
// The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing).
@@ -2310,16 +2314,16 @@ bool CChainState::FlushStateToDisk(
// The cache is over the limit, we have to write now.
bool fCacheCritical = mode == FlushStateMode::IF_NEEDED && cache_state >= CoinsCacheSizeState::CRITICAL;
// It's been a while since we wrote the block index to disk. Do this frequently, so we don't need to redownload after a crash.
- bool fPeriodicWrite = mode == FlushStateMode::PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000;
+ bool fPeriodicWrite = mode == FlushStateMode::PERIODIC && nNow > nLastWrite + DATABASE_WRITE_INTERVAL;
// It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage.
- bool fPeriodicFlush = mode == FlushStateMode::PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000;
+ bool fPeriodicFlush = mode == FlushStateMode::PERIODIC && nNow > nLastFlush + DATABASE_FLUSH_INTERVAL;
// Combine all conditions that result in a full cache flush.
fDoFullFlush = (mode == FlushStateMode::ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune;
// Write blocks and block index to disk.
if (fDoFullFlush || fPeriodicWrite) {
// Depend on nMinDiskSpace to ensure we can write block index
if (!CheckDiskSpace(GetBlocksDir())) {
- return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
+ return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
}
{
LOG_TIME_MILLIS_WITH_CATEGORY("write block and undo data to disk", BCLog::BENCH);
@@ -2367,7 +2371,7 @@ bool CChainState::FlushStateToDisk(
// an overestimation, as most will delete an existing entry or
// overwrite one. Still, use a conservative safety factor of 2.
if (!CheckDiskSpace(GetDataDir(), 48 * 2 * 2 * CoinsTip().GetCacheSize())) {
- return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
+ return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
}
// Flush the chainstate (which may refer to block index entries).
if (!CoinsTip().Flush())
@@ -2404,20 +2408,20 @@ void CChainState::PruneAndFlush() {
}
}
-static void DoWarning(const std::string& strWarning)
+static void DoWarning(const bilingual_str& warning)
{
static bool fWarned = false;
- SetMiscWarning(strWarning);
+ SetMiscWarning(warning);
if (!fWarned) {
- AlertNotify(strWarning);
+ AlertNotify(warning.original);
fWarned = true;
}
}
/** Private helper function that concatenates warning messages. */
-static void AppendWarning(std::string& res, const std::string& warn)
+static void AppendWarning(bilingual_str& res, const bilingual_str& warn)
{
- if (!res.empty()) res += ", ";
+ if (!res.empty()) res += Untranslated(", ");
res += warn;
}
@@ -2434,7 +2438,7 @@ void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainPar
g_best_block_cv.notify_all();
}
- std::string warningMessages;
+ bilingual_str warning_messages;
if (!::ChainstateActive().IsInitialBlockDownload())
{
int nUpgraded = 0;
@@ -2443,11 +2447,11 @@ void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainPar
WarningBitsConditionChecker checker(bit);
ThresholdState state = checker.GetStateFor(pindex, chainParams.GetConsensus(), warningcache[bit]);
if (state == ThresholdState::ACTIVE || state == ThresholdState::LOCKED_IN) {
- const std::string strWarning = strprintf(_("Warning: unknown new rules activated (versionbit %i)").translated, bit);
+ const bilingual_str warning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit);
if (state == ThresholdState::ACTIVE) {
- DoWarning(strWarning);
+ DoWarning(warning);
} else {
- AppendWarning(warningMessages, strWarning);
+ AppendWarning(warning_messages, warning);
}
}
}
@@ -2460,14 +2464,14 @@ void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainPar
pindex = pindex->pprev;
}
if (nUpgraded > 0)
- AppendWarning(warningMessages, strprintf(_("%d of last 100 blocks have unexpected version").translated, nUpgraded));
+ AppendWarning(warning_messages, strprintf(_("%d of last 100 blocks have unexpected version"), nUpgraded));
}
- LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n", __func__,
+ LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx,
FormatISO8601DateTime(pindexNew->GetBlockTime()),
GuessVerificationProgress(chainParams.TxData(), pindexNew), ::ChainstateActive().CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), ::ChainstateActive().CoinsTip().GetCacheSize(),
- !warningMessages.empty() ? strprintf(" warning='%s'", warningMessages) : "");
+ !warning_messages.empty() ? strprintf(" warning='%s'", warning_messages.original) : "");
}
@@ -2854,8 +2858,6 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar
CBlockIndex *pindexNewTip = nullptr;
int nStopAtHeight = gArgs.GetArg("-stopatheight", DEFAULT_STOPATHEIGHT);
do {
- boost::this_thread::interruption_point();
-
// Block until the validation queue drains. This should largely
// never happen in normal operation, however may happen during
// reindex, causing memory blowup if we run too far ahead.
@@ -2924,8 +2926,7 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar
// never shutdown before connecting the genesis block during LoadChainTip(). Previously this
// caused an assert() failure during shutdown in such cases as the UTXO DB flushing checks
// that the best block hash is non-null.
- if (ShutdownRequested())
- break;
+ if (ShutdownRequested()) break;
} while (pindexNewTip != pindexMostWork);
CheckBlockIndex(chainparams.GetConsensus());
@@ -3243,8 +3244,13 @@ static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int n
vinfoBlockFile.resize(nFile + 1);
}
+ bool finalize_undo = false;
if (!fKnown) {
while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) {
+ // when the undo file is keeping up with the block file, we want to flush it explicitly
+ // when it is lagging behind (more blocks arrive than are being connected), we let the
+ // undo block write case handle it
+ finalize_undo = (vinfoBlockFile[nFile].nHeightLast == (unsigned int)ChainActive().Tip()->nHeight);
nFile++;
if (vinfoBlockFile.size() <= nFile) {
vinfoBlockFile.resize(nFile + 1);
@@ -3258,7 +3264,7 @@ static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int n
if (!fKnown) {
LogPrintf("Leaving block file %i: %s\n", nLastBlockFile, vinfoBlockFile[nLastBlockFile].ToString());
}
- FlushBlockFile(!fKnown);
+ FlushBlockFile(!fKnown, finalize_undo);
nLastBlockFile = nFile;
}
@@ -3272,7 +3278,7 @@ static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int n
bool out_of_space;
size_t bytes_allocated = BlockFileSeq().Allocate(pos, nAddSize, out_of_space);
if (out_of_space) {
- return AbortNode("Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
+ return AbortNode("Disk space is too low!", _("Disk space is too low!"));
}
if (bytes_allocated != 0 && fPruneMode) {
fCheckForPruning = true;
@@ -3296,7 +3302,7 @@ static bool FindUndoPos(BlockValidationState &state, int nFile, FlatFilePos &pos
bool out_of_space;
size_t bytes_allocated = UndoFileSeq().Allocate(pos, nAddSize, out_of_space);
if (out_of_space) {
- return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
+ return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
}
if (bytes_allocated != 0 && fPruneMode) {
fCheckForPruning = true;
@@ -3429,7 +3435,7 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
if (consensusParams.SegwitHeight != std::numeric_limits<int>::max()) {
if (commitpos == -1) {
uint256 witnessroot = BlockWitnessMerkleRoot(block, nullptr);
- CHash256().Write(witnessroot.begin(), 32).Write(ret.data(), 32).Finalize(witnessroot.begin());
+ CHash256().Write(witnessroot).Write(ret).Finalize(witnessroot);
CTxOut out;
out.nValue = 0;
out.scriptPubKey.resize(MINIMUM_WITNESS_COMMITMENT);
@@ -3574,7 +3580,7 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat
if (block.vtx[0]->vin[0].scriptWitness.stack.size() != 1 || block.vtx[0]->vin[0].scriptWitness.stack[0].size() != 32) {
return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "bad-witness-nonce-size", strprintf("%s : invalid witness reserved value size", __func__));
}
- CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0]->vin[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin());
+ CHash256().Write(hashWitness).Write(block.vtx[0]->vin[0].scriptWitness.stack[0]).Finalize(hashWitness);
if (memcmp(hashWitness.begin(), &block.vtx[0]->vout[commitpos].scriptPubKey[6], 32)) {
return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "bad-witness-merkle-match", strprintf("%s : witness merkle commitment mismatch", __func__));
}
@@ -3624,8 +3630,10 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationS
return true;
}
- if (!CheckBlockHeader(block, state, chainparams.GetConsensus()))
- return error("%s: Consensus::CheckBlockHeader: %s, %s", __func__, hash.ToString(), state.ToString());
+ if (!CheckBlockHeader(block, state, chainparams.GetConsensus())) {
+ LogPrint(BCLog::VALIDATION, "%s: Consensus::CheckBlockHeader: %s, %s\n", __func__, hash.ToString(), state.ToString());
+ return false;
+ }
// Get prev block index
CBlockIndex* pindexPrev = nullptr;
@@ -4267,7 +4275,6 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
int reportDone = 0;
LogPrintf("[0%%]..."); /* Continued */
for (pindex = ::ChainActive().Tip(); pindex && pindex->pprev; pindex = pindex->pprev) {
- boost::this_thread::interruption_point();
const int percentageDone = std::max(1, std::min(99, (int)(((double)(::ChainActive().Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))));
if (reportDone < percentageDone/10) {
// report every 10% step
@@ -4300,7 +4307,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
}
}
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks
- if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + ::ChainstateActive().CoinsTip().DynamicMemoryUsage()) <= nCoinCacheUsage) {
+ if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + ::ChainstateActive().CoinsTip().DynamicMemoryUsage()) <= ::ChainstateActive().m_coinstip_cache_size_bytes) {
assert(coins.GetBestBlock() == pindex->GetBlockHash());
DisconnectResult res = ::ChainstateActive().DisconnectBlock(block, pindex, coins);
if (res == DISCONNECT_FAILED) {
@@ -4313,8 +4320,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
nGoodTransactions += block.vtx.size();
}
}
- if (ShutdownRequested())
- return true;
+ if (ShutdownRequested()) return true;
}
if (pindexFailure)
return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", ::ChainActive().Height() - pindexFailure->nHeight + 1, nGoodTransactions);
@@ -4325,7 +4331,6 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
// check level 4: try reconnecting blocks
if (nCheckLevel >= 4) {
while (pindex != ::ChainActive().Tip()) {
- boost::this_thread::interruption_point();
const int percentageDone = std::max(1, std::min(99, 100 - (int)(((double)(::ChainActive().Height() - pindex->nHeight)) / (double)nCheckDepth * 50)));
if (reportDone < percentageDone/10) {
// report every 10% step
@@ -4339,6 +4344,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
if (!::ChainstateActive().ConnectBlock(block, state, pindex, coins, chainparams))
return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), state.ToString());
+ if (ShutdownRequested()) return true;
}
}
@@ -4582,13 +4588,13 @@ void CChainState::UnloadBlockIndex() {
// May NOT be used after any connections are up as much
// of the peer-processing logic assumes a consistent
// block index state
-void UnloadBlockIndex()
+void UnloadBlockIndex(CTxMemPool* mempool)
{
LOCK(cs_main);
g_chainman.Unload();
pindexBestInvalid = nullptr;
pindexBestHeader = nullptr;
- mempool.clear();
+ if (mempool) mempool->clear();
vinfoBlockFile.clear();
nLastBlockFile = 0;
setDirtyBlockIndex.clear();
@@ -4693,7 +4699,6 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi
if (dbp)
dbp->nPos = nBlockPos;
blkdat.SetLimit(nBlockPos + nSize);
- blkdat.SetPos(nBlockPos);
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
CBlock& block = *pblock;
blkdat >> block;
@@ -4965,6 +4970,39 @@ std::string CChainState::ToString()
tip ? tip->nHeight : -1, tip ? tip->GetBlockHash().ToString() : "null");
}
+bool CChainState::ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size)
+{
+ if (coinstip_size == m_coinstip_cache_size_bytes &&
+ coinsdb_size == m_coinsdb_cache_size_bytes) {
+ // Cache sizes are unchanged, no need to continue.
+ return true;
+ }
+ size_t old_coinstip_size = m_coinstip_cache_size_bytes;
+ m_coinstip_cache_size_bytes = coinstip_size;
+ m_coinsdb_cache_size_bytes = coinsdb_size;
+ CoinsDB().ResizeCache(coinsdb_size);
+
+ LogPrintf("[%s] resized coinsdb cache to %.1f MiB\n",
+ this->ToString(), coinsdb_size * (1.0 / 1024 / 1024));
+ LogPrintf("[%s] resized coinstip cache to %.1f MiB\n",
+ this->ToString(), coinstip_size * (1.0 / 1024 / 1024));
+
+ BlockValidationState state;
+ const CChainParams& chainparams = Params();
+
+ bool ret;
+
+ if (coinstip_size > old_coinstip_size) {
+ // Likely no need to flush if cache sizes have grown.
+ ret = FlushStateToDisk(chainparams, state, FlushStateMode::IF_NEEDED);
+ } else {
+ // Otherwise, flush state to disk and deallocate the in-memory coins map.
+ ret = FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS);
+ CoinsTip().ReallocateCache();
+ }
+ return ret;
+}
+
std::string CBlockFileInfo::ToString() const
{
return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, FormatISO8601Date(nTimeFirst), FormatISO8601Date(nTimeLast));
@@ -5068,19 +5106,22 @@ bool LoadMempool(CTxMemPool& pool)
}
// TODO: remove this try except in v0.22
+ std::map<uint256, uint256> unbroadcast_txids;
try {
- std::set<uint256> unbroadcast_txids;
file >> unbroadcast_txids;
unbroadcast = unbroadcast_txids.size();
-
- for (const auto& txid : unbroadcast_txids) {
- pool.AddUnbroadcastTx(txid);
- }
} catch (const std::exception&) {
// mempool.dat files created prior to v0.21 will not have an
// unbroadcast set. No need to log a failure if parsing fails here.
}
-
+ for (const auto& elem : unbroadcast_txids) {
+ // Don't add unbroadcast transactions that didn't get back into the
+ // mempool.
+ const CTransactionRef& added_tx = pool.get(elem.first);
+ if (added_tx != nullptr) {
+ pool.AddUnbroadcastTx(elem.first, added_tx->GetWitnessHash());
+ }
+ }
} catch (const std::exception& e) {
LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what());
return false;
@@ -5096,7 +5137,7 @@ bool DumpMempool(const CTxMemPool& pool)
std::map<uint256, CAmount> mapDeltas;
std::vector<TxMempoolInfo> vinfo;
- std::set<uint256> unbroadcast_txids;
+ std::map<uint256, uint256> unbroadcast_txids;
static Mutex dump_mutex;
LOCK(dump_mutex);
@@ -5228,10 +5269,10 @@ CChainState& ChainstateManager::InitializeChainstate(const uint256& snapshot_blo
return *to_modify;
}
-CChain& ChainstateManager::ActiveChain() const
+CChainState& ChainstateManager::ActiveChainstate() const
{
assert(m_active_chainstate);
- return m_active_chainstate->m_chain;
+ return *m_active_chainstate;
}
bool ChainstateManager::IsSnapshotActive() const
@@ -5270,3 +5311,33 @@ void ChainstateManager::Reset()
m_active_chainstate = nullptr;
m_snapshot_validated = false;
}
+
+void ChainstateManager::MaybeRebalanceCaches()
+{
+ if (m_ibd_chainstate && !m_snapshot_chainstate) {
+ LogPrintf("[snapshot] allocating all cache to the IBD chainstate\n");
+ // Allocate everything to the IBD chainstate.
+ m_ibd_chainstate->ResizeCoinsCaches(m_total_coinstip_cache, m_total_coinsdb_cache);
+ }
+ else if (m_snapshot_chainstate && !m_ibd_chainstate) {
+ LogPrintf("[snapshot] allocating all cache to the snapshot chainstate\n");
+ // Allocate everything to the snapshot chainstate.
+ m_snapshot_chainstate->ResizeCoinsCaches(m_total_coinstip_cache, m_total_coinsdb_cache);
+ }
+ else if (m_ibd_chainstate && m_snapshot_chainstate) {
+ // If both chainstates exist, determine who needs more cache based on IBD status.
+ //
+ // Note: shrink caches first so that we don't inadvertently overwhelm available memory.
+ if (m_snapshot_chainstate->IsInitialBlockDownload()) {
+ m_ibd_chainstate->ResizeCoinsCaches(
+ m_total_coinstip_cache * 0.05, m_total_coinsdb_cache * 0.05);
+ m_snapshot_chainstate->ResizeCoinsCaches(
+ m_total_coinstip_cache * 0.95, m_total_coinsdb_cache * 0.95);
+ } else {
+ m_snapshot_chainstate->ResizeCoinsCaches(
+ m_total_coinstip_cache * 0.05, m_total_coinsdb_cache * 0.05);
+ m_ibd_chainstate->ResizeCoinsCaches(
+ m_total_coinstip_cache * 0.95, m_total_coinsdb_cache * 0.95);
+ }
+ }
+}