Skip to content

Commit 85883a9

Browse files
author
MarcoFalke
committed
Merge #16443: refactor: have CCoins* data managed under CChainState
582d2cd Cover UTXO set access with lock annotations (James O'Beirne) 5693530 refactor: have CCoins* data managed under CChainState (James O'Beirne) fae6ab6 refactor: pcoinsTip -> CChainState::CoinsTip() (James O'Beirne) Pull request description: This is part of the [assumeutxo project](https://github.com/bitcoin/bitcoin/projects/11): Parent PR: #15606 Issue: #15605 Specification: https://github.com/jamesob/assumeutxo-docs/tree/2019-04-proposal/proposal --- This change encapsulates UTXO set data within CChainState instances, removing global data `pcoinsTip` and `pcoinsviewdb`. This is necessary if we want to maintain multiple chainstates with their own rendering of the UTXO set. We introduce a class CoinsViews which consolidates the construction of a CCoins* hierarchy. This commit could be broken into smaller pieces, but it would require more ephemeral diffs to, e.g., temporarily change CCoinsViewDB's constructor invocations. ACKs for top commit: Sjors: reACK 582d2cd MarcoFalke: ACK 582d2cd Tree-SHA512: ec9d904fe5dca8cd2dc4b7916daa5d8bab30856dd4645987300f905e0a19f9919fce4f9d1ff03eda982943ca73e6e9a746be6cf53b46510de36e8c81a1eafba1
2 parents 8bd5e0a + 582d2cd commit 85883a9

17 files changed

+247
-100
lines changed

src/init.cpp

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
#include <blockfilter.h>
1616
#include <chain.h>
1717
#include <chainparams.h>
18-
#include <coins.h>
1918
#include <compat/sanity.h>
2019
#include <consensus/validation.h>
2120
#include <fs.h>
@@ -150,7 +149,6 @@ NODISCARD static bool CreatePidFile()
150149
// shutdown thing.
151150
//
152151

153-
static std::unique_ptr<CCoinsViewErrorCatcher> pcoinscatcher;
154152
static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle;
155153

156154
static boost::thread_group threadGroup;
@@ -235,8 +233,14 @@ void Shutdown(InitInterfaces& interfaces)
235233
}
236234

