blockstorage.h raw

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