Skip to content

Commit ea263e1

Browse files
committed
Merge #13243: Make reusable base class for auxiliary indices
ec3073a index: Move index DBs into index/ directory. (Jim Posen) 89eddcd index: Remove TxIndexDB from public interface of TxIndex. (Jim Posen) 2318aff MOVEONLY: Move BaseIndex to its own file. (Jim Posen) f376a49 index: Generalize logged statements in BaseIndex. (Jim Posen) 61a1226 index: Extract logic from TxIndex into reusable base class. (Jim Posen) e5af5fc db: Make reusable base class for index databases. (Jim Posen) 9b0ec1a db: Remove obsolete methods from CBlockTreeDB. (Jim Posen) Pull request description: This refactors most of the logic in TxIndex into a reusable base class for other indices. There are two commits moving code between files, which may be be more easily reviewed using `git diff --color-moved` (https://blog.github.com/2018-04-05-git-217-released/). The motivation for this is to support BIP 157 by indexing block filters. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/bitcoin/bitcoin/13243) <!-- Reviewable:end --> Tree-SHA512: 0857f04df2aa920178dab2eb8e57984d8eb4d5010deca9971190358479e05b6672ccca2a08af0a7ac9fe02afb947be84cf35a3693204d0667263c6add2959cbf
2 parents 3d3d8ae + ec3073a commit ea263e1

File tree

9 files changed

+594
-524
lines changed

9 files changed

+594
-524
lines changed

src/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ BITCOIN_CORE_H = \
107107
fs.h \
108108
httprpc.h \
109109
httpserver.h \
110+
index/base.h \
110111
index/txindex.h \
111112
indirectmap.h \
112113
init.h \
@@ -208,6 +209,7 @@ libbitcoin_server_a_SOURCES = \
208209
consensus/tx_verify.cpp \
209210
httprpc.cpp \
210211
httpserver.cpp \
212+
index/base.cpp \
211213
index/txindex.cpp \
212214
init.cpp \
213215
dbwrapper.cpp \

src/index/base.cpp

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
// Copyright (c) 2017-2018 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <chainparams.h>
6+
#include <index/base.h>
7+
#include <init.h>
8+
#include <tinyformat.h>
9+
#include <ui_interface.h>
10+
#include <util.h>
11+
#include <validation.h>
12+
#include <warnings.h>
13+
14+
constexpr char DB_BEST_BLOCK = 'B';
15+
16+
constexpr int64_t SYNC_LOG_INTERVAL = 30; // seconds
17+
constexpr int64_t SYNC_LOCATOR_WRITE_INTERVAL = 30; // seconds
18+
19+
template<typename... Args>
20+
static void FatalError(const char* fmt, const Args&... args)
21+
{
22+
std::string strMessage = tfm::format(fmt, args...);
23+
SetMiscWarning(strMessage);
24+
LogPrintf("*** %s\n", strMessage);
25+
uiInterface.ThreadSafeMessageBox(
26+
"Error: A fatal internal error occurred, see debug.log for details",
27+
"", CClientUIInterface::MSG_ERROR);
28+
StartShutdown();
29+
}
30+
31+
BaseIndex::DB::DB(const fs::path& path, size_t n_cache_size, bool f_memory, bool f_wipe, bool f_obfuscate) :
32+
CDBWrapper(path, n_cache_size, f_memory, f_wipe, f_obfuscate)
33+
{}
34+
35+
bool BaseIndex::DB::ReadBestBlock(CBlockLocator& locator) const
36+
{
37+
bool success = Read(DB_BEST_BLOCK, locator);
38+
if (!success) {
39+
locator.SetNull();
40+
}
41+
return success;
42+
}
43+
44+
bool BaseIndex::DB::WriteBestBlock(const CBlockLocator& locator)
45+
{
46+
return Write(DB_BEST_BLOCK, locator);
47+
}
48+
49+
BaseIndex::~BaseIndex()
50+
{
51+
Interrupt();
52+
Stop();
53+
}
54+
55+
bool BaseIndex::Init()
56+
{
57+
CBlockLocator locator;
58+
if (!GetDB().ReadBestBlock(locator)) {
59+
locator.SetNull();
60+
}
61+
62+
LOCK(cs_main);
63+
m_best_block_index = FindForkInGlobalIndex(chainActive, locator);
64+
m_synced = m_best_block_index.load() == chainActive.Tip();
65+
return true;
66+
}
67+
68+
static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev)
69+
{
70+
AssertLockHeld(cs_main);
71+
72+
if (!pindex_prev) {
73+
return chainActive.Genesis();
74+
}
75+
76+
const CBlockIndex* pindex = chainActive.Next(pindex_prev);
77+
if (pindex) {
78+
return pindex;
79+
}
80+
81+
return chainActive.Next(chainActive.FindFork(pindex_prev));
82+
}
83+
84+
void BaseIndex::ThreadSync()
85+
{
86+
const CBlockIndex* pindex = m_best_block_index.load();
87+
if (!m_synced) {
88+
auto& consensus_params = Params().GetConsensus();
89+
90+
int64_t last_log_time = 0;
91+
int64_t last_locator_write_time = 0;
92+
while (true) {
93+
if (m_interrupt) {
94+
WriteBestBlock(pindex);
95+
return;
96+
}
97+
98+
{
99+
LOCK(cs_main);
100+
const CBlockIndex* pindex_next = NextSyncBlock(pindex);
101+
if (!pindex_next) {
102+
WriteBestBlock(pindex);
103+
m_best_block_index = pindex;
104+
m_synced = true;
105+
break;
106+
}
107+
pindex = pindex_next;
108+
}
109+
110+
int64_t current_time = GetTime();
111+
if (last_log_time + SYNC_LOG_INTERVAL < current_time) {
112+
LogPrintf("Syncing %s with block chain from height %d\n",
113+
GetName(), pindex->nHeight);
114+
last_log_time = current_time;
115+
}
116+
117+
if (last_locator_write_time + SYNC_LOCATOR_WRITE_INTERVAL < current_time) {
118+
WriteBestBlock(pindex);
119+
last_locator_write_time = current_time;
120+
}
121+
122+
CBlock block;
123+
if (!ReadBlockFromDisk(block, pindex, consensus_params)) {
124+
FatalError("%s: Failed to read block %s from disk",
125+
__func__, pindex->GetBlockHash().ToString());
126+
return;
127+
}
128+
if (!WriteBlock(block, pindex)) {
129+
FatalError("%s: Failed to write block %s to index database",
130+
__func__, pindex->GetBlockHash().ToString());
131+
return;
132+
}
133+
}
134+
}
135+
136+
if (pindex) {
137+
LogPrintf("%s is enabled at height %d\n", GetName(), pindex->nHeight);
138+
} else {
139+
LogPrintf("%s is enabled\n", GetName());
140+
}
141+
}
142+
143+
bool BaseIndex::WriteBestBlock(const CBlockIndex* block_index)
144+
{
145+
LOCK(cs_main);
146+
if (!GetDB().WriteBestBlock(chainActive.GetLocator(block_index))) {
147+
return error("%s: Failed to write locator to disk", __func__);
148+
}
149+
return true;
150+
}
151+
152+
void BaseIndex::BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex,
153+
const std::vector<CTransactionRef>& txn_conflicted)
154+
{
155+
if (!m_synced) {
156+
return;
157+
}
158+
159+
const CBlockIndex* best_block_index = m_best_block_index.load();
160+
if (!best_block_index) {
161+
if (pindex->nHeight != 0) {
162+
FatalError("%s: First block connected is not the genesis block (height=%d)",
163+
__func__, pindex->nHeight);
164+
return;
165+
}
166+
} else {
167+
// Ensure block connects to an ancestor of the current best block. This should be the case
168+
// most of the time, but may not be immediately after the sync thread catches up and sets
169+
// m_synced. Consider the case where there is a reorg and the blocks on the stale branch are
170+
// in the ValidationInterface queue backlog even after the sync thread has caught up to the
171+
// new chain tip. In this unlikely event, log a warning and let the queue clear.
172+
if (best_block_index->GetAncestor(pindex->nHeight - 1) != pindex->pprev) {
173+
LogPrintf("%s: WARNING: Block %s does not connect to an ancestor of " /* Continued */
174+
"known best chain (tip=%s); not updating index\n",
175+
__func__, pindex->GetBlockHash().ToString(),
176+
best_block_index->GetBlockHash().ToString());
177+
return;
178+
}
179+
}
180+
181+
if (WriteBlock(*block, pindex)) {
182+
m_best_block_index = pindex;
183+
} else {
184+
FatalError("%s: Failed to write block %s to index",
185+
__func__, pindex->GetBlockHash().ToString());
186+
return;
187+
}
188+
}
189+
190+
void BaseIndex::ChainStateFlushed(const CBlockLocator& locator)
191+
{
192+
if (!m_synced) {
193+
return;
194+
}
195+
196+
const uint256& locator_tip_hash = locator.vHave.front();
197+
const CBlockIndex* locator_tip_index;
198+
{
199+
LOCK(cs_main);
200+
locator_tip_index = LookupBlockIndex(locator_tip_hash);
201+
}
202+
203+
if (!locator_tip_index) {
204+
FatalError("%s: First block (hash=%s) in locator was not found",
205+
__func__, locator_tip_hash.ToString());
206+
return;
207+
}
208+
209+
// This checks that ChainStateFlushed callbacks are received after BlockConnected. The check may fail
210+
// immediately after the sync thread catches up and sets m_synced. Consider the case where
211+
// there is a reorg and the blocks on the stale branch are in the ValidationInterface queue
212+
// backlog even after the sync thread has caught up to the new chain tip. In this unlikely
213+
// event, log a warning and let the queue clear.
214+
const CBlockIndex* best_block_index = m_best_block_index.load();
215+
if (best_block_index->GetAncestor(locator_tip_index->nHeight) != locator_tip_index) {
216+
LogPrintf("%s: WARNING: Locator contains block (hash=%s) not on known best " /* Continued */
217+
"chain (tip=%s); not writing index locator\n",
218+
__func__, locator_tip_hash.ToString(),
219+
best_block_index->GetBlockHash().ToString());
220+
return;
221+
}
222+
223+
if (!GetDB().WriteBestBlock(locator)) {
224+
error("%s: Failed to write locator to disk", __func__);
225+
}
226+
}
227+
228+
bool BaseIndex::BlockUntilSyncedToCurrentChain()
229+
{
230+
AssertLockNotHeld(cs_main);
231+
232+
if (!m_synced) {
233+
return false;
234+
}
235+
236+
{
237+
// Skip the queue-draining stuff if we know we're caught up with
238+
// chainActive.Tip().
239+
LOCK(cs_main);
240+
const CBlockIndex* chain_tip = chainActive.Tip();
241+
const CBlockIndex* best_block_index = m_best_block_index.load();
242+
if (best_block_index->GetAncestor(chain_tip->nHeight) == chain_tip) {
243+
return true;
244+
}
245+
}
246+
247+
LogPrintf("%s: %s is catching up on block notifications\n", __func__, GetName());
248+
SyncWithValidationInterfaceQueue();
249+
return true;
250+
}
251+
252+
void BaseIndex::Interrupt()
253+
{
254+
m_interrupt();
255+
}
256+
257+
void BaseIndex::Start()
258+
{
259+
// Need to register this ValidationInterface before running Init(), so that
260+
// callbacks are not missed if Init sets m_synced to true.
261+
RegisterValidationInterface(this);
262+
if (!Init()) {
263+
FatalError("%s: %s failed to initialize", __func__, GetName());
264+
return;
265+
}
266+
267+
m_thread_sync = std::thread(&TraceThread<std::function<void()>>, GetName(),
268+
std::bind(&BaseIndex::ThreadSync, this));
269+
}
270+
271+
void BaseIndex::Stop()
272+
{
273+
UnregisterValidationInterface(this);
274+
275+
if (m_thread_sync.joinable()) {
276+
m_thread_sync.join();
277+
}
278+
}

