Skip to content

Commit 19765dc

Browse files
committed
Merge bitcoin/bitcoin#32694: index: move disk read lookups to base class
029ba1a index: remove CBlockIndex access from CustomAppend() (furszy) 91b7ab6 refactor: index, simplify CopyHeightIndexToHashIndex to process single block (furszy) 6f1392c indexes, refactor: Remove remaining CBlockIndex* uses in index Rewind methods (Ryan Ofsky) 0a24870 indexes, refactor: Stop requiring CBlockIndex type to call IsBIP30Unspendable (Ryan Ofsky) 331a25c test: indexes, avoid creating threads when sync runs synchronously (furszy) Pull request description: Combining common refactors from #24230 and #26966, aiming to move both efforts forward while reducing their size and review burden. Broadly, #24230 focuses on enabling indexes to run in a separate process, and #26966 aims to parallelize the indexes initial synchronization process. A shared prerequisite for both is ensuring that only the base index class interacts with the node’s chain internals - child index classes should instead operate solely through chain events. This PR moves disk read lookups from child index classes to the base index class. It also includes a few documentation improvements and a test-only code cleanup. ACKs for top commit: maflcko: review ACK 029ba1a 👡 achow101: ACK 029ba1a TheCharlatan: Re-ACK 029ba1a davidgumberg: ACK 029ba1a mzumsande: Code Review ACK 029ba1a Tree-SHA512: f073af407fc86f228cb47a32c7bcf2241551cc89ff32059317eb81d5b86fd5fda35f228d2567e0aedbc9fd6826291f5fee05619db35ba44108421ae04d11e6fb
2 parents 5757de4 + 029ba1a commit 19765dc

15 files changed

+167
-196
lines changed

src/index/base.cpp

Lines changed: 62 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <node/database_args.h>
1515
#include <node/interface_ui.h>
1616
#include <tinyformat.h>
17+
#include <undo.h>
1718
#include <util/string.h>
1819
#include <util/thread.h>
1920
#include <util/translation.h>
@@ -143,9 +144,44 @@ static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev, CChain&
143144
return pindex;
144145
}
145146

147+
// Since block is not in the chain, return the next block in the chain AFTER the last common ancestor.
148+
// Caller will be responsible for rewinding back to the common ancestor.
146149
return chain.Next(chain.FindFork(pindex_prev));
147150
}
148151

