Coverage Report

Created: 2026-10-03 10:33

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
std::shared_ptr<CWallet> TestCreateWallet(WalletContext& context);
41
std::shared_ptr<CWallet> TestCreateWallet(std::unique_ptr<WalletDatabase> database, WalletContext& context, uint64_t create_flags);
42
std::shared_ptr<CWallet> TestLoadWallet(WalletContext& context);
43
std::shared_ptr<CWallet> TestLoadWallet(std::unique_ptr<WalletDatabase> database, WalletContext& context);
44
void TestUnloadWallet(std::shared_ptr<CWallet>&& wallet);
45
46
/** Returns a new encoded destination from the wallet (hardcoded to BECH32) */
47
std::string getnewaddress(CWallet& w);
48
/** Returns a new destination, of an specific type, from the wallet */
49
CTxDestination getNewDestination(CWallet& w, OutputType output_type);
50
51
using MockableData = std::map<SerializeData, SerializeData, std::less<>>;
52
53
54
class MockableSQLiteBatch : public SQLiteBatch
55
{
56
public:
57
    using SQLiteBatch::SQLiteBatch;
58
    using SQLiteBatch::WriteKey;
59
};
60
61
/** A WalletDatabase whose contents and return values can be modified as needed for testing
62
 **/
63
class MockableSQLiteDatabase : public InMemoryWalletDatabase
64
{
65
public:
66
    MockableSQLiteDatabase();
67
68
0
    bool Backup(const std::string& strDest) const override { return true; }
69
70
46.1k
    std::string Filename() override { return "mockable"; }
71
0
    std::string Format() override { return "sqlite-mock"; }
72
20.3k
    std::unique_ptr<DatabaseBatch> MakeBatch() override { return std::make_unique<MockableSQLiteBatch>(*this); }
73
};
74
75
/** A SQLite wallet database that can fail selected operations for testing */
76
class FaultInjectingDatabase : public MockableSQLiteDatabase
77
{
78
public:
79
    void FailNextWrite(std::string record_type, size_t match_skip_count = 0)
80
5
    {
81
5
        m_fail_write = {std::move(record_type), match_skip_count};
82
5
    }
83
2
    void FailNextErase(std::string record_type) { m_fail_erase = {std::move(record_type)}; }
84
2
    void FailNextCommit() { m_fail_commit = true; }
85
86
    std::optional<SerializeData> GetRecordValue(const std::string& record_type)
87
37
    {
88
37
        auto batch{MakeBatch()};
89
37
        if (auto cursor{batch->GetNewPrefixCursor(DataStream() << record_type)}) {
90
37
            DataStream key, value;
91
37
            if (cursor->Next(key, value) == DatabaseCursor::Status::MORE) {
92
20
                return SerializeData{value.begin(), value.end()};
93
20
            }
94
37
        }
95
17
        return std::nullopt;
96
37
    }
97
98
34
    bool HasRecordType(const std::string& record_type) { return GetRecordValue(record_type).has_value(); }
99
100
268
    std::unique_ptr<DatabaseBatch> MakeBatch() override { return std::make_unique<Batch>(*this); }
101
102
private:
103
    struct Failure {
104
        std::string record_type;
105
        size_t match_skip_count{0};
106
    };
107
108
    static bool ShouldFail(std::optional<Failure>& failure, const DataStream& key)
109
1.08k
    {
110
1.08k
        if (!failure) return false;
111
9
        std::string record_type;
112
9
        SpanReader{MakeByteSpan(key)} >> record_type;
113
9
        if (failure->record_type != record_type) return false;
114
8
        if (failure->match_skip_count > 0) {
115
1
            --failure->match_skip_count;
116
1
            return false;
117
1
        }
118
7
        failure.reset();
119
7
        return true;
120
8
    }
121
122
    struct Batch : SQLiteBatch {
123
268
        explicit Batch(FaultInjectingDatabase& database) : SQLiteBatch(database), m_owner{database} {}
124
125
        bool TxnCommit() override
126
83
        {
127
83
            if (std::exchange(m_owner.m_fail_commit, false)) return false;
128
81
            return SQLiteBatch::TxnCommit();
129
83
        }
130
131
        bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite = true) override
132
978
        {
133
978
            if (ShouldFail(m_owner.m_fail_write, key)) return false;
134
973
            return SQLiteBatch::WriteKey(std::move(key), std::move(value), overwrite);
135
978
        }
136
137
        bool EraseKey(DataStream&& key) override
138
102
        {
139
102
            if (ShouldFail(m_owner.m_fail_erase, key)) return false;
140
100
            return SQLiteBatch::EraseKey(std::move(key));
141
102
        }
142
143
        FaultInjectingDatabase& m_owner;
144
    };
145
146
    std::optional<Failure> m_fail_write;
147
    std::optional<Failure> m_fail_erase;
148
    bool m_fail_commit{false};
149
};
150
151
std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase();
152
MockableSQLiteDatabase& GetMockableDatabase(CWallet& wallet);
153
154
DescriptorScriptPubKeyMan* CreateDescriptor(CWallet& keystore, const std::string& desc_str, bool success);
155
} // namespace wallet
156
157
#endif // BITCOIN_WALLET_TEST_UTIL_H