fuzz coverage

Coverage Report

Created: 2025-06-01 19:34

/Users/eugenesiegel/btc/bitcoin/src/txdb.cpp
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) 2009-2010 Satoshi Nakamoto
2
// Copyright (c) 2009-2022 The Bitcoin Core developers
3
// Distributed under the MIT software license, see the accompanying
4
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6
#include <txdb.h>
7
8
#include <coins.h>
9
#include <dbwrapper.h>
10
#include <logging.h>
11
#include <primitives/transaction.h>
12
#include <random.h>
13
#include <serialize.h>
14
#include <uint256.h>
15
#include <util/vector.h>
16
17
#include <cassert>
18
#include <cstdlib>
19
#include <iterator>
20
#include <utility>
21
22
static constexpr uint8_t DB_COIN{'C'};
23
static constexpr uint8_t DB_BEST_BLOCK{'B'};
24
static constexpr uint8_t DB_HEAD_BLOCKS{'H'};
25
// Keys used in previous version that might still be found in the DB:
26
static constexpr uint8_t DB_COINS{'c'};
27
28
bool CCoinsViewDB::NeedsUpgrade()
29
49.9k
{
30
49.9k
    std::unique_ptr<CDBIterator> cursor{m_db->NewIterator()};
31
    // DB_COINS was deprecated in v0.15.0, commit
32
    // 1088b02f0ccd7358d2b7076bb9e122d59d502d02
33
49.9k
    cursor->Seek(std::make_pair(DB_COINS, uint256{}));
34
49.9k
    return cursor->Valid();
35
49.9k
}
36
37
namespace {
38
39
struct CoinEntry {
40
    COutPoint* outpoint;
41
    uint8_t key;
42
40.0M
    explicit CoinEntry(const COutPoint* ptr) : outpoint(const_cast<COutPoint*>(ptr)), key(DB_COIN)  {}
43
44
40.0M
    SERIALIZE_METHODS(CoinEntry, obj) { READWRITE(obj.key, obj.outpoint->hash, VARINT(obj.outpoint->n)); }
Line
Count
Source
156
40.0M
#define READWRITE(...) (ser_action.SerReadWriteMany(s, __VA_ARGS__))
    SERIALIZE_METHODS(CoinEntry, obj) { READWRITE(obj.key, obj.outpoint->hash, VARINT(obj.outpoint->n)); }
Line
Count
Source
156
0
#define READWRITE(...) (ser_action.SerReadWriteMany(s, __VA_ARGS__))
txdb.cpp:_ZN12_GLOBAL__N_19CoinEntry16SerializationOpsI10DataStreamKS0_15ActionSerializeEEvRT0_RT_T1_
Line
Count
Source
44
40.0M
    SERIALIZE_METHODS(CoinEntry, obj) { READWRITE(obj.key, obj.outpoint->hash, VARINT(obj.outpoint->n)); }
Line
Count
Source
156
40.0M
#define READWRITE(...) (ser_action.SerReadWriteMany(s, __VA_ARGS__))
Unexecuted instantiation: txdb.cpp:_ZN12_GLOBAL__N_19CoinEntry16SerializationOpsI10DataStreamS0_17ActionUnserializeEEvRT0_RT_T1_
45
};
46
47
} // namespace
48
49
CCoinsViewDB::CCoinsViewDB(DBParams db_params, CoinsViewOptions options) :
50
49.9k
    m_db_params{std::move(db_params)},
51
49.9k
    m_options{std::move(options)},
52
49.9k
    m_db{std::make_unique<CDBWrapper>(m_db_params)} { }
