1 // Copyright (c) 2011-2022 The Limenka 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 LIMENKA_NODE_BLOCKSTORAGE_H
6 #define LIMENKA_NODE_BLOCKSTORAGE_H
7 8 #include <attributes.h>
9 #include <chain.h>
10 #include <dbwrapper.h>
11 #include <flatfile.h>
12 #include <kernel/blockmanager_opts.h>
13 #include <kernel/chainparams.h>
14 #include <kernel/cs_main.h>
15 #include <kernel/messagestartchars.h>
16 #include <primitives/block.h>
17 #include <streams.h>
18 #include <sync.h>
19 #include <uint256.h>
20 #include <util/fs.h>
21 #include <util/hasher.h>
22 23 #include <array>
24 #include <atomic>
25 #include <cstdint>
26 #include <functional>
27 #include <limits>
28 #include <map>
29 #include <memory>
30 #include <optional>
31 #include <set>
32 #include <span>
33 #include <string>
34 #include <unordered_map>
35 #include <utility>
36 #include <vector>
37 38 class BlockValidationState;
39 class CBlockUndo;
40 class Chainstate;
41 class ChainstateManager;
42 namespace Consensus {
43 struct Params;
44 }
45 namespace node {
46 struct PruneLockInfo;
47 };
48 namespace util {
49 class SignalInterrupt;
50 } // namespace util
51 52 namespace kernel {
53 /** Access to the block database (blocks/index/) */
54 class BlockTreeDB : public CDBWrapper
55 {
56 public:
57 using CDBWrapper::CDBWrapper;
58 bool WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*>>& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo, const std::unordered_map<std::string, node::PruneLockInfo>& prune_locks);
59 bool ReadBlockFileInfo(int nFile, CBlockFileInfo& info);
60 bool ReadLastBlockFile(int& nFile);
61 bool WriteReindexing(bool fReindexing);
62 void ReadReindexing(bool& fReindexing);
63 bool WritePruneLock(const std::string& name, const node::PruneLockInfo&);
64 bool DeletePruneLock(const std::string& name);
65 bool WriteFlag(const std::string& name, bool fValue);
66 bool ReadFlag(const std::string& name, bool& fValue);
67 bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex, const util::SignalInterrupt& interrupt)
68 EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
69 bool LoadPruneLocks(std::unordered_map<std::string, node::PruneLockInfo>& prune_locks, const util::SignalInterrupt& interrupt);
70 };
71 } // namespace kernel
72 73 namespace node {
74 using kernel::BlockTreeDB;
75 76 /** The pre-allocation chunk size for blk?????.dat files (since 0.8) */
77 static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB
78 /** The pre-allocation chunk size for rev?????.dat files (since 0.8) */
79 static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB
80 /** The maximum size of a blk?????.dat file (since 0.8) */
81 static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
82 83 /** Size of header written by WriteBlock before a serialized CBlock (8 bytes) */
84 static constexpr uint32_t BLOCK_SERIALIZATION_HEADER_SIZE{std::tuple_size_v<MessageStartChars> + sizeof(unsigned int)};
85 86 /** Total overhead when writing undo data: header (8 bytes) plus checksum (32 bytes) */
87 static constexpr uint32_t UNDO_DATA_DISK_OVERHEAD{BLOCK_SERIALIZATION_HEADER_SIZE + uint256::size()};
88 89 // Because validation code takes pointers to the map's CBlockIndex objects, if
90 // we ever switch to another associative container, we need to either use a
91 // container that has stable addressing (true of all std associative
92 // containers), or make the key a `std::unique_ptr<CBlockIndex>`
93 using BlockMap = std::unordered_map<uint256, CBlockIndex, BlockHasher>;
94 95 struct CBlockIndexWorkComparator {
96 bool operator()(const CBlockIndex* pa, const CBlockIndex* pb) const;
97 };
98 99 struct CBlockIndexHeightOnlyComparator {
100 /* Only compares the height of two block indices, doesn't try to tie-break */
101 bool operator()(const CBlockIndex* pa, const CBlockIndex* pb) const;
102 };
103 104 struct PruneLockInfo {
105 std::string desc; //! Arbitrary human-readable description of the lock purpose
106 uint64_t height_first{std::numeric_limits<uint64_t>::max()}; //! Height of earliest block that should be kept and not pruned
107 uint64_t height_last{std::numeric_limits<uint64_t>::max()}; //! Height of latest block that should be kept and not pruned
108 bool temporary{true};
109 110 SERIALIZE_METHODS(PruneLockInfo, obj)
111 {
112 READWRITE(obj.desc);
113 READWRITE(VARINT(obj.height_first));
114 READWRITE(VARINT(obj.height_last));
115 }
116 };
117 118 enum BlockfileType {
119 // Values used as array indexes - do not change carelessly.
120 NORMAL = 0,
121 ASSUMED = 1,
122 NUM_TYPES = 2,
123 };
124 125 std::ostream& operator<<(std::ostream& os, const BlockfileType& type);
126 127 struct BlockfileCursor {
128 // The latest blockfile number.
129 int file_num{0};
130 131 // Track the height of the highest block in file_num whose undo
132 // data has been written. Block data is written to block files in download
133 // order, but is written to undo files in validation order, which is
134 // usually in order by height. To avoid wasting disk space, undo files will
135 // be trimmed whenever the corresponding block file is finalized and
136 // the height of the highest block written to the block file equals the
137 // height of the highest block written to the undo file. This is a
138 // heuristic and can sometimes preemptively trim undo files that will write
139 // more data later, and sometimes fail to trim undo files that can't have
140 // more data written later.
141 int undo_height{0};
142 };
143 144 std::ostream& operator<<(std::ostream& os, const BlockfileCursor& cursor);
145 146 147 /**
148 * Maintains a tree of blocks (stored in `m_block_index`) which is consulted
149 * to determine where the most-work tip is.
150 *
151 * This data is used mostly in `Chainstate` - information about, e.g.,
152 * candidate tips is not maintained here.
153 */
154 class BlockManager
155 {
156 friend Chainstate;
157 friend ChainstateManager;
158 159 private:
160 const CChainParams& GetParams() const { return m_opts.chainparams; }
161 const Consensus::Params& GetConsensus() const { return m_opts.chainparams.GetConsensus(); }
162 /**
163 * Load the blocktree off disk and into memory. Populate certain metadata
164 * per index entry (nStatus, nChainWork, nTimeMax, etc.) as well as peripheral
165 * collections like m_dirty_blockindex.
166 */
167 bool LoadBlockIndex(const std::optional<uint256>& snapshot_blockhash)
168 EXCLUSIVE_LOCKS_REQUIRED(cs_main);
169 170 /** Return false if block file or undo file flushing fails. */
171 [[nodiscard]] bool FlushBlockFile(int blockfile_num, bool fFinalize, bool finalize_undo);
172 173 /** Return false if undo file flushing fails. */
174 [[nodiscard]] bool FlushUndoFile(int block_file, bool finalize = false);
175 176 /**
177 * Helper function performing various preparations before a block can be saved to disk:
178 * Returns the correct position for the block to be saved, which may be in the current or a new
179 * block file depending on nAddSize. May flush the previous blockfile to disk if full, updates
180 * blockfile info, and checks if there is enough disk space to save the block.
181 *
182 * The nAddSize argument passed to this function should include not just the size of the serialized CBlock, but also the size of
183 * separator fields (BLOCK_SERIALIZATION_HEADER_SIZE).
184 */
185 [[nodiscard]] FlatFilePos FindNextBlockPos(unsigned int nAddSize, unsigned int nHeight, uint64_t nTime);
186 [[nodiscard]] bool FlushChainstateBlockFile(int tip_height);
187 bool FindUndoPos(BlockValidationState& state, int nFile, FlatFilePos& pos, unsigned int nAddSize);
188 189 AutoFile OpenUndoFile(const FlatFilePos& pos, bool fReadOnly = false) const;
190 191 bool DoPruneLocksForbidPruning(const CBlockFileInfo& block_file_info) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
192 193 /* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */
194 void FindFilesToPruneManual(
195 std::set<int>& setFilesToPrune,
196 int nManualPruneHeight,
197 const Chainstate& chain,
198 ChainstateManager& chainman);
199 200 /**
201 * Prune block and undo files (blk???.dat and rev???.dat) so that the disk space used is less than a user-defined target.
202 * The user sets the target (in MB) on the command line or in config file. This will be run on startup and whenever new
203 * space is allocated in a block or undo file, staying below the target. Changing back to unpruned requires a reindex
204 * (which in this case means the blockchain must be re-downloaded.)
205 *
206 * Pruning functions are called from FlushStateToDisk when the m_check_for_pruning flag has been set.
207 * Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.)
208 * Pruning cannot take place until the longest chain is at least a certain length (CChainParams::nPruneAfterHeight).
209 * Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip.
210 * The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files.
211 * A db flag records the fact that at least some block files have been pruned.
212 *
213 * @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned
214 * @param last_prune The last height we're able to prune, according to the prune locks
215 */
216 void FindFilesToPrune(
217 std::set<int>& setFilesToPrune,
218 int last_prune,
219 const Chainstate& chain,
220 ChainstateManager& chainman);
221 222 RecursiveMutex cs_LastBlockFile;
223 std::vector<CBlockFileInfo> m_blockfile_info;
224 225 //! Since assumedvalid chainstates may be syncing a range of the chain that is very
226 //! far away from the normal/background validation process, we should segment blockfiles
227 //! for assumed chainstates. Otherwise, we might have wildly different height ranges
228 //! mixed into the same block files, which would impair our ability to prune
229 //! effectively.
230 //!
231 //! This data structure maintains separate blockfile number cursors for each
232 //! BlockfileType. The ASSUMED state is initialized, when necessary, in FindNextBlockPos().
233 //!
234 //! The first element is the NORMAL cursor, second is ASSUMED.
235 std::array<std::optional<BlockfileCursor>, BlockfileType::NUM_TYPES>
236 m_blockfile_cursors GUARDED_BY(cs_LastBlockFile) = {
237 BlockfileCursor{},
238 std::nullopt,
239 };
240 int MaxBlockfileNum() const EXCLUSIVE_LOCKS_REQUIRED(cs_LastBlockFile)
241 {
242 static const BlockfileCursor empty_cursor;
243 const auto& normal = m_blockfile_cursors[BlockfileType::NORMAL].value_or(empty_cursor);
244 const auto& assumed = m_blockfile_cursors[BlockfileType::ASSUMED].value_or(empty_cursor);
245 return std::max(normal.file_num, assumed.file_num);
246 }
247 248 /** Global flag to indicate we should check to see if there are
249 * block/undo files that should be deleted. Set on startup
250 * or if we allocate more file space when we're in prune mode
251 */
252 bool m_check_for_pruning = false;
253 254 const bool m_prune_mode;
255 256 const Obfuscation m_xor_key;
257 258 /** Dirty block index entries. */
259 std::set<CBlockIndex*> m_dirty_blockindex;
260 261 /** Dirty block file entries. */
262 std::set<int> m_dirty_fileinfo;
263 264 public:
265 /**
266 * Map from external index name to oldest block that must not be pruned.
267 *
268 * @note Internally, only blocks before height (height_first - PRUNE_LOCK_BUFFER - 1) and
269 * after height (height_last + PRUNE_LOCK_BUFFER) will be pruned, but callers should
270 * avoid assuming any particular buffer size.
271 */
272 std::unordered_map<std::string, PruneLockInfo> m_prune_locks GUARDED_BY(::cs_main);
273 274 private:
275 BlockfileType BlockfileTypeForHeight(int height);
276 277 const kernel::BlockManagerOpts m_opts;
278 279 const FlatFileSeq m_block_file_seq;
280 const FlatFileSeq m_undo_file_seq;
281 282 public:
283 using Options = kernel::BlockManagerOpts;
284 285 explicit BlockManager(const util::SignalInterrupt& interrupt, Options opts);
286 287 const util::SignalInterrupt& m_interrupt;
288 std::atomic<bool> m_importing{false};
289 290 /**
291 * Whether all blockfiles have been added to the block tree database.
292 * Normally true, but set to false when a reindex is requested and the
293 * database is wiped. The value is persisted in the database across restarts
294 * and will be false until reindexing completes.
295 */
296 std::atomic_bool m_blockfiles_indexed{true};
297 298 BlockMap m_block_index GUARDED_BY(cs_main);
299 300 /**
301 * The height of the base block of an assumeutxo snapshot, if one is in use.
302 *
303 * This controls how blockfiles are segmented by chainstate type to avoid
304 * comingling different height regions of the chain when an assumedvalid chainstate
305 * is in use. If heights are drastically different in the same blockfile, pruning
306 * suffers.
307 *
308 * This is set during ActivateSnapshot() or upon LoadBlockIndex() if a snapshot
309 * had been previously loaded. After the snapshot is validated, this is unset to
310 * restore normal LoadBlockIndex behavior.
311 */
312 std::optional<int> m_snapshot_height;
313 314 std::vector<CBlockIndex*> GetAllBlockIndices() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
315 316 /**
317 * All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions.
318 * Pruned nodes may have entries where B is missing data.
319 */
320 std::multimap<CBlockIndex*, CBlockIndex*> m_blocks_unlinked;
321 322 std::unique_ptr<BlockTreeDB> m_block_tree_db GUARDED_BY(::cs_main);
323 324 bool WriteBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
325 bool LoadBlockIndexDB(const std::optional<uint256>& snapshot_blockhash)
326 EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
327 328 /**
329 * Remove any pruned block & undo files that are still on disk.
330 * This could happen on some systems if the file was still being read while unlinked,
331 * or if we crash before unlinking.
332 */
333 void ScanAndUnlinkAlreadyPrunedFiles() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
334 335 CBlockIndex* AddToBlockIndex(const CBlockHeader& block, CBlockIndex*& best_header) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
336 /** Create a new block index entry for a given block hash */
337 CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
338 339 //! Mark one block file as pruned (modify associated database entries)
340 void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
341 342 CBlockIndex* LookupBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
343 const CBlockIndex* LookupBlockIndex(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
344 345 /** Get block file info entry for one block file */
346 CBlockFileInfo* GetBlockFileInfo(size_t n);
347 348 bool WriteBlockUndo(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex& block)
349 EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
350 351 /** Store block on disk and update block file statistics.
352 *
353 * @param[in] block the block to be stored
354 * @param[in] nHeight the height of the block
355 *
356 * @returns in case of success, the position to which the block was written to
357 * in case of an error, an empty FlatFilePos
358 */
359 FlatFilePos WriteBlock(const CBlock& block, int nHeight);
360 361 /** Update blockfile info while processing a block during reindex. The block must be available on disk.
362 *
363 * @param[in] block the block being processed
364 * @param[in] nHeight the height of the block
365 * @param[in] pos the position of the serialized CBlock on disk
366 */
367 void UpdateBlockInfo(const CBlock& block, unsigned int nHeight, const FlatFilePos& pos);
368 369 /** Whether running in -prune mode. */
370 [[nodiscard]] bool IsPruneMode() const { return m_prune_mode; }
371 372 /** Attempt to stay below this number of bytes of block files. */
373 [[nodiscard]] uint64_t GetPruneTarget() const { return m_opts.prune_target; }
374 [[nodiscard]] uint64_t GetPruneTargetForChainstate(const Chainstate& chain, ChainstateManager& chainman) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
375 static constexpr auto PRUNE_TARGET_MANUAL{std::numeric_limits<uint64_t>::max()};
376 377 [[nodiscard]] bool LoadingBlocks() const { return m_importing || !m_blockfiles_indexed; }
378 379 /** Calculate the amount of disk space the block & undo files currently use */
380 uint64_t CalculateCurrentUsage();
381 382 //! Returns last CBlockIndex* that is a checkpoint
383 const CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
384 385 //! Check if all blocks in the [upper_block, lower_block] range have data available.
386 //! The caller is responsible for ensuring that lower_block is an ancestor of upper_block
387 //! (part of the same chain).
388 bool CheckBlockDataAvailability(const CBlockIndex& upper_block LIFETIMEBOUND, const CBlockIndex& lower_block LIFETIMEBOUND) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
389 390 /**
391 * @brief Returns the earliest block with specified `status_mask` flags set after
392 * the latest block _not_ having those flags.
393 *
394 * This function starts from `upper_block`, which must have all
395 * `status_mask` flags set, and iterates backwards through its ancestors. It
396 * continues as long as each block has all `status_mask` flags set, until
397 * reaching the oldest ancestor or `lower_block`.
398 *
399 * @pre `upper_block` must have all `status_mask` flags set.
400 * @pre `lower_block` must be null or an ancestor of `upper_block`
401 *
402 * @param upper_block The starting block for the search, which must have all
403 * `status_mask` flags set.
404 * @param status_mask Bitmask specifying required status flags.
405 * @param lower_block The earliest possible block to return. If null, the
406 * search can extend to the genesis block.
407 *
408 * @return A non-null pointer to the earliest block between `upper_block`
409 * and `lower_block`, inclusive, such that every block between the
410 * returned block and `upper_block` has `status_mask` flags set.
411 */
412 const CBlockIndex* GetFirstBlock(
413 const CBlockIndex& upper_block LIFETIMEBOUND,
414 uint32_t status_mask,
415 const CBlockIndex* lower_block = nullptr
416 ) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
417 418 /** True if any block files have ever been pruned. */
419 bool m_have_pruned = false;
420 421 //! Check whether the block associated with this index entry is pruned or not.
422 bool IsBlockPruned(const CBlockIndex& block) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
423 424 bool PruneLockExists(const std::string& name) const SHARED_LOCKS_REQUIRED(::cs_main);
425 //! Create or update a prune lock identified by its name
426 bool UpdatePruneLock(const std::string& name, const PruneLockInfo& lock_info, bool sync=false) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
427 bool DeletePruneLock(const std::string& name) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
428 429 /** Open a block file (blk?????.dat) */
430 AutoFile OpenBlockFile(const FlatFilePos& pos, bool fReadOnly) const;
431 432 /** Translation to a filesystem path */
433 fs::path GetBlockPosFilename(const FlatFilePos& pos) const;
434 435 /**
436 * Actually unlink the specified files
437 */
438 void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune) const;
439 440 /** Functions for disk access for blocks */
441 bool ReadBlock(CBlock& block, const FlatFilePos& pos, const std::optional<uint256>& expected_hash = {}, bool lowprio = false) const;
442 bool ReadBlock(CBlock& block, const CBlockIndex& index, bool lowprio = false) const;
443 bool ReadRawBlock(std::vector<uint8_t>& block, const FlatFilePos& pos, bool lowprio = false) const;
444 445 bool ReadBlockUndo(CBlockUndo& blockundo, const CBlockIndex& index) const;
446 447 void CleanupBlockRevFiles() const;
448 };
449 450 // Calls ActivateBestChain() even if no blocks are imported.
451 void ImportBlocks(ChainstateManager& chainman, std::span<const fs::path> import_paths);
452 } // namespace node
453 454 #endif // LIMENKA_NODE_BLOCKSTORAGE_H
455