src/index/base.h

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright (c) 2017-2018 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_INDEX_BASE_H
6+
#define BITCOIN_INDEX_BASE_H
7+
8+
#include <dbwrapper.h>
9+
#include <primitives/block.h>
10+
#include <primitives/transaction.h>
11+
#include <threadinterrupt.h>
12+
#include <uint256.h>
13+
#include <validationinterface.h>
14+
15+
class CBlockIndex;
16+
17+
/**
18+
* Base class for indices of blockchain data. This implements
19+
* CValidationInterface and ensures blocks are indexed sequentially according
20+
* to their position in the active chain.
21+
*/
22+
class BaseIndex : public CValidationInterface
23+
{
24+
protected:
25+
class DB : public CDBWrapper
26+
{
27+
public:
28+
DB(const fs::path& path, size_t n_cache_size,
29+
bool f_memory = false, bool f_wipe = false, bool f_obfuscate = false);
30+
31+
/// Read block locator of the chain that the txindex is in sync with.
32+
bool ReadBestBlock(CBlockLocator& locator) const;
33+
34+
/// Write block locator of the chain that the txindex is in sync with.
35+
bool WriteBestBlock(const CBlockLocator& locator);
36+
};
37+
38+
private:
39+
/// Whether the index is in sync with the main chain. The flag is flipped
40+
/// from false to true once, after which point this starts processing
41+
/// ValidationInterface notifications to stay in sync.
42+
std::atomic<bool> m_synced{false};
43+
44+
/// The last block in the chain that the index is in sync with.
45+
std::atomic<const CBlockIndex*> m_best_block_index{nullptr};
46+
47+
std::thread m_thread_sync;
48+
CThreadInterrupt m_interrupt;
49+
50+
/// Sync the index with the block index starting from the current best block.
51+
/// Intended to be run in its own thread, m_thread_sync, and can be
52+
/// interrupted with m_interrupt. Once the index gets in sync, the m_synced
53+
/// flag is set and the BlockConnected ValidationInterface callback takes
54+
/// over and the sync thread exits.
55+
void ThreadSync();
56+
57+
/// Write the current chain block locator to the DB.
58+
bool WriteBestBlock(const CBlockIndex* block_index);
59+
60+
protected:
61+
void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex,
62+
const std::vector<CTransactionRef>& txn_conflicted) override;
63+
64+
void ChainStateFlushed(const CBlockLocator& locator) override;
65+
66+
/// Initialize internal state from the database and block index.
67+
virtual bool Init();
68+
69+
/// Write update index entries for a newly connected block.
70+
virtual bool WriteBlock(const CBlock& block, const CBlockIndex* pindex) { return true; }
71+
72+
virtual DB& GetDB() const = 0;
73+
74+
/// Get the name of the index for display in logs.
75+
virtual const char* GetName() const = 0;
76+
77+
public:
78+
/// Destructor interrupts sync thread if running and blocks until it exits.
79+
virtual ~BaseIndex();
80+
81+
/// Blocks the current thread until the index is caught up to the current
82+
/// state of the block chain. This only blocks if the index has gotten in
83+
/// sync once and only needs to process blocks in the ValidationInterface
84+
/// queue. If the index is catching up from far behind, this method does
85+
/// not block and immediately returns false.
86+
bool BlockUntilSyncedToCurrentChain();
87+
88+
void Interrupt();
89+
90+
/// Start initializes the sync state and registers the instance as a
91+
/// ValidationInterface so that it stays in sync with blockchain updates.
92+
void Start();
93+
94+
/// Stops the instance from staying in sync with blockchain updates.
95+
void Stop();
96+
};
97+
98+
#endif // BITCOIN_INDEX_BASE_H

0 commit comments

Comments
 (0)