Coverage Report

Created: 2026-09-14 20:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 <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
inline constexpr uint8_t DB_BLOCK_HASH{'s'};
30
inline constexpr uint8_t DB_BLOCK_HEIGHT{'t'};
31
32
struct DBHeightKey {
33
    int height;
34
35
14.7k
    explicit DBHeightKey(int height_in) : height(height_in) {}
36
37
    template<typename Stream>
38
    void Serialize(Stream& s) const
39
14.3k
    {
40
14.3k
        ser_writedata8(s, DB_BLOCK_HEIGHT);
41
14.3k
        ser_writedata32be(s, height);
42
14.3k
    }
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
268
    explicit DBHashKey(const uint256& hash_in) : hash(hash_in) {}
59
60
268
    SERIALIZE_METHODS(DBHashKey, obj) {
61
268
        uint8_t prefix{DB_BLOCK_HASH};
62
268
        READWRITE(prefix);
63
268
        if (prefix != DB_BLOCK_HASH) {
64
0
            throw std::ios_base::failure("Invalid format for index DB hash key");
65
0
        }
66
67
268
        READWRITE(obj.hash);
68
268
    }
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
233
{
75
233
    DBHeightKey key(height);
76
233
    db_it.Seek(key);
77
78
233
    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
233
    std::pair<uint256, DBVal> value;
85
233
    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
233
    batch.Write(DBHashKey(value.first), value.second);
92
233
    return true;
93
233
}
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
112
{
75
112
    DBHeightKey key(height);
76
112
    db_it.Seek(key);
77
78
112
    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
112
    std::pair<uint256, DBVal> value;
85
112
    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
112
    batch.Write(DBHashKey(value.first), value.second);
92
112
    return true;
93
112
}
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.29k
{
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.29k
    std::pair<uint256, DBVal> read_out;
102
1.29k
    if (!db.Read(DBHeightKey(block.height), read_out)) {
103
305
        return false;
104
305
    }
105
985
    if (read_out.first == block.hash) {
106
962
        result = std::move(read_out.second);
107
962
        return true;
108
962
    }
109
110
    // If value at the height index corresponds to an different block, the
111
    // result will be stored in the hash index.
112
23
    return db.Read(DBHashKey(block.hash), result);
113
985
}
blockfilterindex.cpp:bool index_util::LookUpOne<(anonymous namespace)::DBVal>(CDBWrapper const&, interfaces::BlockRef const&, (anonymous namespace)::DBVal&)
Line
Count
Source
97
1.19k
{
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.19k
    std::pair<uint256, DBVal> read_out;
102
1.19k
    if (!db.Read(DBHeightKey(block.height), read_out)) {
103
304
        return false;
104
304
    }
105
886
    if (read_out.first == block.hash) {
106
865
        result = std::move(read_out.second);
107
865
        return true;
108
865
    }
109
110
    // If value at the height index corresponds to an different block, the
111
    // result will be stored in the hash index.
112
21
    return db.Read(DBHashKey(block.hash), result);
113
886
}
coinstatsindex.cpp:bool index_util::LookUpOne<(anonymous namespace)::DBVal>(CDBWrapper const&, interfaces::BlockRef const&, (anonymous namespace)::DBVal&)
Line
Count
Source
97
100
{
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
100
    std::pair<uint256, DBVal> read_out;
102
100
    if (!db.Read(DBHeightKey(block.height), read_out)) {
103
1
        return false;
104
1
    }
105
99
    if (read_out.first == block.hash) {
106
97
        result = std::move(read_out.second);
107
97
        return true;
108
97
    }
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
99
}
114
} // namespace index_util
115
116
#endif // BITCOIN_INDEX_DB_KEY_H