152+
bool BaseIndex::ProcessBlock(const CBlockIndex* pindex, const CBlock* block_data)
153+
{
154+
interfaces::BlockInfo block_info = kernel::MakeBlockInfo(pindex, block_data);
155+
156+
CBlock block;
157+
if (!block_data) { // disk lookup if block data wasn't provided
158+
if (!m_chainstate->m_blockman.ReadBlock(block, *pindex)) {
159+
FatalErrorf("%s: Failed to read block %s from disk",
160+
__func__, pindex->GetBlockHash().ToString());
161+
return false;
162+
}
163+
block_info.data = &block;
164+
}
165+
166+
CBlockUndo block_undo;
167+
if (CustomOptions().connect_undo_data) {
168+
if (pindex->nHeight > 0 && !m_chainstate->m_blockman.ReadBlockUndo(block_undo, *pindex)) {
169+
FatalErrorf("%s: Failed to read undo block data %s from disk",
170+
__func__, pindex->GetBlockHash().ToString());
171+
return false;
172+
}
173+
block_info.undo_data = &block_undo;
174+
}
175+
176+
if (!CustomAppend(block_info)) {
177+
FatalErrorf("%s: Failed to write block %s to index database",
178+
__func__, pindex->GetBlockHash().ToString());
179+
return false;
180+
}
181+
182+
return true;
183+
}
184+
149185
void BaseIndex::Sync()
150186
{
151187
const CBlockIndex* pindex = m_best_block_index.load();
@@ -191,20 +227,7 @@ void BaseIndex::Sync()
191227
pindex = pindex_next;
192228

193229

194-
CBlock block;
195-
interfaces::BlockInfo block_info = kernel::MakeBlockInfo(pindex);
196-
if (!m_chainstate->m_blockman.ReadBlock(block, *pindex)) {
197-
FatalErrorf("%s: Failed to read block %s from disk",
198-
__func__, pindex->GetBlockHash().ToString());
199-
return;
200-
} else {
201-
block_info.data = &block;
202-
}
203-
if (!CustomAppend(block_info)) {
204-
FatalErrorf("%s: Failed to write block %s to index database",
205-
__func__, pindex->GetBlockHash().ToString());
206-
return;
207-
}
230+
if (!ProcessBlock(pindex)) return; // error logged internally
208231

209232
auto current_time{std::chrono::steady_clock::now()};
210233
if (last_log_time + SYNC_LOG_INTERVAL < current_time) {
@@ -254,8 +277,28 @@ bool BaseIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_ti
254277
assert(current_tip == m_best_block_index);
255278
assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip);
256279

257-
if (!CustomRewind({current_tip->GetBlockHash(), current_tip->nHeight}, {new_tip->GetBlockHash(), new_tip->nHeight})) {
258-
return false;
280+
CBlock block;
281+
CBlockUndo block_undo;
282+
283+
for (const CBlockIndex* iter_tip = current_tip; iter_tip != new_tip; iter_tip = iter_tip->pprev) {
284+
interfaces::BlockInfo block_info = kernel::MakeBlockInfo(iter_tip);
285+
if (CustomOptions().disconnect_data) {
286+
if (!m_chainstate->m_blockman.ReadBlock(block, *iter_tip)) {
287+
LogError("%s: Failed to read block %s from disk",
288+
__func__, iter_tip->GetBlockHash().ToString());
289+
return false;
290+
}
291+
block_info.data = &block;
292+
}
293+
if (CustomOptions().disconnect_undo_data && iter_tip->nHeight > 0) {
294+
if (!m_chainstate->m_blockman.ReadBlockUndo(block_undo, *iter_tip)) {
295+
return false;
296+
}
297+
block_info.undo_data = &block_undo;
298+
}
299+
if (!CustomRemove(block_info)) {
300+
return false;
301+
}
259302
}
260303

261304
// In the case of a reorg, ensure persisted block locator is not stale.
@@ -316,17 +359,14 @@ void BaseIndex::BlockConnected(ChainstateRole role, const std::shared_ptr<const
316359
return;
317360
}
318361
}
319-
interfaces::BlockInfo block_info = kernel::MakeBlockInfo(pindex, block.get());
320-
if (CustomAppend(block_info)) {
362+
363+
// Dispatch block to child class; errors are logged internally and abort the node.
364+
if (ProcessBlock(pindex, block.get())) {
321365
// Setting the best block index is intentionally the last step of this
322366
// function, so BlockUntilSyncedToCurrentChain callers waiting for the
323367
// best block index to be updated can rely on the block being fully
324368
// processed, and the index object being safe to delete.
325369
SetBestBlockIndex(pindex);
326-
} else {
327-
FatalErrorf("%s: Failed to write block %s to index",
328-
__func__, pindex->GetBlockHash().ToString());
329-
return;
330370
}
331371
}
332372

src/index/base.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,11 @@ class BaseIndex : public CValidationInterface
9090
/// getting corrupted.
9191
bool Commit();
9292

93-
/// Loop over disconnected blocks and call CustomRewind.
93+
/// Loop over disconnected blocks and call CustomRemove.
9494
bool Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip);
9595

96+
bool ProcessBlock(const CBlockIndex* pindex, const CBlock* block_data = nullptr);
97+
9698
virtual bool AllowPrune() const = 0;
9799

98100
template <typename... Args>
@@ -107,6 +109,9 @@ class BaseIndex : public CValidationInterface
107109