237235
// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
238-
if (pcoinsTip != nullptr) {
239-
::ChainstateActive().ForceFlushStateToDisk();
236+
//
237+
// g_chainstate is referenced here directly (instead of ::ChainstateActive()) because it
238+
// may not have been initialized yet.
239+
{
240+
LOCK(cs_main);
241+
if (g_chainstate && g_chainstate->CanFlushToDisk()) {
242+
g_chainstate->ForceFlushStateToDisk();
243+
}
240244
}
241245

242246
// After there are no more peers/RPC left to give us new data which may generate
@@ -251,12 +255,10 @@ void Shutdown(InitInterfaces& interfaces)
251255

252256
{
253257
LOCK(cs_main);
254-
if (pcoinsTip != nullptr) {
255-
::ChainstateActive().ForceFlushStateToDisk();
258+
if (g_chainstate && g_chainstate->CanFlushToDisk()) {
259+
g_chainstate->ForceFlushStateToDisk();
260+
g_chainstate->ResetCoinsViews();
256261
}
257-
pcoinsTip.reset();
258-
pcoinscatcher.reset();
259-
pcoinsdbview.reset();
260262
pblocktree.reset();
261263
}
262264
for (const auto& client : interfaces.chain_clients) {
@@ -1467,10 +1469,10 @@ bool AppInitMain(InitInterfaces& interfaces)
14671469
bool is_coinsview_empty;
14681470
try {
14691471
LOCK(cs_main);
1472+
// This statement makes ::ChainstateActive() usable.
1473+
g_chainstate = MakeUnique<CChainState>();
14701474
UnloadBlockIndex();
1471-
pcoinsTip.reset();
1472-
pcoinsdbview.reset();
1473-
pcoinscatcher.reset();
1475+
14741476
// new CBlockTreeDB tries to delete the existing file, which
14751477
// fails if it's still open from the previous loop. Close it first:
14761478
pblocktree.reset();
@@ -1521,33 +1523,38 @@ bool AppInitMain(InitInterfaces& interfaces)
15211523
// At this point we're either in reindex or we've loaded a useful
15221524
// block tree into BlockIndex()!
15231525

1524-
pcoinsdbview.reset(new CCoinsViewDB(nCoinDBCache, false, fReset || fReindexChainState));
1525-
pcoinscatcher.reset(new CCoinsViewErrorCatcher(pcoinsdbview.get()));
1526-
pcoinscatcher->AddReadErrCallback([]() {
1526+
::ChainstateActive().InitCoinsDB(
1527+
/* cache_size_bytes */ nCoinDBCache,
1528+
/* in_memory */ false,
1529+
/* should_wipe */ fReset || fReindexChainState);
1530+
1531+
::ChainstateActive().CoinsErrorCatcher().AddReadErrCallback([]() {
15271532
uiInterface.ThreadSafeMessageBox(
15281533
_("Error reading from database, shutting down.").translated,
15291534
"", CClientUIInterface::MSG_ERROR);
15301535
});
15311536

15321537
// If necessary, upgrade from older database format.
15331538
// This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
1534-
if (!pcoinsdbview->Upgrade()) {
1539+
if (!::ChainstateActive().CoinsDB().Upgrade()) {
15351540
strLoadError = _("Error upgrading chainstate database").translated;
15361541
break;
15371542
}
15381543

15391544
// ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
1540-
if (!ReplayBlocks(chainparams, pcoinsdbview.get())) {
1545+
if (!ReplayBlocks(chainparams, &::ChainstateActive().CoinsDB())) {
15411546
strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.").translated;
15421547
break;
15431548
}
15441549

15451550
// The on-disk coinsdb is now in a good state, create the cache
1546-
pcoinsTip.reset(new CCoinsViewCache(pcoinscatcher.get()));
1551+
::ChainstateActive().InitCoinsCache();
1552+
assert(::ChainstateActive().CanFlushToDisk());
15471553

1548-
is_coinsview_empty = fReset || fReindexChainState || pcoinsTip->GetBestBlock().IsNull();
1554+
is_coinsview_empty = fReset || fReindexChainState ||
1555+
::ChainstateActive().CoinsTip().GetBestBlock().IsNull();
15491556
if (!is_coinsview_empty) {
1550-
// LoadChainTip sets ::ChainActive() based on pcoinsTip's best block
1557+
// LoadChainTip sets ::ChainActive() based on CoinsTip()'s best block
15511558
if (!LoadChainTip(chainparams)) {
15521559
strLoadError = _("Error initializing block database").translated;
15531560
break;
@@ -1589,7 +1596,7 @@ bool AppInitMain(InitInterfaces& interfaces)
15891596
break;
15901597
}
15911598

1592-
if (!CVerifyDB().VerifyDB(chainparams, pcoinsdbview.get(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
1599+
if (!CVerifyDB().VerifyDB(chainparams, &::ChainstateActive().CoinsDB(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
15931600
gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
15941601
strLoadError = _("Corrupted block database detected").translated;
15951602
break;

src/interfaces/node.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ class NodeImpl : public Node
232232
bool getUnspentOutput(const COutPoint& output, Coin& coin) override
233233
{
234234
LOCK(::cs_main);
235-
return ::pcoinsTip->GetCoin(output, coin);
235+
return ::ChainstateActive().CoinsTip().GetCoin(output, coin);
236236
}
237237
std::string getWalletDir() override
238238
{

src/net_processing.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,11 +1291,12 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
12911291
LOCK(g_cs_orphans);
12921292
if (mapOrphanTransactions.count(inv.hash)) return true;
12931293
}
1294+
const CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip();
12941295

12951296
return recentRejects->contains(inv.hash) ||
12961297
mempool.exists(inv.hash) ||
1297-
pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 0)) || // Best effort: only try output 0 and 1
1298-
pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 1));
1298+
coins_cache.HaveCoinInCache(COutPoint(inv.hash, 0)) || // Best effort: only try output 0 and 1
1299+
coins_cache.HaveCoinInCache(COutPoint(inv.hash, 1));
12991300
}
13001301
case MSG_BLOCK:
13011302
case MSG_WITNESS_BLOCK:
@@ -1844,7 +1845,7 @@ void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_se
18441845
EraseOrphanTx(orphanHash);
18451846
done = true;
18461847
}
1847-
mempool.check(pcoinsTip.get());
1848+
mempool.check(&::ChainstateActive().CoinsTip());
18481849
}
18491850
}
18501851

@@ -2497,7 +2498,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
24972498

24982499
if (!AlreadyHave(inv) &&
24992500
AcceptToMemoryPool(mempool, state, ptx, &fMissingInputs, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
2500-
mempool.check(pcoinsTip.get());
2501+
mempool.check(&::ChainstateActive().CoinsTip());
25012502
RelayTransaction(tx.GetHash(), *connman);
25022503
for (unsigned int i = 0; i < tx.vout.size(); i++) {
25032504
auto it_by_prev = mapOrphanTransactionsByPrev.find(COutPoint(inv.hash, i));

src/node/coin.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
void FindCoins(std::map<COutPoint, Coin>& coins)
1111
{
1212
LOCK2(cs_main, ::mempool.cs);
13-
assert(pcoinsTip);
14-
CCoinsViewCache& chain_view = *::pcoinsTip;
13+
CCoinsViewCache& chain_view = ::ChainstateActive().CoinsTip();
1514
CCoinsViewMemPool mempool_view(&chain_view, ::mempool);
1615
for (auto& coin : coins) {
1716
if (!mempool_view.GetCoin(coin.first, coin.second)) {

src/node/transaction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ TransactionError BroadcastTransaction(const CTransactionRef tx, std::string& err
2828
LOCK(cs_main);
2929
// If the transaction is already confirmed in the chain, don't do anything
3030
// and return early.
31-
CCoinsViewCache &view = *pcoinsTip;
31+
CCoinsViewCache &view = ::ChainstateActive().CoinsTip();
3232
for (size_t o = 0; o < tx->vout.size(); o++) {
3333
const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o));
3434
// IsSpent doesnt mean the coin is spent, it means the output doesnt' exist.

src/rest.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -503,12 +503,12 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
503503
if (fCheckMemPool) {
504504
// use db+mempool as cache backend in case user likes to query mempool
505505
LOCK2(cs_main, mempool.cs);
506-
CCoinsViewCache& viewChain = *pcoinsTip;
506+
CCoinsViewCache& viewChain = ::ChainstateActive().CoinsTip();
507507
CCoinsViewMemPool viewMempool(&viewChain, mempool);
508508
process_utxos(viewMempool, mempool);
509509
} else {
510510
LOCK(cs_main); // no need to lock mempool!
511-
process_utxos(*pcoinsTip, CTxMemPool());
511+
process_utxos(::ChainstateActive().CoinsTip(), CTxMemPool());
512512
}
513513

514514
for (size_t i = 0; i < hits.size(); ++i) {

src/rpc/blockchain.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,7 +1062,9 @@ static UniValue gettxoutsetinfo(const JSONRPCRequest& request)
10621062

10631063
CCoinsStats stats;
10641064
::ChainstateActive().ForceFlushStateToDisk();
1065-
if (GetUTXOStats(pcoinsdbview.get(), stats)) {
1065+
1066+
CCoinsView* coins_view = WITH_LOCK(cs_main, return &ChainstateActive().CoinsDB());
1067+
if (GetUTXOStats(coins_view, stats)) {
10661068
ret.pushKV("height", (int64_t)stats.nHeight);
10671069
ret.pushKV("bestblock", stats.hashBlock.GetHex());
10681070
ret.pushKV("transactions", (int64_t)stats.nTransactions);
@@ -1126,19 +1128,21 @@ UniValue gettxout(const JSONRPCRequest& request)
11261128
fMempool = request.params[2].get_bool();
11271129

11281130
Coin coin;
1131+
CCoinsViewCache* coins_view = &::ChainstateActive().CoinsTip();
1132+
11291133
if (fMempool) {
11301134
LOCK(mempool.cs);
1131-
CCoinsViewMemPool view(pcoinsTip.get(), mempool);
1135+
CCoinsViewMemPool view(coins_view, mempool);
11321136
if (!view.GetCoin(out, coin) || mempool.isSpent(out)) {
11331137
return NullUniValue;
11341138
}
11351139
} else {
1136-
if (!pcoinsTip->GetCoin(out, coin)) {
1140+
if (!coins_view->GetCoin(out, coin)) {
11371141
return NullUniValue;
11381142
}
11391143
}
11401144

1141-
const CBlockIndex* pindex = LookupBlockIndex(pcoinsTip->GetBestBlock());
1145+
const CBlockIndex* pindex = LookupBlockIndex(coins_view->GetBestBlock());
11421146
ret.pushKV("bestblock", pindex->GetBlockHash().GetHex());
11431147
if (coin.nHeight == MEMPOOL_HEIGHT) {
11441148
ret.pushKV("confirmations", 0);
@@ -1180,7 +1184,8 @@ static UniValue verifychain(const JSONRPCRequest& request)
11801184
if (!request.params[1].isNull())
11811185
nCheckDepth = request.params[1].get_int();
11821186

1183-
return CVerifyDB().VerifyDB(Params(), pcoinsTip.get(), nCheckLevel, nCheckDepth);
1187+
return CVerifyDB().VerifyDB(
1188+
Params(), &::ChainstateActive().CoinsTip(), nCheckLevel, nCheckDepth);
11841189
}
11851190

11861191
/** Implementation of IsSuperMajority with better feedback */
@@ -2203,7 +2208,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
22032208
{
22042209
LOCK(cs_main);
22052210
::ChainstateActive().ForceFlushStateToDisk();
2206-
pcursor = std::unique_ptr<CCoinsViewCursor>(pcoinsdbview->Cursor());
2211+
pcursor = std::unique_ptr<CCoinsViewCursor>(::ChainstateActive().CoinsDB().Cursor());
22072212
assert(pcursor);
22082213
}
22092214
bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins);

src/rpc/rawtransaction.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
259259

260260
// Loop through txids and try to find which block they're in. Exit loop once a block is found.
261261
for (const auto& tx : setTxids) {
262-
const Coin& coin = AccessByTxid(*pcoinsTip, tx);
262+
const Coin& coin = AccessByTxid(::ChainstateActive().CoinsTip(), tx);
263263
if (!coin.IsSpent()) {
264264
pblockindex = ::ChainActive()[coin.nHeight];
265265
break;
@@ -636,7 +636,7 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
636636
{
637637
LOCK(cs_main);
638638
LOCK(mempool.cs);
639-
CCoinsViewCache &viewChain = *pcoinsTip;
639+
CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip();
640640
CCoinsViewMemPool viewMempool(&viewChain, mempool);
641641
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
642642

@@ -1505,7 +1505,7 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request)
15051505
CCoinsViewCache view(&viewDummy);
15061506
{
15071507
LOCK2(cs_main, mempool.cs);
1508-
CCoinsViewCache &viewChain = *pcoinsTip;
1508+
CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip();
15091509
CCoinsViewMemPool viewMempool(&viewChain, mempool);
15101510
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
15111511

src/test/miner_tests.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
372372
CBlockIndex* prev = ::ChainActive().Tip();
373373
CBlockIndex* next = new CBlockIndex();
374374
next->phashBlock = new uint256(InsecureRand256());
375-
pcoinsTip->SetBestBlock(next->GetBlockHash());
375+
::ChainstateActive().CoinsTip().SetBestBlock(next->GetBlockHash());
376376
next->pprev = prev;
377377
next->nHeight = prev->nHeight + 1;
378378
next->BuildSkip();
@@ -384,7 +384,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
384384
CBlockIndex* prev = ::ChainActive().Tip();
385385
CBlockIndex* next = new CBlockIndex();
386386
next->phashBlock = new uint256(InsecureRand256());
387-
pcoinsTip->SetBestBlock(next->GetBlockHash());
387+
::ChainstateActive().CoinsTip().SetBestBlock(next->GetBlockHash());
388388
next->pprev = prev;
389389
next->nHeight = prev->nHeight + 1;
390390
next->BuildSkip();
@@ -414,7 +414,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
414414
while (::ChainActive().Tip()->nHeight > nHeight) {
415415
CBlockIndex* del = ::ChainActive().Tip();
416416
::ChainActive().SetTip(del->pprev);
417-
pcoinsTip->SetBestBlock(del->pprev->GetBlockHash());
417+
::ChainstateActive().CoinsTip().SetBestBlock(del->pprev->GetBlockHash());
418418
delete del->phashBlock;
419419
delete del;
420420
}

src/test/setup_common.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,12 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
8585

8686
mempool.setSanityCheck(1.0);
8787
pblocktree.reset(new CBlockTreeDB(1 << 20, true));
88-
pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true));
89-
pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get()));
88+
g_chainstate = MakeUnique<CChainState>();
89+
::ChainstateActive().InitCoinsDB(
90+
/* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
91+
assert(!::ChainstateActive().CanFlushToDisk());
92+
::ChainstateActive().InitCoinsCache();
93+
assert(::ChainstateActive().CanFlushToDisk());
9094
if (!LoadGenesisBlock(chainparams)) {
9195
throw std::runtime_error("LoadGenesisBlock failed.");
9296
}
@@ -113,8 +117,7 @@ TestingSetup::~TestingSetup()
113117
g_connman.reset();
114118
g_banman.reset();
115119
UnloadBlockIndex();
116-
pcoinsTip.reset();
117-
pcoinsdbview.reset();
120+
g_chainstate.reset();
118121
pblocktree.reset();
119122
}
120123

0 commit comments

Comments
 (0)