Merge 85883a9f8e into merged_master (Bitcoin PR #16443)

This commit is contained in:
Andrew Poelstra 2020-11-09 21:20:14 +00:00
commit 028f2d94d1
17 changed files with 247 additions and 100 deletions

View file

@ -85,11 +85,17 @@ namespace {
BlockManager g_blockman;
} // anon namespace
static CChainState g_chainstate(g_blockman);
std::unique_ptr<CChainState> g_chainstate;
CChainState& ChainstateActive() { return g_chainstate; }
CChainState& ChainstateActive() {
assert(g_chainstate);
return *g_chainstate;
}
CChain& ChainActive() { return g_chainstate.m_chain; }
CChain& ChainActive() {
assert(g_chainstate);
return g_chainstate->m_chain;
}
/**
* Mutex to guard access to validation specific variables, such as reading
@ -176,8 +182,6 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc
return chain.Genesis();
}
std::unique_ptr<CCoinsViewDB> pcoinsdbview;
std::unique_ptr<CCoinsViewCache> pcoinsTip;
std::unique_ptr<CBlockTreeDB> pblocktree;
// See definition for documentation
@ -266,8 +270,8 @@ bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flag
lockPair.second = lp->time;
}
else {
// pcoinsTip contains the UTXO set for ::ChainActive().Tip()
CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool);
// CoinsTip() contains the UTXO set for ::ChainActive().Tip()
CCoinsViewMemPool viewMemPool(&::ChainstateActive().CoinsTip(), pool);
std::vector<int> prevheights;
prevheights.resize(tx.vin.size());
for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
@ -322,7 +326,8 @@ bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flag
// Returns the script flags which should be checked for a given block
static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& chainparams);
static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age)
EXCLUSIVE_LOCKS_REQUIRED(pool.cs, ::cs_main)
{
int expired = pool.Expire(GetTime() - age);
if (expired != 0) {
@ -332,7 +337,7 @@ static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age)
std::vector<COutPoint> vNoSpendsRemaining;
pool.TrimToSize(limit, &vNoSpendsRemaining);
for (const COutPoint& removed : vNoSpendsRemaining)
pcoinsTip->Uncache(removed);
::ChainstateActive().CoinsTip().Uncache(removed);
}
static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@ -394,7 +399,7 @@ static void UpdateMempoolForReorg(DisconnectedBlockTransactions& disconnectpool,
mempool.UpdateTransactionsFromBlock(vHashUpdate);
// We also need to remove any now-immature transactions
mempool.removeForReorg(pcoinsTip.get(), ::ChainActive().Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
mempool.removeForReorg(&::ChainstateActive().CoinsTip(), ::ChainActive().Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
// Re-limit mempool size, in case we added any transactions
LimitMempoolSize(mempool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
}
@ -430,7 +435,7 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt
assert(txFrom->vout.size() > txin.prevout.n);
assert(txFrom->vout[txin.prevout.n] == coin.out);
} else {
const Coin& coinFromDisk = pcoinsTip->AccessCoin(txin.prevout);
const Coin& coinFromDisk = ::ChainstateActive().CoinsTip().AccessCoin(txin.prevout);
assert(!coinFromDisk.IsSpent());
assert(coinFromDisk.out == coin.out);
}
@ -531,7 +536,8 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
std::set<std::pair<uint256, COutPoint> > setPeginsSpent;
LockPoints lp;
CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool);
CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip();
CCoinsViewMemPool viewMemPool(&coins_cache, pool);
view.SetBackend(viewMemPool);
// Quickly check for peg-in witness data on non-peg-in inputs
@ -570,18 +576,18 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
continue;
}
if (!pcoinsTip->HaveCoinInCache(txin.prevout)) {
if (!coins_cache.HaveCoinInCache(txin.prevout)) {
coins_to_uncache.push_back(txin.prevout);
}
// Note: this call may add txin.prevout to the coins cache
// (pcoinsTip.cacheCoins) by way of FetchCoin(). It should be removed
// (CoinsTip().cacheCoins) by way of FetchCoin(). It should be removed
// later (via coins_to_uncache) if this tx turns out to be invalid.
if (!view.HaveCoin(txin.prevout)) {
// Are inputs missing because we already have the tx?
for (size_t out = 0; out < tx.vout.size(); out++) {
// Optimistically just do efficient check of cache for outputs
if (pcoinsTip->HaveCoinInCache(COutPoint(hash, out))) {
if (coins_cache.HaveCoinInCache(COutPoint(hash, out))) {
return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-known");
}
}
@ -918,7 +924,7 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo
// (`CCoinsViewCache::cacheCoins`).
for (const COutPoint& hashTx : coins_to_uncache)
pcoinsTip->Uncache(hashTx);
::ChainstateActive().CoinsTip().Uncache(hashTx);
}
// After we've (potentially) uncached entries, ensure our coins cache is still within its size limits
CValidationState stateDummy;
@ -1101,6 +1107,40 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
return nSubsidy;
}
CoinsViews::CoinsViews(
std::string ldb_name,
size_t cache_size_bytes,
bool in_memory,
bool should_wipe) : m_dbview(
GetDataDir() / ldb_name, cache_size_bytes, in_memory, should_wipe),
m_catcherview(&m_dbview) {}
void CoinsViews::InitCache()
{
m_cacheview = MakeUnique<CCoinsViewCache>(&m_catcherview);
}
// NOTE: for now m_blockman is set to a global, but this will be changed
// in a future commit.
CChainState::CChainState() : m_blockman(g_blockman) {}
void CChainState::InitCoinsDB(
size_t cache_size_bytes,
bool in_memory,
bool should_wipe,
std::string leveldb_name)
{
m_coins_views = MakeUnique<CoinsViews>(
leveldb_name, cache_size_bytes, in_memory, should_wipe);
}
void CChainState::InitCoinsCache()
{
assert(m_coins_views != nullptr);
m_coins_views->InitCache();
}
// Note that though this is marked const, we may end up modifying `m_cached_finished_ibd`, which
// is a performance-related implementation detail. This function must be marked
// `const` so that `CValidationInterface` clients (which are given a `const CChainState*`)
@ -2125,6 +2165,7 @@ bool CChainState::FlushStateToDisk(
{
int64_t nMempoolUsage = mempool.DynamicMemoryUsage();
LOCK(cs_main);
assert(this->CanFlushToDisk());
static int64_t nLastWrite = 0;
static int64_t nLastFlush = 0;
std::set<int> setFilesToPrune;
@ -2158,7 +2199,7 @@ bool CChainState::FlushStateToDisk(
nLastFlush = nNow;
}
int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
int64_t cacheSize = pcoinsTip->DynamicMemoryUsage();
int64_t cacheSize = CoinsTip().DynamicMemoryUsage();
int64_t nTotalSpace = nCoinCacheUsage + std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0);
// 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).
bool fCacheLarge = mode == FlushStateMode::PERIODIC && cacheSize > std::max((9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024);
@ -2202,17 +2243,17 @@ bool CChainState::FlushStateToDisk(
nLastWrite = nNow;
}
// Flush best chain related state. This can only be done if the blocks / block index write was also done.
if (fDoFullFlush && !pcoinsTip->GetBestBlock().IsNull()) {
if (fDoFullFlush && !CoinsTip().GetBestBlock().IsNull()) {
// Typical Coin structures on disk are around 48 bytes in size.
// Pushing a new one to the database can cause it to be written
// twice (once in the log, and once in the tables). This is already
// 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 * pcoinsTip->GetCacheSize())) {
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);
}
// Flush the chainstate (which may refer to block index entries).
if (!pcoinsTip->Flush())
if (!CoinsTip().Flush())
return AbortNode(state, "Failed to write to coin database");
nLastFlush = nNow;
full_flush_completed = true;
@ -2264,7 +2305,9 @@ static void AppendWarning(std::string& res, const std::string& warn)
}
/** Check warning conditions and do some notifications on new chain tip set. */
void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainParams) {
void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainParams)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
// New best block
mempool.AddTransactionsUpdated(1);
@ -2307,7 +2350,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
(unsigned long)pindexNew->nChainTx,
FormatISO8601DateTime(pindexNew->GetBlockTime()),
GuessVerificationProgress(pindexNew, chainParams.GetConsensus().nPowTargetSpacing),
pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize());
::ChainstateActive().CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), ::ChainstateActive().CoinsTip().GetCacheSize());
if (!warningMessages.empty())
LogPrintf(" warning='%s'", warningMessages); /* Continued */
LogPrintf("\n");
@ -2336,7 +2379,7 @@ bool CChainState::DisconnectTip(CValidationState& state, const CChainParams& cha
// Apply the block atomically to the chain state.
int64_t nStart = GetTimeMicros();
{
CCoinsViewCache view(pcoinsTip.get());
CCoinsViewCache view(&CoinsTip());
assert(view.GetBestBlock() == pindexDelete->GetBlockHash());
if (DisconnectBlock(block, pindexDelete, view) != DISCONNECT_OK)
return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
@ -2469,7 +2512,7 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
std::set<std::pair<uint256, COutPoint>> setPeginsSpent;
{
CCoinsViewCache view(pcoinsTip.get());
CCoinsViewCache view(&CoinsTip());
bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams, &setPeginsSpent);
GetMainSignals().BlockChecked(blockConnecting, state);
if (!rv) {
@ -2695,7 +2738,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
// any disconnected transactions back to the mempool.
UpdateMempoolForReorg(disconnectpool, true);
}
mempool.check(pcoinsTip.get());
mempool.check(&CoinsTip());
// Callbacks/notifications for a new best chain.
if (fInvalidFound)
@ -3797,7 +3840,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
{
AssertLockHeld(cs_main);
assert(pindexPrev && pindexPrev == ::ChainActive().Tip());
CCoinsViewCache viewNew(pcoinsTip.get());
CCoinsViewCache viewNew(&::ChainstateActive().CoinsTip());
uint256 block_hash(block.GetHash());
CBlockIndex indexDummy(block);
indexDummy.pprev = pindexPrev;
@ -4150,12 +4193,14 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_RE
bool LoadChainTip(const CChainParams& chainparams)
{
AssertLockHeld(cs_main);
assert(!pcoinsTip->GetBestBlock().IsNull()); // Never called when the coins view is empty
const CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip();
assert(!coins_cache.GetBestBlock().IsNull()); // Never called when the coins view is empty
if (::ChainActive().Tip() && ::ChainActive().Tip()->GetBlockHash() == pcoinsTip->GetBestBlock()) return true;
if (::ChainActive().Tip() &&
::ChainActive().Tip()->GetBlockHash() == coins_cache.GetBestBlock()) return true;
// Load pointer to end of best chain
CBlockIndex* pindex = LookupBlockIndex(pcoinsTip->GetBestBlock());
CBlockIndex* pindex = LookupBlockIndex(coins_cache.GetBestBlock());
if (!pindex) {
return false;
}
@ -4232,7 +4277,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() + pcoinsTip->DynamicMemoryUsage()) <= nCoinCacheUsage) {
if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + ::ChainstateActive().CoinsTip().DynamicMemoryUsage()) <= nCoinCacheUsage) {
assert(coins.GetBestBlock() == pindex->GetBlockHash());
DisconnectResult res = ::ChainstateActive().DisconnectBlock(block, pindex, coins);
if (res == DISCONNECT_FAILED) {