db_key.h raw

   1  // Copyright (c) 2025-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_DB_KEY_H
   6  #define BITCOIN_INDEX_DB_KEY_H
   7  
   8  #include <dbwrapper.h>
   9  #include <interfaces/types.h>
  10  #include <serialize.h>
  11  #include <uint256.h>
  12  #include <util/log.h>
  13  
  14  #include <cstdint>
  15  #include <ios>
  16  #include <string>
  17  #include <utility>
  18  
  19  namespace index_util {
  20  /*
  21   * This file includes the logic for the db keys used by blockfilterindex and coinstatsindex.
  22   * Index data is usually indexed by height, but in case of a reorg, entries of blocks no
  23   * longer in the main chain will be copied to a hash index by which they can still be queried.
  24   * Keys for the height index have the type [DB_BLOCK_HEIGHT, uint32 (BE)]. The height is represented
  25   * as big-endian so that sequential reads of filters by height are fast.
  26   * Keys for the hash index have the type [DB_BLOCK_HASH, uint256].
  27   */
  28  
  29  static constexpr uint8_t DB_BLOCK_HASH{'s'};
  30  static constexpr uint8_t DB_BLOCK_HEIGHT{'t'};
  31  
  32  struct DBHeightKey {
  33      int height;
  34  
  35      explicit DBHeightKey(int height_in) : height(height_in) {}
  36  
  37      template<typename Stream>
  38      void Serialize(Stream& s) const
  39      {
  40          ser_writedata8(s, DB_BLOCK_HEIGHT);
  41          ser_writedata32be(s, height);
  42      }
  43  
  44      template<typename Stream>
  45      void Unserialize(Stream& s)
  46      {
  47          const uint8_t prefix{ser_readdata8(s)};
  48          if (prefix != DB_BLOCK_HEIGHT) {
  49              throw std::ios_base::failure("Invalid format for index DB height key");
  50          }
  51          height = ser_readdata32be(s);
  52      }
  53  };
  54  
  55  struct DBHashKey {
  56      uint256 hash;
  57  
  58      explicit DBHashKey(const uint256& hash_in) : hash(hash_in) {}
  59  
  60      SERIALIZE_METHODS(DBHashKey, obj) {
  61          uint8_t prefix{DB_BLOCK_HASH};
  62          READWRITE(prefix);
  63          if (prefix != DB_BLOCK_HASH) {
  64              throw std::ios_base::failure("Invalid format for index DB hash key");
  65          }
  66  
  67          READWRITE(obj.hash);
  68      }
  69  };
  70  
  71  template <typename DBVal>
  72  [[nodiscard]] static bool CopyHeightIndexToHashIndex(CDBIterator& db_it, CDBBatch& batch,
  73                                                       const std::string& index_name, int height)
  74  {
  75      DBHeightKey key(height);
  76      db_it.Seek(key);
  77  
  78      if (!db_it.GetKey(key) || key.height != height) {
  79          LogError("unexpected key in %s: expected (%c, %d)",
  80                    index_name, DB_BLOCK_HEIGHT, height);
  81          return false;
  82      }
  83  
  84      std::pair<uint256, DBVal> value;
  85      if (!db_it.GetValue(value)) {
  86          LogError("unable to read value in %s at key (%c, %d)",
  87                   index_name, DB_BLOCK_HEIGHT, height);
  88          return false;
  89      }
  90  
  91      batch.Write(DBHashKey(value.first), value.second);
  92      return true;
  93  }
  94  
  95  template <typename DBVal>
  96  static bool LookUpOne(const CDBWrapper& db, const interfaces::BlockRef& block, DBVal& result)
  97  {
  98      // First check if the result is stored under the height index and the value
  99      // there matches the block hash. This should be the case if the block is on
 100      // the active chain.
 101      std::pair<uint256, DBVal> read_out;
 102      if (!db.Read(DBHeightKey(block.height), read_out)) {
 103          return false;
 104      }
 105      if (read_out.first == block.hash) {
 106          result = std::move(read_out.second);
 107          return true;
 108      }
 109  
 110      // If value at the height index corresponds to an different block, the
 111      // result will be stored in the hash index.
 112      return db.Read(DBHashKey(block.hash), result);
 113  }
 114  } // namespace index_util
 115  
 116  #endif // BITCOIN_INDEX_DB_KEY_H
 117