/Users/brunogarcia/projects/bitcoin-core-dev/src/index/base.h
Line | Count | Source |
1 | | // Copyright (c) 2017-present 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 <interfaces/chain.h> |
10 | | #include <interfaces/types.h> |
11 | | #include <util/string.h> |
12 | | #include <util/threadinterrupt.h> |
13 | | #include <validationinterface.h> |
14 | | |
15 | | #include <any> |
16 | | #include <string> |
17 | | |
18 | | class CBlock; |
19 | | class CBlockIndex; |
20 | | class Chainstate; |
21 | | class ChainstateManager; |
22 | | class ThreadPool; |
23 | | namespace interfaces { |
24 | | class Chain; |
25 | | } // namespace interfaces |
26 | | |
27 | | /** Maximum number of threads a single thread pool instance can have */ |
28 | | static constexpr int16_t MAX_INDEX_WORKERS_COUNT = 100; |
29 | | /** Number of concurrent jobs during the initial sync process */ |
30 | | static constexpr int16_t INDEX_WORKERS_COUNT = 0; |
31 | | /** Number of tasks processed by each worker */ |
32 | | static constexpr int16_t INDEX_WORK_PER_CHUNK = 1000; |
33 | | |
34 | | struct IndexSummary { |
35 | | std::string name; |
36 | | bool synced{false}; |
37 | | int best_block_height{0}; |
38 | | uint256 best_block_hash; |
39 | | }; |
40 | | |
41 | | /** |
42 | | * Base class for indices of blockchain data. This implements |
43 | | * CValidationInterface and ensures blocks are indexed sequentially according |
44 | | * to their position in the active chain. |
45 | | * |
46 | | * In the presence of multiple chainstates (i.e. if a UTXO snapshot is loaded), |
47 | | * only the background "IBD" chainstate will be indexed to avoid building the |
48 | | * index out of order. When the background chainstate completes validation, the |
49 | | * index will be reinitialized and indexing will continue. |
50 | | */ |
51 | | class BaseIndex : public CValidationInterface |
52 | | { |
53 | | protected: |
54 | | /** |
55 | | * The database stores a block locator of the chain the database is synced to |
56 | | * so that the index can efficiently determine the point it last stopped at. |
57 | | * A locator is used instead of a simple hash of the chain tip because blocks |
58 | | * and block index entries may not be flushed to disk until after this database |
59 | | * is updated. |
60 | | */ |
61 | | class DB : public CDBWrapper |
62 | | { |
63 | | public: |
64 | | DB(const fs::path& path, size_t n_cache_size, |
65 | | bool f_memory = false, bool f_wipe = false, bool f_obfuscate = false); |
66 | | |
67 | | /// Read block locator of the chain that the index is in sync with. |
68 | | bool ReadBestBlock(CBlockLocator& locator) const; |
69 | | |
70 | | /// Write block locator of the chain that the index is in sync with. |
71 | | void WriteBestBlock(CDBBatch& batch, const CBlockLocator& locator); |
72 | | }; |
73 | | |
74 | | private: |
75 | | /// Whether the index has been initialized or not. |
76 | | std::atomic<bool> m_init{false}; |
77 | | /// Whether the index is in sync with the main chain. The flag is flipped |
78 | | /// from false to true once, after which point this starts processing |
79 | | /// ValidationInterface notifications to stay in sync. |
80 | | /// |
81 | | /// Note that this will latch to true *immediately* upon startup if |
82 | | /// `m_chainstate->m_chain` is empty, which will be the case upon startup |
83 | | /// with an empty datadir if, e.g., `-txindex=1` is specified. |
84 | | std::atomic<bool> m_synced{false}; |
85 | | |
86 | | /// The last block in the chain that the index is in sync with. |
87 | | std::atomic<const CBlockIndex*> m_best_block_index{nullptr}; |
88 | | |
89 | | std::thread m_thread_sync; |
90 | | CThreadInterrupt m_interrupt; |
91 | | |
92 | | ThreadPool* m_thread_pool{nullptr}; |
93 | | int m_blocks_per_worker{INDEX_WORK_PER_CHUNK}; |
94 | | |
95 | | /// Write the current index state (eg. chain block locator and subclass-specific items) to disk. |
96 | | /// |
97 | | /// Recommendations for error handling: |
98 | | /// If called on a successor of the previous committed best block in the index, the index can |
99 | | /// continue processing without risk of corruption, though the index state will need to catch up |
100 | | /// from further behind on reboot. If the new state is not a successor of the previous state (due |
101 | | /// to a chain reorganization), the index must halt until Commit succeeds or else it could end up |
102 | | /// getting corrupted. |
103 | | bool Commit(); |
104 | | |
105 | | /// Loop over disconnected blocks and call CustomRemove. |
106 | | bool Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip); |
107 | | |
108 | | std::any ProcessBlock(const CBlockIndex* pindex, const CBlock* block_data = nullptr); |
109 | | std::vector<std::any> ProcessBlocks(bool process_in_order, const CBlockIndex* start, const CBlockIndex* end); |
110 | | |
111 | | virtual bool AllowPrune() const = 0; |
112 | | |
113 | | template <typename... Args> |
114 | | void FatalErrorf(util::ConstevalFormatString<sizeof...(Args)> fmt, const Args&... args); |
115 | | |
116 | | protected: |
117 | | std::unique_ptr<interfaces::Chain> m_chain; |
118 | | Chainstate* m_chainstate{nullptr}; |
119 | | const std::string m_name; |
120 | | |
121 | | void BlockConnected(ChainstateRole role, const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex) override; |
122 | | |
123 | | void ChainStateFlushed(ChainstateRole role, const CBlockLocator& locator) override; |
124 | | |
125 | | /// Return custom notification options for index. |
126 | 0 | [[nodiscard]] virtual interfaces::Chain::NotifyOptions CustomOptions() { return {}; } |
127 | | |
128 | | /// Initialize internal state from the database and block index. |
129 | 0 | [[nodiscard]] virtual bool CustomInit(const std::optional<interfaces::BlockRef>& block) { return true; } |
130 | | |
131 | | /// Write update index entries for a newly connected block. |
132 | 0 | [[nodiscard]] virtual bool CustomAppend(const interfaces::BlockInfo& block) { return true; } |
133 | | |
134 | | /// Virtual method called internally by Commit that can be overridden to atomically |
135 | | /// commit more index state. |
136 | 0 | virtual bool CustomCommit(CDBBatch& batch) { return true; } |
137 | | |
138 | | /// Rewind index by one block during a chain reorg. |
139 | 0 | [[nodiscard]] virtual bool CustomRemove(const interfaces::BlockInfo& block) { return true; } |
140 | | |
141 | | virtual DB& GetDB() const = 0; |
142 | | |
143 | | /// Update the internal best block index as well as the prune lock. |
144 | | void SetBestBlockIndex(const CBlockIndex* block); |
145 | | |
146 | | /// If 'AllowParallelSync()' returns true, 'ProcessBlock()' will run concurrently in batches. |
147 | | /// The 'std::any' result will be passed to 'CustomPostProcessBlocks()' so the index can process |
148 | | /// async result batches in a synchronous fashion (if required). |
149 | 0 | [[nodiscard]] virtual std::any CustomProcessBlock(const interfaces::BlockInfo& block_info) { |
150 | | // If parallel sync is enabled, the child class must implement this method. |
151 | 0 | if (AllowParallelSync()) return std::any(); |
152 | | |
153 | | // Default, synchronous write |
154 | 0 | if (!CustomAppend(block_info)) { |
155 | 0 | throw std::runtime_error(strprintf("%s: Failed to write block %s to index database",Line | Count | Source | 1172 | 0 | #define strprintf tfm::format |
|
156 | 0 | __func__, block_info.hash.ToString())); |
157 | 0 | } |
158 | 0 | return true; |
159 | 0 | } |
160 | | |
161 | | /// 'CustomPostProcessBlocks()' is called in a synchronous manner after a batch of async 'ProcessBlock()' |
162 | | /// calls have completed. |
163 | | /// Here the index usually links and dump information that cannot be processed in an asynchronous fashion. |
164 | 0 | [[nodiscard]] virtual bool CustomPostProcessBlocks(const std::any& obj) { return true; }; |
165 | | |
166 | | public: |
167 | | BaseIndex(std::unique_ptr<interfaces::Chain> chain, std::string name); |
168 | | /// Destructor interrupts sync thread if running and blocks until it exits. |
169 | | virtual ~BaseIndex(); |
170 | | |
171 | | /// Get the name of the index for display in logs. |
172 | 0 | const std::string& GetName() const LIFETIMEBOUND { return m_name; } |
173 | | |
174 | 0 | void SetThreadPool(ThreadPool& thread_pool) { m_thread_pool = &thread_pool; } |
175 | | |
176 | | /// Blocks the current thread until the index is caught up to the current |
177 | | /// state of the block chain. This only blocks if the index has gotten in |
178 | | /// sync once and only needs to process blocks in the ValidationInterface |
179 | | /// queue. If the index is catching up from far behind, this method does |
180 | | /// not block and immediately returns false. |
181 | | bool BlockUntilSyncedToCurrentChain() const LOCKS_EXCLUDED(::cs_main); |
182 | | |
183 | | void Interrupt(); |
184 | | |
185 | | /// Initializes the sync state and registers the instance to the |
186 | | /// validation interface so that it stays in sync with blockchain updates. |
187 | | [[nodiscard]] bool Init(); |
188 | | |
189 | | /// Starts the initial sync process on a background thread. |
190 | | [[nodiscard]] bool StartBackgroundSync(); |
191 | | |
192 | | /// Sync the index with the block index starting from the current best block. |
193 | | /// Intended to be run in its own thread, m_thread_sync, and can be |
194 | | /// interrupted with m_interrupt. Once the index gets in sync, the m_synced |
195 | | /// flag is set and the BlockConnected ValidationInterface callback takes |
196 | | /// over and the sync thread exits. |
197 | | void Sync(); |
198 | | |
199 | | /// Stops the instance from staying in sync with blockchain updates. |
200 | | void Stop(); |
201 | | |
202 | | /// Number of blocks each worker thread will process at a time |
203 | 0 | void SetBlocksPerWorker(int count) { m_blocks_per_worker = count; } |
204 | | |
205 | | /// True if the child class allows concurrent sync. |
206 | 0 | virtual bool AllowParallelSync() { return false; } |
207 | | |
208 | | /// Get a summary of the index and its state. |
209 | | IndexSummary GetSummary() const; |
210 | | }; |
211 | | |
212 | | #endif // BITCOIN_INDEX_BASE_H |