108110
void ChainStateFlushed(ChainstateRole role, const CBlockLocator& locator) override;
109111

112+
/// Return custom notification options for index.
113+
[[nodiscard]] virtual interfaces::Chain::NotifyOptions CustomOptions() { return {}; }
114+
110115
/// Initialize internal state from the database and block index.
111116
[[nodiscard]] virtual bool CustomInit(const std::optional<interfaces::BlockRef>& block) { return true; }
112117

@@ -117,9 +122,8 @@ class BaseIndex : public CValidationInterface
117122
/// commit more index state.
118123
virtual bool CustomCommit(CDBBatch& batch) { return true; }
119124

120-
/// Rewind index to an earlier chain tip during a chain reorg. The tip must
121-
/// be an ancestor of the current best block.
122-
[[nodiscard]] virtual bool CustomRewind(const interfaces::BlockRef& current_tip, const interfaces::BlockRef& new_tip) { return true; }
125+
/// Rewind index by one block during a chain reorg.
126+
[[nodiscard]] virtual bool CustomRemove(const interfaces::BlockInfo& block) { return true; }
123127

124128
virtual DB& GetDB() const = 0;
125129

src/index/blockfilterindex.cpp

Lines changed: 29 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
#include <node/blockstorage.h>
1414
#include <undo.h>
1515
#include <util/fs_helpers.h>
16-
#include <validation.h>
1716