53
54
void CCoinsViewDB::ResizeCache(size_t new_cache_size)
55
0
{
56
    // We can't do this operation with an in-memory DB since we'll lose all the coins upon
57
    // reset.
58
0
    if (!m_db_params.memory_only) {
59
        // Have to do a reset first to get the original `m_db` state to release its
60
        // filesystem lock.
61
0
        m_db.reset();
62
0
        m_db_params.cache_bytes = new_cache_size;
63
0
        m_db_params.wipe_data = false;
64
0
        m_db = std::make_unique<CDBWrapper>(m_db_params);
65
0
    }
66
0
}
67
68
std::optional<Coin> CCoinsViewDB::GetCoin(const COutPoint& outpoint) const
69
39.9M
{
70
39.9M
    if (Coin coin; m_db->Read(CoinEntry(&outpoint), coin)) 
return coin0
;
71
39.9M
    return std::nullopt;
72
39.9M
}
73
74
0
bool CCoinsViewDB::HaveCoin(const COutPoint &outpoint) const {
75
0
    return m_db->Exists(CoinEntry(&outpoint));
76
0
}
77
78
150k
uint256 CCoinsViewDB::GetBestBlock() const {
79
150k
    uint256 hashBestChain;
80
150k
    if (!m_db->Read(DB_BEST_BLOCK, hashBestChain))
81
150k
        return uint256();
82
0
    return hashBestChain;
83
150k
}
84
85
50.4k
std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const {
86
50.4k
    std::vector<uint256> vhashHeadBlocks;
87
50.4k
    if (!m_db->Read(DB_HEAD_BLOCKS, vhashHeadBlocks)) {
88
50.4k
        return std::vector<uint256>();
89
50.4k
    }
90
0
    return vhashHeadBlocks;
91
50.4k
}
92
93
517
bool CCoinsViewDB::BatchWrite(CoinsViewCacheCursor& cursor, const uint256 &hashBlock) {
94
517
    CDBBatch batch(*m_db);
95
517
    size_t count = 0;
96
517
    size_t changed = 0;
97
517
    assert(!hashBlock.IsNull());
98
99
517
    uint256 old_tip = GetBestBlock();
100
517
    if (old_tip.IsNull()) {
101
        // We may be in the middle of replaying.
102
517
        std::vector<uint256> old_heads = GetHeadBlocks();
103
517
        if (old_heads.size() == 2) {
104
0
            if (old_heads[0] != hashBlock) {
105
0
                LogPrintLevel(BCLog::COINDB, BCLog::Level::Error, "The coins database detected an inconsistent state, likely due to a previous crash or shutdown. You will need to restart bitcoind with the -reindex-chainstate or -reindex configuration option.\n");
Line
Count
Source
273
0
    do {                                                  \
274
0
        if (LogAcceptCategory((category), (level))) {     \
275
0
            LogPrintLevel_(category, level, __VA_ARGS__); \
Line
Count
Source
255
0
#define LogPrintLevel_(category, level, ...) LogPrintFormatInternal(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__)
276
0
        }                                                 \
277
0
    } while (0)
106
0
            }
107
0
            assert(old_heads[0] == hashBlock);
108
0
            old_tip = old_heads[1];
109
0
        }
110
517
    }
111
112
    // In the first batch, mark the database as being in the middle of a
113
    // transition from old_tip to hashBlock.
114
    // A vector is used for future extensibility, as we may want to support
115
    // interrupting after partial writes from multiple independent reorgs.
116
517
    batch.Erase(DB_BEST_BLOCK);
117
517
    batch.Write(DB_HEAD_BLOCKS, Vector(hashBlock, old_tip));
118
119
104k
    for (auto it{cursor.Begin()}; it != cursor.End();) {
120
103k
        if (it->second.IsDirty()) {
121
103k
            CoinEntry entry(&it->first);
122
103k
            if (it->second.coin.IsSpent()) {
123
0
                batch.Erase(entry);
124
103k
            } else {
125
103k
                batch.Write(entry, it->second.coin);
126
103k
            }
127
128
103k
            changed++;
129
103k
        }
130
103k
        count++;
131
103k
        it = cursor.NextAndMaybeErase(*it);
132
103k
        if (batch.ApproximateSize() > m_options.batch_write_bytes) {
133
0
            LogDebug(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", batch.ApproximateSize() * (1.0 / 1048576.0));
Line
Count
Source
280
0
#define LogDebug(category, ...) LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__)
Line
Count
Source
273
0
    do {                                                  \
274
0
        if (LogAcceptCategory((category), (level))) {     \
275
0
            LogPrintLevel_(category, level, __VA_ARGS__); \
Line
Count
Source
255
0
#define LogPrintLevel_(category, level, ...) LogPrintFormatInternal(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__)
276
0
        }                                                 \
277
0
    } while (0)
134
135
0
            m_db->WriteBatch(batch);
136
0
            batch.Clear();
137
0
            if (m_options.simulate_crash_ratio) {
138
0
                static FastRandomContext rng;
139
0
                if (rng.randrange(m_options.simulate_crash_ratio) == 0) {
140
0
                    LogPrintf("Simulating a crash. Goodbye.\n");
Line
Count
Source
266
0
#define LogPrintf(...) LogInfo(__VA_ARGS__)
Line
Count
Source
261
0
#define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, __VA_ARGS__)
Line
Count
Source
255
0
#define LogPrintLevel_(category, level, ...) LogPrintFormatInternal(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__)
141
0
                    _Exit(0);
142
0
                }
143
0
            }
144
0
        }
145
103k
    }
146
147
    // In the last batch, mark the database as consistent with hashBlock again.
148
517
    batch.Erase(DB_HEAD_BLOCKS);
149
517
    batch.Write(DB_BEST_BLOCK, hashBlock);
150
151
517
    LogDebug(BCLog::COINDB, "Writing final batch of %.2f MiB\n", batch.ApproximateSize() * (1.0 / 1048576.0));
Line
Count
Source
280
517
#define LogDebug(category, ...) LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__)
Line
Count
Source
273
517
    do {                                                  \
274
517
        if (LogAcceptCategory((category), (level))) {     \
275
0
            LogPrintLevel_(category, level, __VA_ARGS__); \
Line
Count
Source
255
0
#define LogPrintLevel_(category, level, ...) LogPrintFormatInternal(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__)
276
0
        }                                                 \
277
517
    } while (0)
152
517
    bool ret = m_db->WriteBatch(batch);
153
517
    LogDebug(BCLog::COINDB, "Committed %u changed transaction outputs (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count);
Line
Count
Source
280
517
#define LogDebug(category, ...) LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__)
Line
Count
Source
273
517
    do {                                                  \
274
517
        if (LogAcceptCategory((category), (level))) {     \
275
0
            LogPrintLevel_(category, level, __VA_ARGS__); \
Line
Count
Source
255
0
#define LogPrintLevel_(category, level, ...) LogPrintFormatInternal(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__)
276
0
        }                                                 \
277
517
    } while (0)
154
517
    return ret;
155
517
}
156
157
size_t CCoinsViewDB::EstimateSize() const
158
0
{
159
0
    return m_db->EstimateSize(DB_COIN, uint8_t(DB_COIN + 1));
160
0
}
161
162
/** Specialization of CCoinsViewCursor to iterate over a CCoinsViewDB */
163
class CCoinsViewDBCursor: public CCoinsViewCursor
164
{
165
public:
166
    // Prefer using CCoinsViewDB::Cursor() since we want to perform some
167
    // cache warmup on instantiation.
168
    CCoinsViewDBCursor(CDBIterator* pcursorIn, const uint256&hashBlockIn):
169
0
        CCoinsViewCursor(hashBlockIn), pcursor(pcursorIn) {}
170
0
    ~CCoinsViewDBCursor() = default;
171
172
    bool GetKey(COutPoint &key) const override;
173
    bool GetValue(Coin &coin) const override;
174
175
    bool Valid() const override;
176
    void Next() override;
177
178
private:
179
    std::unique_ptr<CDBIterator> pcursor;
180
    std::pair<char, COutPoint> keyTmp;
181
182
    friend class CCoinsViewDB;
183
};
184
185
std::unique_ptr<CCoinsViewCursor> CCoinsViewDB::Cursor() const
186
0
{
187
0
    auto i = std::make_unique<CCoinsViewDBCursor>(
188
0
        const_cast<CDBWrapper&>(*m_db).NewIterator(), GetBestBlock());
189
    /* It seems that there are no "const iterators" for LevelDB.  Since we
190
       only need read operations on it, use a const-cast to get around
191
       that restriction.  */
192
0
    i->pcursor->Seek(DB_COIN);
193
    // Cache key of first record
194
0
    if (i->pcursor->Valid()) {
195
0
        CoinEntry entry(&i->keyTmp.second);
196
0
        i->pcursor->GetKey(entry);
197
0
        i->keyTmp.first = entry.key;
198
0
    } else {
199
0
        i->keyTmp.first = 0; // Make sure Valid() and GetKey() return false
200
0
    }
201
0
    return i;
202
0
}
203
204
bool CCoinsViewDBCursor::GetKey(COutPoint &key) const
205
0
{
206
    // Return cached key
207
0
    if (keyTmp.first == DB_COIN) {
208
0
        key = keyTmp.second;
209
0
        return true;
210
0
    }
211
0
    return false;
212
0
}
213
214
bool CCoinsViewDBCursor::GetValue(Coin &coin) const
215
0
{
216
0
    return pcursor->GetValue(coin);
217
0
}
218
219
bool CCoinsViewDBCursor::Valid() const
220
0
{
221
0
    return keyTmp.first == DB_COIN;
222
0
}
223
224
void CCoinsViewDBCursor::Next()
225
0
{
226
0
    pcursor->Next();
227
0
    CoinEntry entry(&keyTmp.second);
228
0
    if (!pcursor->Valid() || !pcursor->GetKey(entry)) {
229
0
        keyTmp.first = 0; // Invalidate cached key after last record so that Valid() and GetKey() return false
230
0
    } else {
231
0
        keyTmp.first = entry.key;
232
0
    }
233
0
}