blockstorage.h raw

   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