/tmp/bitcoin/src/index/db_key.h
Line | Count | Source |
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 | 13.9k | explicit DBHeightKey(int height_in) : height(height_in) {} |
36 | | |
37 | | template<typename Stream> |
38 | | void Serialize(Stream& s) const |
39 | 13.5k | { |
40 | 13.5k | ser_writedata8(s, DB_BLOCK_HEIGHT); |
41 | 13.5k | ser_writedata32be(s, height); |
42 | 13.5k | } |
43 | | |
44 | | template<typename Stream> |
45 | | void Unserialize(Stream& s) |
46 | 3.09k | { |
47 | 3.09k | const uint8_t prefix{ser_readdata8(s)}; |
48 | 3.09k | if (prefix != DB_BLOCK_HEIGHT) { |
49 | 0 | throw std::ios_base::failure("Invalid format for index DB height key"); |
50 | 0 | } |
51 | 3.09k | height = ser_readdata32be(s); |
52 | 3.09k | } |
53 | | }; |
54 | | |
55 | | struct DBHashKey { |
56 | | uint256 hash; |
57 | | |
58 | 263 | explicit DBHashKey(const uint256& hash_in) : hash(hash_in) {} |
59 | | |
60 | 263 | SERIALIZE_METHODS(DBHashKey, obj) { |
61 | 263 | uint8_t prefix{DB_BLOCK_HASH}; |
62 | 263 | READWRITE(prefix); |
63 | 263 | if (prefix != DB_BLOCK_HASH) { |
64 | 0 | throw std::ios_base::failure("Invalid format for index DB hash key"); |
65 | 0 | } |
66 | | |
67 | 263 | READWRITE(obj.hash); |
68 | 263 | } |
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 | 232 | { |
75 | 232 | DBHeightKey key(height); |
76 | 232 | db_it.Seek(key); |
77 | | |
78 | 232 | if (!db_it.GetKey(key) || key.height != height) { |
79 | 0 | LogError("unexpected key in %s: expected (%c, %d)", |
80 | 0 | index_name, DB_BLOCK_HEIGHT, height); |
81 | 0 | return false; |
82 | 0 | } |
83 | | |
84 | 232 | std::pair<uint256, DBVal> value; |
85 | 232 | if (!db_it.GetValue(value)) { |
86 | 0 | LogError("unable to read value in %s at key (%c, %d)", |
87 | 0 | index_name, DB_BLOCK_HEIGHT, height); |
88 | 0 | return false; |
89 | 0 | } |
90 | | |
91 | 232 | batch.Write(DBHashKey(value.first), value.second); |
92 | 232 | return true; |
93 | 232 | } blockfilterindex.cpp:bool index_util::CopyHeightIndexToHashIndex<(anonymous namespace)::DBVal>(CDBIterator&, CDBBatch&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, int) Line | Count | Source | 74 | 111 | { | 75 | 111 | DBHeightKey key(height); | 76 | 111 | db_it.Seek(key); | 77 | | | 78 | 111 | if (!db_it.GetKey(key) || key.height != height) { | 79 | 0 | LogError("unexpected key in %s: expected (%c, %d)", | 80 | 0 | index_name, DB_BLOCK_HEIGHT, height); | 81 | 0 | return false; | 82 | 0 | } | 83 | | | 84 | 111 | std::pair<uint256, DBVal> value; | 85 | 111 | if (!db_it.GetValue(value)) { | 86 | 0 | LogError("unable to read value in %s at key (%c, %d)", | 87 | 0 | index_name, DB_BLOCK_HEIGHT, height); | 88 | 0 | return false; | 89 | 0 | } | 90 | | | 91 | 111 | batch.Write(DBHashKey(value.first), value.second); | 92 | 111 | return true; | 93 | 111 | } |
coinstatsindex.cpp:bool index_util::CopyHeightIndexToHashIndex<(anonymous namespace)::DBVal>(CDBIterator&, CDBBatch&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, int) Line | Count | Source | 74 | 121 | { | 75 | 121 | DBHeightKey key(height); | 76 | 121 | db_it.Seek(key); | 77 | | | 78 | 121 | if (!db_it.GetKey(key) || key.height != height) { | 79 | 0 | LogError("unexpected key in %s: expected (%c, %d)", | 80 | 0 | index_name, DB_BLOCK_HEIGHT, height); | 81 | 0 | return false; | 82 | 0 | } | 83 | | | 84 | 121 | std::pair<uint256, DBVal> value; | 85 | 121 | if (!db_it.GetValue(value)) { | 86 | 0 | LogError("unable to read value in %s at key (%c, %d)", | 87 | 0 | index_name, DB_BLOCK_HEIGHT, height); | 88 | 0 | return false; | 89 | 0 | } | 90 | | | 91 | 121 | batch.Write(DBHashKey(value.first), value.second); | 92 | 121 | return true; | 93 | 121 | } |
|
94 | | |
95 | | template <typename DBVal> |
96 | | static bool LookUpOne(const CDBWrapper& db, const interfaces::BlockRef& block, DBVal& result) |
97 | 1.17k | { |
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 | 1.17k | std::pair<uint256, DBVal> read_out; |
102 | 1.17k | if (!db.Read(DBHeightKey(block.height), read_out)) { |
103 | 203 | return false; |
104 | 203 | } |
105 | 972 | if (read_out.first == block.hash) { |
106 | 953 | result = std::move(read_out.second); |
107 | 953 | return true; |
108 | 953 | } |
109 | | |
110 | | // If value at the height index corresponds to an different block, the |
111 | | // result will be stored in the hash index. |
112 | 19 | return db.Read(DBHashKey(block.hash), result); |
113 | 972 | } blockfilterindex.cpp:bool index_util::LookUpOne<(anonymous namespace)::DBVal>(CDBWrapper const&, interfaces::BlockRef const&, (anonymous namespace)::DBVal&) Line | Count | Source | 97 | 1.07k | { | 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 | 1.07k | std::pair<uint256, DBVal> read_out; | 102 | 1.07k | if (!db.Read(DBHeightKey(block.height), read_out)) { | 103 | 202 | return false; | 104 | 202 | } | 105 | 875 | if (read_out.first == block.hash) { | 106 | 858 | result = std::move(read_out.second); | 107 | 858 | return true; | 108 | 858 | } | 109 | | | 110 | | // If value at the height index corresponds to an different block, the | 111 | | // result will be stored in the hash index. | 112 | 17 | return db.Read(DBHashKey(block.hash), result); | 113 | 875 | } |
coinstatsindex.cpp:bool index_util::LookUpOne<(anonymous namespace)::DBVal>(CDBWrapper const&, interfaces::BlockRef const&, (anonymous namespace)::DBVal&) Line | Count | Source | 97 | 98 | { | 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 | 98 | std::pair<uint256, DBVal> read_out; | 102 | 98 | if (!db.Read(DBHeightKey(block.height), read_out)) { | 103 | 1 | return false; | 104 | 1 | } | 105 | 97 | if (read_out.first == block.hash) { | 106 | 95 | result = std::move(read_out.second); | 107 | 95 | return true; | 108 | 95 | } | 109 | | | 110 | | // If value at the height index corresponds to an different block, the | 111 | | // result will be stored in the hash index. | 112 | 2 | return db.Read(DBHashKey(block.hash), result); | 113 | 97 | } |
|
114 | | } // namespace index_util |
115 | | |
116 | | #endif // BITCOIN_INDEX_DB_KEY_H |