1817
/* The index database stores three items for each block: the disk location of the encoded filter,
1918
* its dSHA256 hash, and the header. Those belonging to blocks on the active chain are indexed by
@@ -112,6 +111,13 @@ BlockFilterIndex::BlockFilterIndex(std::unique_ptr<interfaces::Chain> chain, Blo
112111
m_filter_fileseq = std::make_unique<FlatFileSeq>(std::move(path), "fltr", FLTR_FILE_CHUNK_SIZE);
113112
}
114113

114+
interfaces::Chain::NotifyOptions BlockFilterIndex::CustomOptions()
115+
{
116+
interfaces::Chain::NotifyOptions options;
117+
options.connect_undo_data = true;
118+
return options;
119+
}
120+
115121
bool BlockFilterIndex::CustomInit(const std::optional<interfaces::BlockRef>& block)
116122
{
117123
if (!m_db->Read(DB_FILTER_POS, m_next_filter_pos)) {
@@ -250,19 +256,7 @@ std::optional<uint256> BlockFilterIndex::ReadFilterHeader(int height, const uint
250256

251257
bool BlockFilterIndex::CustomAppend(const interfaces::BlockInfo& block)
252258
{
253-
CBlockUndo block_undo;
254-
255-
if (block.height > 0) {
256-
// pindex variable gives indexing code access to node internals. It
257-
// will be removed in upcoming commit
258-
const CBlockIndex* pindex = WITH_LOCK(cs_main, return m_chainstate->m_blockman.LookupBlockIndex(block.hash));
259-
if (!m_chainstate->m_blockman.ReadBlockUndo(block_undo, *pindex)) {
260-
return false;
261-
}
262-
}
263-
264-
BlockFilter filter(m_filter_type, *Assert(block.data), block_undo);
265-
259+
BlockFilter filter(m_filter_type, *Assert(block.data), *Assert(block.undo_data));
266260
const uint256& header = filter.ComputeHeader(m_last_header);
267261
bool res = Write(filter, block.height, header);
268262
if (res) m_last_header = header; // update last header
@@ -289,42 +283,37 @@ bool BlockFilterIndex::Write(const BlockFilter& filter, uint32_t block_height, c
289283
}
290284

291285
[[nodiscard]] static bool CopyHeightIndexToHashIndex(CDBIterator& db_it, CDBBatch& batch,
292-
const std::string& index_name,
293-
int start_height, int stop_height)
286+
const std::string& index_name, int height)
294287
{
295-
DBHeightKey key(start_height);
288+
DBHeightKey key(height);
296289
db_it.Seek(key);
297290

298-
for (int height = start_height; height <= stop_height; ++height) {
299-
if (!db_it.GetKey(key) || key.height != height) {
300-
LogError("%s: unexpected key in %s: expected (%c, %d)\n",
301-
__func__, index_name, DB_BLOCK_HEIGHT, height);
302-
return false;
303-
}
304-
305-
std::pair<uint256, DBVal> value;
306-
if (!db_it.GetValue(value)) {
307-
LogError("%s: unable to read value in %s at key (%c, %d)\n",
308-
__func__, index_name, DB_BLOCK_HEIGHT, height);
309-
return false;
310-
}
311-
312-
batch.Write(DBHashKey(value.first), std::move(value.second));
291+
if (!db_it.GetKey(key) || key.height != height) {
292+
LogError("%s: unexpected key in %s: expected (%c, %d)\n",
293+
__func__, index_name, DB_BLOCK_HEIGHT, height);
294+
return false;
295+
}
313296

314-
db_it.Next();
297+
std::pair<uint256, DBVal> value;
298+
if (!db_it.GetValue(value)) {
299+
LogError("%s: unable to read value in %s at key (%c, %d)\n",
300+
__func__, index_name, DB_BLOCK_HEIGHT, height);
301+
return false;
315302
}
303+
304+
batch.Write(DBHashKey(value.first), std::move(value.second));
316305
return true;
317306
}
318307

319-
bool BlockFilterIndex::CustomRewind(const interfaces::BlockRef& current_tip, const interfaces::BlockRef& new_tip)
308+
bool BlockFilterIndex::CustomRemove(const interfaces::BlockInfo& block)
320309
{
321310
CDBBatch batch(*m_db);
322311
std::unique_ptr<CDBIterator> db_it(m_db->NewIterator());
323312

324-
// During a reorg, we need to copy all filters for blocks that are getting disconnected from the
325-
// height index to the hash index so we can still find them when the height index entries are
326-
// overwritten.
327-
if (!CopyHeightIndexToHashIndex(*db_it, batch, m_name, new_tip.height, current_tip.height)) {
313+
// During a reorg, we need to copy block filter that is getting disconnected from the
314+
// height index to the hash index so we can still find it when the height index entry
315+
// is overwritten.
316+
if (!CopyHeightIndexToHashIndex(*db_it, batch, m_name, block.height)) {
328317
return false;
329318
}
330319

@@ -334,8 +323,8 @@ bool BlockFilterIndex::CustomRewind(const interfaces::BlockRef& current_tip, con
334323
batch.Write(DB_FILTER_POS, m_next_filter_pos);
335324
if (!m_db->WriteBatch(batch)) return false;
336325

337-
// Update cached header
338-
m_last_header = *Assert(ReadFilterHeader(new_tip.height, new_tip.hash));
326+
// Update cached header to the previous block hash
327+
m_last_header = *Assert(ReadFilterHeader(block.height - 1, *Assert(block.prev_hash)));
339328
return true;
340329
}
341330

src/index/blockfilterindex.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,15 @@ class BlockFilterIndex final : public BaseIndex
5252
std::optional<uint256> ReadFilterHeader(int height, const uint256& expected_block_hash);
5353

5454
protected:
55+
interfaces::Chain::NotifyOptions CustomOptions() override;
56+
5557
bool CustomInit(const std::optional<interfaces::BlockRef>& block) override;
5658

5759
bool CustomCommit(CDBBatch& batch) override;
5860

5961
bool CustomAppend(const interfaces::BlockInfo& block) override;
6062

61-
bool CustomRewind(const interfaces::BlockRef& current_tip, const interfaces::BlockRef& new_tip) override;
63+
bool CustomRemove(const interfaces::BlockInfo& block) override;
6264

6365
BaseIndex::DB& GetDB() const LIFETIMEBOUND override { return *m_db; }
6466

0 commit comments

Comments
 (0)