refactor: pcoinsTip -> CChainState::CoinsTip()

This aliasing makes subsequent commits easier to review; eventually CoinsTip()
will return the CCoinsViewCache managed by CChainState.
This commit is contained in:
James O'Beirne 2019-07-24 11:45:04 -04:00
parent e5fdda68c6
commit fae6ab6aed
13 changed files with 70 additions and 58 deletions

View file

@ -232,7 +232,7 @@ public:
bool getUnspentOutput(const COutPoint& output, Coin& coin) override bool getUnspentOutput(const COutPoint& output, Coin& coin) override
{ {
LOCK(::cs_main); LOCK(::cs_main);
return ::pcoinsTip->GetCoin(output, coin); return ::ChainstateActive().CoinsTip().GetCoin(output, coin);
} }
std::string getWalletDir() override std::string getWalletDir() override
{ {

View file

@ -1291,11 +1291,12 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
LOCK(g_cs_orphans); LOCK(g_cs_orphans);
if (mapOrphanTransactions.count(inv.hash)) return true; if (mapOrphanTransactions.count(inv.hash)) return true;
} }
const CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip();
return recentRejects->contains(inv.hash) || return recentRejects->contains(inv.hash) ||
mempool.exists(inv.hash) || mempool.exists(inv.hash) ||
pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 0)) || // Best effort: only try output 0 and 1 coins_cache.HaveCoinInCache(COutPoint(inv.hash, 0)) || // Best effort: only try output 0 and 1
pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 1)); coins_cache.HaveCoinInCache(COutPoint(inv.hash, 1));
} }
case MSG_BLOCK: case MSG_BLOCK:
case MSG_WITNESS_BLOCK: case MSG_WITNESS_BLOCK:
@ -1844,7 +1845,7 @@ void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_se
EraseOrphanTx(orphanHash); EraseOrphanTx(orphanHash);
done = true; done = true;
} }
mempool.check(pcoinsTip.get()); mempool.check(&::ChainstateActive().CoinsTip());
} }
} }
@ -2497,7 +2498,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (!AlreadyHave(inv) && if (!AlreadyHave(inv) &&
AcceptToMemoryPool(mempool, state, ptx, &fMissingInputs, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) { AcceptToMemoryPool(mempool, state, ptx, &fMissingInputs, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
mempool.check(pcoinsTip.get()); mempool.check(&::ChainstateActive().CoinsTip());
RelayTransaction(tx.GetHash(), *connman); RelayTransaction(tx.GetHash(), *connman);
for (unsigned int i = 0; i < tx.vout.size(); i++) { for (unsigned int i = 0; i < tx.vout.size(); i++) {
auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(inv.hash, i)); auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(inv.hash, i));

View file

@ -10,8 +10,7 @@
void FindCoins(std::map<COutPoint, Coin>& coins) void FindCoins(std::map<COutPoint, Coin>& coins)
{ {
LOCK2(cs_main, ::mempool.cs); LOCK2(cs_main, ::mempool.cs);
assert(pcoinsTip); CCoinsViewCache& chain_view = ::ChainstateActive().CoinsTip();
CCoinsViewCache& chain_view = *::pcoinsTip;
CCoinsViewMemPool mempool_view(&chain_view, ::mempool); CCoinsViewMemPool mempool_view(&chain_view, ::mempool);
for (auto& coin : coins) { for (auto& coin : coins) {
if (!mempool_view.GetCoin(coin.first, coin.second)) { if (!mempool_view.GetCoin(coin.first, coin.second)) {

View file

@ -25,7 +25,7 @@ TransactionError BroadcastTransaction(const CTransactionRef tx, std::string& err
LOCK(cs_main); LOCK(cs_main);
// If the transaction is already confirmed in the chain, don't do anything // If the transaction is already confirmed in the chain, don't do anything
// and return early. // and return early.
CCoinsViewCache &view = *pcoinsTip; CCoinsViewCache &view = ::ChainstateActive().CoinsTip();
for (size_t o = 0; o < tx->vout.size(); o++) { for (size_t o = 0; o < tx->vout.size(); o++) {
const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o)); const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o));
// IsSpent doesnt mean the coin is spent, it means the output doesnt' exist. // IsSpent doesnt mean the coin is spent, it means the output doesnt' exist.

View file

@ -503,12 +503,12 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
if (fCheckMemPool) { if (fCheckMemPool) {
// use db+mempool as cache backend in case user likes to query mempool // use db+mempool as cache backend in case user likes to query mempool
LOCK2(cs_main, mempool.cs); LOCK2(cs_main, mempool.cs);
CCoinsViewCache& viewChain = *pcoinsTip; CCoinsViewCache& viewChain = ::ChainstateActive().CoinsTip();
CCoinsViewMemPool viewMempool(&viewChain, mempool); CCoinsViewMemPool viewMempool(&viewChain, mempool);
process_utxos(viewMempool, mempool); process_utxos(viewMempool, mempool);
} else { } else {
LOCK(cs_main); // no need to lock mempool! LOCK(cs_main); // no need to lock mempool!
process_utxos(*pcoinsTip, CTxMemPool()); process_utxos(::ChainstateActive().CoinsTip(), CTxMemPool());
} }
for (size_t i = 0; i < hits.size(); ++i) { for (size_t i = 0; i < hits.size(); ++i) {

View file

@ -1126,19 +1126,21 @@ UniValue gettxout(const JSONRPCRequest& request)
fMempool = request.params[2].get_bool(); fMempool = request.params[2].get_bool();
Coin coin; Coin coin;
CCoinsViewCache* coins_view = &::ChainstateActive().CoinsTip();
if (fMempool) { if (fMempool) {
LOCK(mempool.cs); LOCK(mempool.cs);
CCoinsViewMemPool view(pcoinsTip.get(), mempool); CCoinsViewMemPool view(coins_view, mempool);
if (!view.GetCoin(out, coin) || mempool.isSpent(out)) { if (!view.GetCoin(out, coin) || mempool.isSpent(out)) {
return NullUniValue; return NullUniValue;
} }
} else { } else {
if (!pcoinsTip->GetCoin(out, coin)) { if (!coins_view->GetCoin(out, coin)) {
return NullUniValue; return NullUniValue;
} }
} }
const CBlockIndex* pindex = LookupBlockIndex(pcoinsTip->GetBestBlock()); const CBlockIndex* pindex = LookupBlockIndex(coins_view->GetBestBlock());
ret.pushKV("bestblock", pindex->GetBlockHash().GetHex()); ret.pushKV("bestblock", pindex->GetBlockHash().GetHex());
if (coin.nHeight == MEMPOOL_HEIGHT) { if (coin.nHeight == MEMPOOL_HEIGHT) {
ret.pushKV("confirmations", 0); ret.pushKV("confirmations", 0);
@ -1180,7 +1182,8 @@ static UniValue verifychain(const JSONRPCRequest& request)
if (!request.params[1].isNull()) if (!request.params[1].isNull())
nCheckDepth = request.params[1].get_int(); nCheckDepth = request.params[1].get_int();
return CVerifyDB().VerifyDB(Params(), pcoinsTip.get(), nCheckLevel, nCheckDepth); return CVerifyDB().VerifyDB(
Params(), &::ChainstateActive().CoinsTip(), nCheckLevel, nCheckDepth);
} }
/** Implementation of IsSuperMajority with better feedback */ /** Implementation of IsSuperMajority with better feedback */

View file

@ -259,7 +259,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
// Loop through txids and try to find which block they're in. Exit loop once a block is found. // Loop through txids and try to find which block they're in. Exit loop once a block is found.
for (const auto& tx : setTxids) { for (const auto& tx : setTxids) {
const Coin& coin = AccessByTxid(*pcoinsTip, tx); const Coin& coin = AccessByTxid(::ChainstateActive().CoinsTip(), tx);
if (!coin.IsSpent()) { if (!coin.IsSpent()) {
pblockindex = ::ChainActive()[coin.nHeight]; pblockindex = ::ChainActive()[coin.nHeight];
break; break;
@ -636,7 +636,7 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
{ {
LOCK(cs_main); LOCK(cs_main);
LOCK(mempool.cs); LOCK(mempool.cs);
CCoinsViewCache &viewChain = *pcoinsTip; CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip();
CCoinsViewMemPool viewMempool(&viewChain, mempool); CCoinsViewMemPool viewMempool(&viewChain, mempool);
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
@ -1505,7 +1505,7 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request)
CCoinsViewCache view(&viewDummy); CCoinsViewCache view(&viewDummy);
{ {
LOCK2(cs_main, mempool.cs); LOCK2(cs_main, mempool.cs);
CCoinsViewCache &viewChain = *pcoinsTip; CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip();
CCoinsViewMemPool viewMempool(&viewChain, mempool); CCoinsViewMemPool viewMempool(&viewChain, mempool);
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view

View file

@ -372,7 +372,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
CBlockIndex* prev = ::ChainActive().Tip(); CBlockIndex* prev = ::ChainActive().Tip();
CBlockIndex* next = new CBlockIndex(); CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256()); next->phashBlock = new uint256(InsecureRand256());
pcoinsTip->SetBestBlock(next->GetBlockHash()); ::ChainstateActive().CoinsTip().SetBestBlock(next->GetBlockHash());
next->pprev = prev; next->pprev = prev;
next->nHeight = prev->nHeight + 1; next->nHeight = prev->nHeight + 1;
next->BuildSkip(); next->BuildSkip();
@ -384,7 +384,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
CBlockIndex* prev = ::ChainActive().Tip(); CBlockIndex* prev = ::ChainActive().Tip();
CBlockIndex* next = new CBlockIndex(); CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256()); next->phashBlock = new uint256(InsecureRand256());
pcoinsTip->SetBestBlock(next->GetBlockHash()); ::ChainstateActive().CoinsTip().SetBestBlock(next->GetBlockHash());
next->pprev = prev; next->pprev = prev;
next->nHeight = prev->nHeight + 1; next->nHeight = prev->nHeight + 1;
next->BuildSkip(); next->BuildSkip();
@ -414,7 +414,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
while (::ChainActive().Tip()->nHeight > nHeight) { while (::ChainActive().Tip()->nHeight > nHeight) {
CBlockIndex* del = ::ChainActive().Tip(); CBlockIndex* del = ::ChainActive().Tip();
::ChainActive().SetTip(del->pprev); ::ChainActive().SetTip(del->pprev);
pcoinsTip->SetBestBlock(del->pprev->GetBlockHash()); ::ChainstateActive().CoinsTip().SetBestBlock(del->pprev->GetBlockHash());
delete del->phashBlock; delete del->phashBlock;
delete del; delete del;
} }

View file

@ -97,7 +97,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
BOOST_CHECK_EQUAL(mempool.size(), 0U); BOOST_CHECK_EQUAL(mempool.size(), 0U);
} }
// Run CheckInputs (using pcoinsTip) on the given transaction, for all script // Run CheckInputs (using CoinsTip()) on the given transaction, for all script
// flags. Test that CheckInputs passes for all flags that don't overlap with // flags. Test that CheckInputs passes for all flags that don't overlap with
// the failing_flags argument, but otherwise fails. // the failing_flags argument, but otherwise fails.
// CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY (and future NOP codes that may // CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY (and future NOP codes that may
@ -125,7 +125,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
// WITNESS requires P2SH // WITNESS requires P2SH
test_flags |= SCRIPT_VERIFY_P2SH; test_flags |= SCRIPT_VERIFY_P2SH;
} }
bool ret = CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, nullptr); bool ret = CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), true, test_flags, true, add_to_cache, txdata, nullptr);
// CheckInputs should succeed iff test_flags doesn't intersect with // CheckInputs should succeed iff test_flags doesn't intersect with
// failing_flags // failing_flags
bool expected_return_value = !(test_flags & failing_flags); bool expected_return_value = !(test_flags & failing_flags);
@ -135,13 +135,13 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail
if (ret && add_to_cache) { if (ret && add_to_cache) {
// Check that we get a cache hit if the tx was valid // Check that we get a cache hit if the tx was valid
std::vector<CScriptCheck> scriptchecks; std::vector<CScriptCheck> scriptchecks;
BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, &scriptchecks)); BOOST_CHECK(CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), true, test_flags, true, add_to_cache, txdata, &scriptchecks));
BOOST_CHECK(scriptchecks.empty()); BOOST_CHECK(scriptchecks.empty());
} else { } else {
// Check that we get script executions to check, if the transaction // Check that we get script executions to check, if the transaction
// was invalid, or we didn't add to cache. // was invalid, or we didn't add to cache.
std::vector<CScriptCheck> scriptchecks; std::vector<CScriptCheck> scriptchecks;
BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, &scriptchecks)); BOOST_CHECK(CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), true, test_flags, true, add_to_cache, txdata, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size()); BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size());
} }
} }
@ -204,13 +204,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
CValidationState state; CValidationState state;
PrecomputedTransactionData ptd_spend_tx(spend_tx); PrecomputedTransactionData ptd_spend_tx(spend_tx);
BOOST_CHECK(!CheckInputs(CTransaction(spend_tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr)); BOOST_CHECK(!CheckInputs(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
// If we call again asking for scriptchecks (as happens in // If we call again asking for scriptchecks (as happens in
// ConnectBlock), we should add a script check object for this -- we're // ConnectBlock), we should add a script check object for this -- we're
// not caching invalidity (if that changes, delete this test case). // not caching invalidity (if that changes, delete this test case).
std::vector<CScriptCheck> scriptchecks; std::vector<CScriptCheck> scriptchecks;
BOOST_CHECK(CheckInputs(CTransaction(spend_tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks)); BOOST_CHECK(CheckInputs(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), 1U); BOOST_CHECK_EQUAL(scriptchecks.size(), 1U);
// Test that CheckInputs returns true iff DERSIG-enforcing flags are // Test that CheckInputs returns true iff DERSIG-enforcing flags are
@ -227,7 +227,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey); block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey);
LOCK(cs_main); LOCK(cs_main);
BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() == block.GetHash()); BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() == block.GetHash());
BOOST_CHECK(pcoinsTip->GetBestBlock() == block.GetHash()); BOOST_CHECK(::ChainstateActive().CoinsTip().GetBestBlock() == block.GetHash());
// Test P2SH: construct a transaction that is valid without P2SH, and // Test P2SH: construct a transaction that is valid without P2SH, and
// then test validity with P2SH. // then test validity with P2SH.
@ -272,7 +272,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100; invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
CValidationState state; CValidationState state;
PrecomputedTransactionData txdata(invalid_with_cltv_tx); PrecomputedTransactionData txdata(invalid_with_cltv_tx);
BOOST_CHECK(CheckInputs(CTransaction(invalid_with_cltv_tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr)); BOOST_CHECK(CheckInputs(CTransaction(invalid_with_cltv_tx), state, ::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
} }
// TEST CHECKSEQUENCEVERIFY // TEST CHECKSEQUENCEVERIFY
@ -300,7 +300,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100; invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
CValidationState state; CValidationState state;
PrecomputedTransactionData txdata(invalid_with_csv_tx); PrecomputedTransactionData txdata(invalid_with_csv_tx);
BOOST_CHECK(CheckInputs(CTransaction(invalid_with_csv_tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr)); BOOST_CHECK(CheckInputs(CTransaction(invalid_with_csv_tx), state, &::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
} }
// TODO: add tests for remaining script flags // TODO: add tests for remaining script flags
@ -362,12 +362,12 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
CValidationState state; CValidationState state;
PrecomputedTransactionData txdata(tx); PrecomputedTransactionData txdata(tx);
// This transaction is now invalid under segwit, because of the second input. // This transaction is now invalid under segwit, because of the second input.
BOOST_CHECK(!CheckInputs(CTransaction(tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr)); BOOST_CHECK(!CheckInputs(CTransaction(tx), state, &::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
std::vector<CScriptCheck> scriptchecks; std::vector<CScriptCheck> scriptchecks;
// Make sure this transaction was not cached (ie because the first // Make sure this transaction was not cached (ie because the first
// input was valid) // input was valid)
BOOST_CHECK(CheckInputs(CTransaction(tx), state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks)); BOOST_CHECK(CheckInputs(CTransaction(tx), state, &::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
// Should get 2 script checks back -- caching is on a whole-transaction basis. // Should get 2 script checks back -- caching is on a whole-transaction basis.
BOOST_CHECK_EQUAL(scriptchecks.size(), 2U); BOOST_CHECK_EQUAL(scriptchecks.size(), 2U);
} }

View file

@ -497,7 +497,7 @@ public:
* *
* 1. Locking both `cs_main` and `mempool.cs` will give a view of mempool * 1. Locking both `cs_main` and `mempool.cs` will give a view of mempool
* that is consistent with current chain tip (`::ChainActive()` and * that is consistent with current chain tip (`::ChainActive()` and
* `pcoinsTip`) and is fully populated. Fully populated means that if the * `CoinsTip()`) and is fully populated. Fully populated means that if the
* current active chain is missing transactions that were present in a * current active chain is missing transactions that were present in a
* previously active chain, all the missing transactions will have been * previously active chain, all the missing transactions will have been
* re-added to the mempool and should be present if they meet size and * re-added to the mempool and should be present if they meet size and

View file

@ -260,8 +260,8 @@ bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flag
lockPair.second = lp->time; lockPair.second = lp->time;
} }
else { else {
// pcoinsTip contains the UTXO set for ::ChainActive().Tip() // CoinsTip() contains the UTXO set for ::ChainActive().Tip()
CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool); CCoinsViewMemPool viewMemPool(&::ChainstateActive().CoinsTip(), pool);
std::vector<int> prevheights; std::vector<int> prevheights;
prevheights.resize(tx.vin.size()); prevheights.resize(tx.vin.size());
for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) { for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
@ -320,7 +320,7 @@ static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age)
std::vector<COutPoint> vNoSpendsRemaining; std::vector<COutPoint> vNoSpendsRemaining;
pool.TrimToSize(limit, &vNoSpendsRemaining); pool.TrimToSize(limit, &vNoSpendsRemaining);
for (const COutPoint& removed : vNoSpendsRemaining) for (const COutPoint& removed : vNoSpendsRemaining)
pcoinsTip->Uncache(removed); ::ChainstateActive().CoinsTip().Uncache(removed);
} }
static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main) static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
@ -382,7 +382,7 @@ static void UpdateMempoolForReorg(DisconnectedBlockTransactions& disconnectpool,
mempool.UpdateTransactionsFromBlock(vHashUpdate); mempool.UpdateTransactionsFromBlock(vHashUpdate);
// We also need to remove any now-immature transactions // 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 // 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); LimitMempoolSize(mempool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
} }
@ -414,7 +414,7 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt
assert(txFrom->vout.size() > txin.prevout.n); assert(txFrom->vout.size() > txin.prevout.n);
assert(txFrom->vout[txin.prevout.n] == coin.out); assert(txFrom->vout[txin.prevout.n] == coin.out);
} else { } else {
const Coin& coinFromDisk = pcoinsTip->AccessCoin(txin.prevout); const Coin& coinFromDisk = ::ChainstateActive().CoinsTip().AccessCoin(txin.prevout);
assert(!coinFromDisk.IsSpent()); assert(!coinFromDisk.IsSpent());
assert(coinFromDisk.out == coin.out); assert(coinFromDisk.out == coin.out);
} }
@ -514,12 +514,13 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
CCoinsViewCache view(&dummy); CCoinsViewCache view(&dummy);
LockPoints lp; LockPoints lp;
CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool); CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip();
CCoinsViewMemPool viewMemPool(&coins_cache, pool);
view.SetBackend(viewMemPool); view.SetBackend(viewMemPool);
// do all inputs exist? // do all inputs exist?
for (const CTxIn& txin : tx.vin) { for (const CTxIn& txin : tx.vin) {
if (!pcoinsTip->HaveCoinInCache(txin.prevout)) { if (!coins_cache.HaveCoinInCache(txin.prevout)) {
coins_to_uncache.push_back(txin.prevout); coins_to_uncache.push_back(txin.prevout);
} }
@ -530,7 +531,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// Are inputs missing because we already have the tx? // Are inputs missing because we already have the tx?
for (size_t out = 0; out < tx.vout.size(); out++) { for (size_t out = 0; out < tx.vout.size(); out++) {
// Optimistically just do efficient check of cache for outputs // 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"); return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-known");
} }
} }
@ -860,7 +861,7 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo
// (`CCoinsViewCache::cacheCoins`). // (`CCoinsViewCache::cacheCoins`).
for (const COutPoint& hashTx : coins_to_uncache) 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 // After we've (potentially) uncached entries, ensure our coins cache is still within its size limits
CValidationState stateDummy; CValidationState stateDummy;
@ -2014,7 +2015,7 @@ bool CChainState::FlushStateToDisk(
nLastFlush = nNow; nLastFlush = nNow;
} }
int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; 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); 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). // 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); bool fCacheLarge = mode == FlushStateMode::PERIODIC && cacheSize > std::max((9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024);
@ -2058,17 +2059,17 @@ bool CChainState::FlushStateToDisk(
nLastWrite = nNow; nLastWrite = nNow;
} }
// Flush best chain related state. This can only be done if the blocks / block index write was also done. // 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. // Typical Coin structures on disk are around 48 bytes in size.
// Pushing a new one to the database can cause it to be written // 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 // twice (once in the log, and once in the tables). This is already
// an overestimation, as most will delete an existing entry or // an overestimation, as most will delete an existing entry or
// overwrite one. Still, use a conservative safety factor of 2. // 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); 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). // 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"); return AbortNode(state, "Failed to write to coin database");
nLastFlush = nNow; nLastFlush = nNow;
full_flush_completed = true; full_flush_completed = true;
@ -2162,7 +2163,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion, pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx, log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx,
FormatISO8601DateTime(pindexNew->GetBlockTime()), FormatISO8601DateTime(pindexNew->GetBlockTime()),
GuessVerificationProgress(chainParams.TxData(), pindexNew), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); GuessVerificationProgress(chainParams.TxData(), pindexNew), ::ChainstateActive().CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), ::ChainstateActive().CoinsTip().GetCacheSize());
if (!warningMessages.empty()) if (!warningMessages.empty())
LogPrintf(" warning='%s'", warningMessages); /* Continued */ LogPrintf(" warning='%s'", warningMessages); /* Continued */
LogPrintf("\n"); LogPrintf("\n");
@ -2191,7 +2192,7 @@ bool CChainState::DisconnectTip(CValidationState& state, const CChainParams& cha
// Apply the block atomically to the chain state. // Apply the block atomically to the chain state.
int64_t nStart = GetTimeMicros(); int64_t nStart = GetTimeMicros();
{ {
CCoinsViewCache view(pcoinsTip.get()); CCoinsViewCache view(&CoinsTip());
assert(view.GetBestBlock() == pindexDelete->GetBlockHash()); assert(view.GetBestBlock() == pindexDelete->GetBlockHash());
if (DisconnectBlock(block, pindexDelete, view) != DISCONNECT_OK) if (DisconnectBlock(block, pindexDelete, view) != DISCONNECT_OK)
return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString()); return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
@ -2319,7 +2320,7 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
int64_t nTime3; int64_t nTime3;
LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * MILLI, nTimeReadFromDisk * MICRO); LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * MILLI, nTimeReadFromDisk * MICRO);
{ {
CCoinsViewCache view(pcoinsTip.get()); CCoinsViewCache view(&CoinsTip());
bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams); bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams);
GetMainSignals().BlockChecked(blockConnecting, state); GetMainSignals().BlockChecked(blockConnecting, state);
if (!rv) { if (!rv) {
@ -2506,7 +2507,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
// any disconnected transactions back to the mempool. // any disconnected transactions back to the mempool.
UpdateMempoolForReorg(disconnectpool, true); UpdateMempoolForReorg(disconnectpool, true);
} }
mempool.check(pcoinsTip.get()); mempool.check(&CoinsTip());
// Callbacks/notifications for a new best chain. // Callbacks/notifications for a new best chain.
if (fInvalidFound) if (fInvalidFound)
@ -3508,7 +3509,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
{ {
AssertLockHeld(cs_main); AssertLockHeld(cs_main);
assert(pindexPrev && pindexPrev == ::ChainActive().Tip()); assert(pindexPrev && pindexPrev == ::ChainActive().Tip());
CCoinsViewCache viewNew(pcoinsTip.get()); CCoinsViewCache viewNew(&::ChainstateActive().CoinsTip());
uint256 block_hash(block.GetHash()); uint256 block_hash(block.GetHash());
CBlockIndex indexDummy(block); CBlockIndex indexDummy(block);
indexDummy.pprev = pindexPrev; indexDummy.pprev = pindexPrev;
@ -3861,12 +3862,14 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_RE
bool LoadChainTip(const CChainParams& chainparams) bool LoadChainTip(const CChainParams& chainparams)
{ {
AssertLockHeld(cs_main); 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 // Load pointer to end of best chain
CBlockIndex* pindex = LookupBlockIndex(pcoinsTip->GetBestBlock()); CBlockIndex* pindex = LookupBlockIndex(coins_cache.GetBestBlock());
if (!pindex) { if (!pindex) {
return false; return false;
} }
@ -3943,7 +3946,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
} }
} }
// check level 3: check for inconsistencies during memory-only disconnect of tip blocks // 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()); assert(coins.GetBestBlock() == pindex->GetBlockHash());
DisconnectResult res = ::ChainstateActive().DisconnectBlock(block, pindex, coins); DisconnectResult res = ::ChainstateActive().DisconnectBlock(block, pindex, coins);
if (res == DISCONNECT_FAILED) { if (res == DISCONNECT_FAILED) {

View file

@ -505,6 +505,9 @@ public:
CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
}; };
/** Global variable that points to the active CCoinsView (protected by cs_main) */
extern std::unique_ptr<CCoinsViewCache> pcoinsTip;
/** /**
* CChainState stores and provides an API to update our local knowledge of the * CChainState stores and provides an API to update our local knowledge of the
* current best chain. * current best chain.
@ -566,6 +569,12 @@ public:
*/ */
std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates; std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexCandidates;
//! @returns A reference to the in-memory cache of the UTXO set.
CCoinsViewCache& CoinsTip()
{
return *::pcoinsTip;
}
/** /**
* Update the on-disk chain state. * Update the on-disk chain state.
* The caches and indexes are flushed depending on the mode we're called with * The caches and indexes are flushed depending on the mode we're called with
@ -662,9 +671,6 @@ BlockMap& BlockIndex();
/** Global variable that points to the coins database (protected by cs_main) */ /** Global variable that points to the coins database (protected by cs_main) */
extern std::unique_ptr<CCoinsViewDB> pcoinsdbview; extern std::unique_ptr<CCoinsViewDB> pcoinsdbview;
/** Global variable that points to the active CCoinsView (protected by cs_main) */
extern std::unique_ptr<CCoinsViewCache> pcoinsTip;
/** Global variable that points to the active block tree (protected by cs_main) */ /** Global variable that points to the active block tree (protected by cs_main) */
extern std::unique_ptr<CBlockTreeDB> pblocktree; extern std::unique_ptr<CBlockTreeDB> pblocktree;

View file

@ -58,7 +58,7 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
self.base_args = ["-limitdescendantsize=0", "-maxmempool=0", "-rpcservertimeout=900", "-dbbatchsize=200000"] self.base_args = ["-limitdescendantsize=0", "-maxmempool=0", "-rpcservertimeout=900", "-dbbatchsize=200000"]
# Set different crash ratios and cache sizes. Note that not all of # Set different crash ratios and cache sizes. Note that not all of
# -dbcache goes to pcoinsTip. # -dbcache goes to the in-memory coins cache.
self.node0_args = ["-dbcrashratio=8", "-dbcache=4"] + self.base_args self.node0_args = ["-dbcrashratio=8", "-dbcache=4"] + self.base_args
self.node1_args = ["-dbcrashratio=16", "-dbcache=8"] + self.base_args self.node1_args = ["-dbcrashratio=16", "-dbcache=8"] + self.base_args
self.node2_args = ["-dbcrashratio=24", "-dbcache=16"] + self.base_args self.node2_args = ["-dbcrashratio=24", "-dbcache=16"] + self.base_args