Same as IsValid()
, except 3 of 4 callers of IsBlockPruned()
already own cs_main
. The 4th caller, blockToJSON()
could be changed to acquire the mutex.
0diff --git i/src/node/blockstorage.cpp w/src/node/blockstorage.cpp
1index 5074080ed1..2217a4d6c8 100644
2--- i/src/node/blockstorage.cpp
3+++ w/src/node/blockstorage.cpp
4@@ -424,13 +424,13 @@ CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
5 }
6 return nullptr;
7 }
8
9 bool IsBlockPruned(const CBlockIndex* pblockindex)
10 {
11- LOCK(::cs_main);
12+ AssertLockHeld(cs_main);
13 return (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0);
14 }
15
16 // If we're using -prune with -reindex, then delete block files that will be ignored by the
17 // reindex. Since reindexing works by starting at block file 0 and looping until a blockfile
18 // is missing, do the same here to delete any later block files after a gap. Also delete all
19diff --git i/src/node/blockstorage.h w/src/node/blockstorage.h
20index 7efa7e3b72..0cdab404da 100644
21--- i/src/node/blockstorage.h
22+++ w/src/node/blockstorage.h
23@@ -163,13 +163,13 @@ public:
24 {
25 Unload();
26 }
27 };
28
29 //! Check whether the block associated with this index entry is pruned or not.
30-bool IsBlockPruned(const CBlockIndex* pblockindex);
31+bool IsBlockPruned(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
32
33 void CleanupBlockRevFiles();
34
35 /** Open a block file (blk?????.dat) */
36 FILE* OpenBlockFile(const FlatFilePos& pos, bool fReadOnly = false);
37 /** Translation to a filesystem path */
38diff --git i/src/rpc/blockchain.cpp w/src/rpc/blockchain.cpp
39index 455c32c57a..d7c1572099 100644
40--- i/src/rpc/blockchain.cpp
41+++ w/src/rpc/blockchain.cpp
42@@ -172,13 +172,17 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
43 }
44 break;
45
46 case TxVerbosity::SHOW_DETAILS:
47 case TxVerbosity::SHOW_DETAILS_AND_PREVOUT:
48 CBlockUndo blockUndo;
49- const bool have_undo = !IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex);
50+ bool have_undo;
51+ {
52+ LOCK(cs_main);
53+ have_undo = !IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex);
54+ }
55
56 for (size_t i = 0; i < block.vtx.size(); ++i) {
57 const CTransactionRef& tx = block.vtx.at(i);
58 // coinbase transaction (i.e. i == 0) doesn't have undo data
59 const CTxUndo* txundo = (have_undo && i > 0) ? &blockUndo.vtxundo.at(i - 1) : nullptr;
60 UniValue objTx(UniValue::VOBJ);
61@@ -925,14 +929,15 @@ static RPCHelpMan getblockheader()
62
63 return blockheaderToJSON(tip, pblockindex);
64 },
65 };
66 }
67
68-static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
69+static CBlock GetBlockChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
70 {
71+ AssertLockHeld(cs_main);
72 CBlock block;
73 if (IsBlockPruned(pblockindex)) {
74 throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)");
75 }
76
77 if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) {
78@@ -942,14 +947,15 @@ static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
79 throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk");
80 }
81
82 return block;
83 }
84
85-static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex)
86+static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
87 {
88+ AssertLockHeld(cs_main);
89 CBlockUndo blockUndo;
90 if (IsBlockPruned(pblockindex)) {
91 throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
92 }
93
94 if (!UndoReadFromDisk(blockUndo, pblockindex)) {
I think it is better to call UndoReadFromDisk()
also under the same acquisition of cs_main
as IsBlockPruned()
(as in the patch above) because that would ensure nStatus
remains unchanged until after the disk read has completed (i.e. we don’t end up with a status that indicates no file on disk and still trying to read the file, if nStatus
is changed after we read it and before we read the file from disk).