Coverage Report

Created: 2026-10-07 13:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/bitcoin/src/wallet/test/util.h
Line
Count
Source
1
// Copyright (c) 2021-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_WALLET_TEST_UTIL_H
6
#define BITCOIN_WALLET_TEST_UTIL_H
7
8
#include <addresstype.h>
9
#include <streams.h>
10
#include <wallet/db.h>
11
#include <wallet/scriptpubkeyman.h>
12
#include <wallet/sqlite.h>
13
14
#include <memory>
15
#include <optional>
16
#include <string>
17
#include <utility>
18
19
class ArgsManager;
20
class CChain;
21
class CKey;
22
enum class OutputType;
23
namespace interfaces {
24
class Chain;
25
} // namespace interfaces
26
27
namespace wallet {
28
class CWallet;
29
class WalletDatabase;
30
struct WalletContext;
31
32
inline constexpr DatabaseFormat DATABASE_FORMATS[] = {
33
       DatabaseFormat::SQLITE,
34
};
35
36
inline const std::string ADDRESS_BCRT1_UNSPENDABLE = "bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj";
37
38
std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, const CKey& key);
39
40
void AddKey(CWallet& wallet, const CKey& key);
41
42
std::shared_ptr<CWallet> TestCreateWallet(WalletContext& context);
43
std::shared_ptr<CWallet> TestCreateWallet(std::unique_ptr<WalletDatabase> database, WalletContext& context, uint64_t create_flags);
44
std::shared_ptr<CWallet> TestLoadWallet(WalletContext& context);
45
std::shared_ptr<CWallet> TestLoadWallet(std::unique_ptr<WalletDatabase> database, WalletContext& context);
46
void TestUnloadWallet(std::shared_ptr<CWallet>&& wallet);
47
48
/** Returns a new encoded destination from the wallet (hardcoded to BECH32) */
49
std::string getnewaddress(CWallet& w);
50
/** Returns a new destination, of an specific type, from the wallet */
51
CTxDestination getNewDestination(CWallet& w, OutputType output_type);
52
53
using MockableData = std::map<SerializeData, SerializeData, std::less<>>;
54
55
56
class MockableSQLiteBatch : public SQLiteBatch
57
{
58
public:
59
    using SQLiteBatch::SQLiteBatch;
60
    using SQLiteBatch::WriteKey;
61
};
62
63
/** A WalletDatabase whose contents and return values can be modified as needed for testing
64
 **/
65
class MockableSQLiteDatabase : public InMemoryWalletDatabase
66
{
67
public:
68
    MockableSQLiteDatabase();
69
70
0
    bool Backup(const std::string& strDest) const override { return true; }
71
72
46.1k
    std::string Filename() override { return "mockable"; }
73
0
    std::string Format() override { return "sqlite-mock"; }
74
20.3k
    std::unique_ptr<DatabaseBatch> MakeBatch() override { return std::make_unique<MockableSQLiteBatch>(*this); }
75
};
76
77
/** A SQLite wallet database that can fail selected operations for testing */
78
class FaultInjectingDatabase : public MockableSQLiteDatabase
79
{
80
public:
81
    void FailNextWrite(std::string record_type, size_t match_skip_count = 0)
82
5
    {
83
5
        m_fail_write = {std::move(record_type), match_skip_count};
84
5
    }
85
2
    void FailNextErase(std::string record_type) { m_fail_erase = {std::move(record_type)}; }
86
2
    void FailNextCommit() { m_fail_commit = true; }
87
88
    std::optional<SerializeData> GetRecordValue(const std::string& record_type)
89
37
    {
90
37
        auto batch{MakeBatch()};
91
37
        if (auto cursor{batch->GetNewPrefixCursor(DataStream() << record_type)}) {
92
37
            DataStream key, value;
93
37
            if (cursor->Next(key, value) == DatabaseCursor::Status::MORE) {
94
20
                return SerializeData{value.begin(), value.end()};
95
20
            }
96
37
        }
97
17
        return std::nullopt;
98
37
    }
99
100
34
    bool HasRecordType(const std::string& record_type) { return GetRecordValue(record_type).has_value(); }
101
102
277
    std::unique_ptr<DatabaseBatch> MakeBatch() override { return std::make_unique<Batch>(*this); }
103
104
private:
105
    struct Failure {
106
        std::string record_type;
107
        size_t match_skip_count{0};
108
    };
109
110
    static bool ShouldFail(std::optional<Failure>& failure, const DataStream& key)
111
1.10k
    {
112
1.10k
        if (!failure) return false;
113
9
        std::string record_type;
114
9
        SpanReader{MakeByteSpan(key)} >> record_type;
115
9
        if (failure->record_type != record_type) return false;
116
8
        if (failure->match_skip_count > 0) {
117
1
            --failure->match_skip_count;
118
1
            return false;
119
1
        }
120
7
        failure.reset();
121
7
        return true;
122
8
    }
123
124
    struct Batch : SQLiteBatch {
125
277
        explicit Batch(FaultInjectingDatabase& database) : SQLiteBatch(database), m_owner{database} {}
126
127
        bool TxnCommit() override
128
83
        {
129
83
            if (std::exchange(m_owner.m_fail_commit, false)) return false;
130
81
            return SQLiteBatch::TxnCommit();
131
83
        }
132
133
        bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite = true) override
134
999
        {
135
999
            if (ShouldFail(m_owner.m_fail_write, key)) return false;
136
994
            return SQLiteBatch::WriteKey(std::move(key), std::move(value), overwrite);
137
999
        }
138
139
        bool EraseKey(DataStream&& key) override
140
102
        {
141
102
            if (ShouldFail(m_owner.m_fail_erase, key)) return false;
142
100
            return SQLiteBatch::EraseKey(std::move(key));
143
102
        }
144
145
        FaultInjectingDatabase& m_owner;
146
    };
147
148
    std::optional<Failure> m_fail_write;
149
    std::optional<Failure> m_fail_erase;
150
    bool m_fail_commit{false};
151
};
152
153
std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase();
154
MockableSQLiteDatabase& GetMockableDatabase(CWallet& wallet);
155
156
DescriptorScriptPubKeyMan* CreateDescriptor(CWallet& keystore, const std::string& desc_str, bool success);
157
} // namespace wallet
158
159
#endif // BITCOIN_WALLET_TEST_UTIL_H