Skip to content

Commit ae9121f

Browse files
committed
node/chainstate: Decouple from stringy errors
This allows us to separate the initialization code from translations and error reporting. This change changes the caller semantics of LoadChainstate quite drastically. To see that this change doesn't change behaviour, observe that: 1. Prior to this change, LoadChainstate returned false only in the "bad genesis block" failure case (by returning InitError()), indicating that the caller should immediately bail. After this change, the corresponding ERROR_BAD_GENESIS_BLOCK handler in src/init.cpp maintains behavioue by also bailing immediately. 2. The failed_* temporary booleans were only used to break out of the outer do/while(false) loop. They can therefore be safely removed.
1 parent cbac28b commit ae9121f

File tree

3 files changed

+109
-86
lines changed

3 files changed

+109
-86
lines changed

src/init.cpp

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,11 +1418,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
14181418
bilingual_str strLoadError;
14191419

14201420
uiInterface.InitMessage(_("Loading block index…").translated);
1421-
14221421
const int64_t load_block_index_start_time = GetTimeMillis();
1423-
bool rv = LoadChainstate(fLoaded,
1424-
strLoadError,
1425-
fReset,
1422+
auto rv = LoadChainstate(fReset,
14261423
chainman,
14271424
node,
14281425
fPruneMode,
@@ -1432,8 +1429,48 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
14321429
nBlockTreeDBCache,
14331430
nCoinDBCache,
14341431
nCoinCacheUsage);
1435-
if (!rv) return false;
1436-
if (fLoaded) {
1432+
if (rv.has_value()) {
1433+
switch (rv.value()) {
1434+
case ChainstateLoadingError::ERROR_LOADING_BLOCK_DB:
1435+
strLoadError = _("Error loading block database");
1436+
break;
1437+
case ChainstateLoadingError::ERROR_BAD_GENESIS_BLOCK:
1438+
return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?"));
1439+
case ChainstateLoadingError::ERROR_PRUNED_NEEDS_REINDEX:
1440+
strLoadError = _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain");
1441+
break;
1442+
case ChainstateLoadingError::ERROR_LOAD_GENESIS_BLOCK_FAILED:
1443+
strLoadError = _("Error initializing block database");
1444+
break;
1445+
case ChainstateLoadingError::ERROR_CHAINSTATE_UPGRADE_FAILED:
1446+
strLoadError = _("Error upgrading chainstate database");
1447+
break;
1448+
case ChainstateLoadingError::ERROR_REPLAYBLOCKS_FAILED:
1449+
strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.");
1450+
break;
1451+
case ChainstateLoadingError::ERROR_LOADCHAINTIP_FAILED:
1452+
strLoadError = _("Error initializing block database");
1453+
break;
1454+
case ChainstateLoadingError::ERROR_GENERIC_BLOCKDB_OPEN_FAILED:
1455+
strLoadError = _("Error opening block database");
1456+
break;
1457+
case ChainstateLoadingError::ERROR_BLOCKS_WITNESS_INSUFFICIENTLY_VALIDATED:
1458+
strLoadError = strprintf(_("Witness data for blocks after height %d requires validation. Please restart with -reindex."),
1459+
chainparams.GetConsensus().SegwitHeight);
1460+
break;
1461+
case ChainstateLoadingError::ERROR_BLOCK_FROM_FUTURE:
1462+
strLoadError = _("The block database contains a block which appears to be from the future. "
1463+
"This may be due to your computer's date and time being set incorrectly. "
1464+
"Only rebuild the block database if you are sure that your computer's date and time are correct");
1465+
break;
1466+
case ChainstateLoadingError::ERROR_CORRUPTED_BLOCK_DB:
1467+
strLoadError = _("Corrupted block database detected");
1468+
break;
1469+
case ChainstateLoadingError::SHUTDOWN_PROBED:
1470+
break;
1471+
}
1472+
} else {
1473+
fLoaded = true;
14371474
LogPrintf(" block index %15dms\n", GetTimeMillis() - load_block_index_start_time);
14381475
}
14391476

src/node/chainstate.cpp

Lines changed: 26 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,23 @@
77
#include <chainparams.h> // for CChainParams
88
#include <rpc/blockchain.h> // for RPCNotifyBlockChange
99
#include <util/time.h> // for GetTime
10-
#include <util/translation.h> // for bilingual_str
1110
#include <node/blockstorage.h> // for CleanupBlockRevFiles, fHavePruned, fReindex
1211
#include <node/context.h> // for NodeContext
1312
#include <node/ui_interface.h> // for InitError, uiInterface, and CClientUIInterface member access
1413
#include <shutdown.h> // for ShutdownRequested
1514
#include <validation.h> // for a lot of things
1615

17-
bool LoadChainstate(bool& fLoaded,
18-
bilingual_str& strLoadError,
19-
bool fReset,
20-
ChainstateManager& chainman,
21-
NodeContext& node,
22-
bool fPruneMode,
23-
const CChainParams& chainparams,
24-
const ArgsManager& args,
25-
bool fReindexChainState,
26-
int64_t nBlockTreeDBCache,
27-
int64_t nCoinDBCache,
28-
int64_t nCoinCacheUsage) {
16+
std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
17+
ChainstateManager& chainman,
18+
NodeContext& node,
19+
bool fPruneMode,
20+
const CChainParams& chainparams,
21+
const ArgsManager& args,
22+
bool fReindexChainState,
23+
int64_t nBlockTreeDBCache,
24+
int64_t nCoinDBCache,
25+
int64_t nCoinCacheUsage)
26+
{
2927
auto is_coinsview_empty = [&](CChainState* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
3028
return fReset || fReindexChainState || chainstate->CoinsTip().GetBestBlock().IsNull();
3129
};
@@ -52,46 +50,41 @@ bool LoadChainstate(bool& fLoaded,
5250
CleanupBlockRevFiles();
5351
}
5452

55-
if (ShutdownRequested()) break;
53+
if (ShutdownRequested()) return ChainstateLoadingError::SHUTDOWN_PROBED;
5654

5755
// LoadBlockIndex will load fHavePruned if we've ever removed a
5856
// block file from disk.
5957
// Note that it also sets fReindex based on the disk flag!
6058
// From here on out fReindex and fReset mean something different!
6159
if (!chainman.LoadBlockIndex()) {
62-
if (ShutdownRequested()) break;
63-
strLoadError = _("Error loading block database");
64-
break;
60+
if (ShutdownRequested()) return ChainstateLoadingError::SHUTDOWN_PROBED;
61+
return ChainstateLoadingError::ERROR_LOADING_BLOCK_DB;
6562
}
6663

6764
// If the loaded chain has a wrong genesis, bail out immediately
6865
// (we're likely using a testnet datadir, or the other way around).
6966
if (!chainman.BlockIndex().empty() &&
7067
!chainman.m_blockman.LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) {
71-
return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?"));
68+
return ChainstateLoadingError::ERROR_BAD_GENESIS_BLOCK;
7269
}
7370

7471
// Check for changed -prune state. What we are concerned about is a user who has pruned blocks
7572
// in the past, but is now trying to run unpruned.
7673
if (fHavePruned && !fPruneMode) {
77-
strLoadError = _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain");
78-
break;
74+
return ChainstateLoadingError::ERROR_PRUNED_NEEDS_REINDEX;
7975
}
8076

8177
// At this point blocktree args are consistent with what's on disk.
8278
// If we're not mid-reindex (based on disk + args), add a genesis block on disk
8379
// (otherwise we use the one already on disk).
8480
// This is called again in ThreadImport after the reindex completes.
8581
if (!fReindex && !chainman.ActiveChainstate().LoadGenesisBlock()) {
86-
strLoadError = _("Error initializing block database");
87-
break;
82+
return ChainstateLoadingError::ERROR_LOAD_GENESIS_BLOCK_FAILED;
8883
}
8984

9085
// At this point we're either in reindex or we've loaded a useful
9186
// block tree into BlockIndex()!
9287

93-
bool failed_chainstate_init = false;
94-
9588
for (CChainState* chainstate : chainman.GetAll()) {
9689
chainstate->InitCoinsDB(
9790
/* cache_size_bytes */ nCoinDBCache,
@@ -107,16 +100,12 @@ bool LoadChainstate(bool& fLoaded,
107100
// If necessary, upgrade from older database format.
108101
// This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
109102
if (!chainstate->CoinsDB().Upgrade()) {
110-
strLoadError = _("Error upgrading chainstate database");
111-
failed_chainstate_init = true;
112-
break;
103+
return ChainstateLoadingError::ERROR_CHAINSTATE_UPGRADE_FAILED;
113104
}
114105

115106
// ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
116107
if (!chainstate->ReplayBlocks()) {
117-
strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.");
118-
failed_chainstate_init = true;
119-
break;
108+
return ChainstateLoadingError::ERROR_REPLAYBLOCKS_FAILED;
120109
}
121110

122111
// The on-disk coinsdb is now in a good state, create the cache
@@ -126,36 +115,25 @@ bool LoadChainstate(bool& fLoaded,
126115
if (!is_coinsview_empty(chainstate)) {
127116
// LoadChainTip initializes the chain based on CoinsTip()'s best block
128117
if (!chainstate->LoadChainTip()) {
129-
strLoadError = _("Error initializing block database");
130-
failed_chainstate_init = true;
131-
break; // out of the per-chainstate loop
118+
return ChainstateLoadingError::ERROR_LOADCHAINTIP_FAILED;
132119
}
133120
assert(chainstate->m_chain.Tip() != nullptr);
134121
}
135122
}
136-
137-
if (failed_chainstate_init) {
138-
break; // out of the chainstate activation do-while
139-
}
140123
} catch (const std::exception& e) {
141124
LogPrintf("%s\n", e.what());
142-
strLoadError = _("Error opening block database");
143-
break;
125+
return ChainstateLoadingError::ERROR_GENERIC_BLOCKDB_OPEN_FAILED;
144126
}
145127

146128
if (!fReset) {
147129
LOCK(cs_main);
148130
auto chainstates{chainman.GetAll()};
149131
if (std::any_of(chainstates.begin(), chainstates.end(),
150132
[](const CChainState* cs) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return cs->NeedsRedownload(); })) {
151-
strLoadError = strprintf(_("Witness data for blocks after height %d requires validation. Please restart with -reindex."),
152-
chainparams.GetConsensus().SegwitHeight);
153-
break;
133+
return ChainstateLoadingError::ERROR_BLOCKS_WITNESS_INSUFFICIENTLY_VALIDATED;
154134
}
155135
}
156136

157-
bool failed_verification = false;
158-
159137
try {
160138
LOCK(cs_main);
161139

@@ -170,33 +148,21 @@ bool LoadChainstate(bool& fLoaded,
170148
const CBlockIndex* tip = chainstate->m_chain.Tip();
171149
RPCNotifyBlockChange(tip);
172150
if (tip && tip->nTime > GetTime() + MAX_FUTURE_BLOCK_TIME) {
173-
strLoadError = _("The block database contains a block which appears to be from the future. "
174-
"This may be due to your computer's date and time being set incorrectly. "
175-
"Only rebuild the block database if you are sure that your computer's date and time are correct");
176-
failed_verification = true;
177-
break;
151+
return ChainstateLoadingError::ERROR_BLOCK_FROM_FUTURE;
178152
}
179153

180154
if (!CVerifyDB().VerifyDB(
181155
*chainstate, chainparams, chainstate->CoinsDB(),
182156
args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL),
183157
args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
184-
strLoadError = _("Corrupted block database detected");
185-
failed_verification = true;
186-
break;
158+
return ChainstateLoadingError::ERROR_CORRUPTED_BLOCK_DB;
187159
}
188160
}
189161
}
190162
} catch (const std::exception& e) {
191163
LogPrintf("%s\n", e.what());
192-
strLoadError = _("Error opening block database");
193-
failed_verification = true;
194-
break;
195-
}
196-
197-
if (!failed_verification) {
198-
fLoaded = true;
164+
return ChainstateLoadingError::ERROR_GENERIC_BLOCKDB_OPEN_FAILED;
199165
}
200166
} while(false);
201-
return true;
167+
return std::nullopt;
202168
}

src/node/chainstate.h

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,28 @@
66
#define BITCOIN_NODE_CHAINSTATE_H
77

88
#include <cstdint> // for int64_t
9+
#include <optional> // for std::optional
910

1011
class ArgsManager;
11-
struct bilingual_str;
1212
class CChainParams;
1313
class ChainstateManager;
1414
struct NodeContext;
1515

16+
enum class ChainstateLoadingError {
17+
ERROR_LOADING_BLOCK_DB,
18+
ERROR_BAD_GENESIS_BLOCK,
19+
ERROR_PRUNED_NEEDS_REINDEX,
20+
ERROR_LOAD_GENESIS_BLOCK_FAILED,
21+
ERROR_CHAINSTATE_UPGRADE_FAILED,
22+
ERROR_REPLAYBLOCKS_FAILED,
23+
ERROR_LOADCHAINTIP_FAILED,
24+
ERROR_GENERIC_BLOCKDB_OPEN_FAILED,
25+
ERROR_BLOCKS_WITNESS_INSUFFICIENTLY_VALIDATED,
26+
ERROR_BLOCK_FROM_FUTURE,
27+
ERROR_CORRUPTED_BLOCK_DB,
28+
SHUTDOWN_PROBED,
29+
};
30+
1631
/** This sequence can have 4 types of outcomes:
1732
*
1833
* 1. Success
@@ -24,25 +39,30 @@ struct NodeContext;
2439
* 4. Hard failure
2540
* - a failure that definitively cannot be recovered from with a reindex
2641
*
27-
* Currently, LoadChainstate returns a bool which:
28-
* - if false
29-
* - Definitely a "Hard failure"
30-
* - if true
31-
* - if fLoaded -> "Success"
32-
* - if ShutdownRequested() -> "Shutdown requested"
33-
* - else -> "Soft failure"
42+
* Currently, LoadChainstate returns a std::optional<ChainstateLoadingError>
43+
* which:
44+
*
45+
* - if has_value()
46+
* - Either "Soft failure", "Hard failure", or "Shutdown requested",
47+
* differentiable by the specific enumerator.
48+
*
49+
* Note that a return value of SHUTDOWN_PROBED means ONLY that "during
50+
* this sequence, when we explicitly checked ShutdownRequested() at
51+
* arbitrary points, one of those calls returned true". Therefore, a
52+
* return value other than SHUTDOWN_PROBED does not guarantee that
53+
* ShutdownRequested() hasn't been called indirectly.
54+
* - else
55+
* - Success!
3456
*/
35-
bool LoadChainstate(bool& fLoaded,
36-
bilingual_str& strLoadError,
37-
bool fReset,
38-
ChainstateManager& chainman,
39-
NodeContext& node,
40-
bool fPruneMode,
41-
const CChainParams& chainparams,
42-
const ArgsManager& args,
43-
bool fReindexChainState,
44-
int64_t nBlockTreeDBCache,
45-
int64_t nCoinDBCache,
46-
int64_t nCoinCacheUsage);
57+
std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
58+
ChainstateManager& chainman,
59+
NodeContext& node,
60+
bool fPruneMode,
61+
const CChainParams& chainparams,
62+
const ArgsManager& args,
63+
bool fReindexChainState,
64+
int64_t nBlockTreeDBCache,
65+
int64_t nCoinDBCache,
66+
int64_t nCoinCacheUsage);
4767

4868
#endif // BITCOIN_NODE_CHAINSTATE_H

0 commit comments

Comments
 (0)