db_key.h
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 <logging.h> 11 #include <serialize.h> 12 #include <uint256.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