Bitcoin Core Fuzz Coverage Report

Coverage Report

Created: 2026-03-24 13:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/bitcoin/src/wallet/dump.cpp
Line
Count
Source
1
// Copyright (c) 2020-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
#include <wallet/dump.h>
6
7
#include <common/args.h>
8
#include <util/fs.h>
9
#include <util/translation.h>
10
#include <wallet/wallet.h>
11
#include <wallet/walletdb.h>
12
13
#include <algorithm>
14
#include <fstream>
15
#include <memory>
16
#include <string>
17
#include <utility>
18
#include <vector>
19
20
namespace wallet {
21
static const std::string DUMP_MAGIC = "BITCOIN_CORE_WALLET_DUMP";
22
uint32_t DUMP_VERSION = 1;
23
24
bool DumpWallet(const ArgsManager& args, WalletDatabase& db, bilingual_str& error)
25
0
{
26
    // Get the dumpfile
27
0
    std::string dump_filename = args.GetArg("-dumpfile", "");
28
0
    if (dump_filename.empty()) {
29
0
        error = _("No dump file provided. To use dump, -dumpfile=<filename> must be provided.");
30
0
        return false;
31
0
    }
32
33
0
    fs::path path = fs::PathFromString(dump_filename);
34
0
    path = fs::absolute(path);
35
0
    if (fs::exists(path)) {
36
0
        error = strprintf(_("File %s already exists. If you are sure this is what you want, move it out of the way first."), fs::PathToString(path));
Line
Count
Source
1172
0
#define strprintf tfm::format
37
0
        return false;
38
0
    }
39
0
    std::ofstream dump_file;
40
0
    dump_file.open(path.std_path());
41
0
    if (dump_file.fail()) {
42
0
        error = strprintf(_("Unable to open %s for writing"), fs::PathToString(path));
Line
Count
Source
1172
0
#define strprintf tfm::format
43
0
        return false;
44
0
    }
45
46
0
    HashWriter hasher{};
47
48
0
    std::unique_ptr<DatabaseBatch> batch = db.MakeBatch();
49
50
0
    bool ret = true;
51
0
    std::unique_ptr<DatabaseCursor> cursor = batch->GetNewCursor();
52
0
    if (!cursor) {
53
0
        error = _("Error: Couldn't create cursor into database");
54
0
        ret = false;
55
0
    }
56
57
    // Write out a magic string with version
58
0
    std::string line = strprintf("%s,%u\n", DUMP_MAGIC, DUMP_VERSION);
Line
Count
Source
1172
0
#define strprintf tfm::format
59
0
    dump_file.write(line.data(), line.size());
60
0
    hasher << std::span{line};
61
62
    // Write out the file format
63
0
    std::string format = db.Format();
64
    // BDB files that are opened using BerkeleyRODatabase have its format as "bdb_ro"
65
    // We want to override that format back to "bdb"
66
0
    if (format == "bdb_ro") {
67
0
        format = "bdb";
68
0
    }
69
0
    line = strprintf("%s,%s\n", "format", format);
Line
Count
Source
1172
0
#define strprintf tfm::format
70
0
    dump_file.write(line.data(), line.size());
71
0
    hasher << std::span{line};
72
73
0
    if (ret) {
74
75
        // Read the records
76
0
        while (true) {
77
0
            DataStream ss_key{};
78
0
            DataStream ss_value{};
79
0
            DatabaseCursor::Status status = cursor->Next(ss_key, ss_value);
80
0
            if (status == DatabaseCursor::Status::DONE) {
81
0
                ret = true;
82
0
                break;
83
0
            } else if (status == DatabaseCursor::Status::FAIL) {
84
0
                error = _("Error reading next record from wallet database");
85
0
                ret = false;
86
0
                break;
87
0
            }
88
0
            std::string key_str = HexStr(ss_key);
89
0
            std::string value_str = HexStr(ss_value);
90
0
            line = strprintf("%s,%s\n", key_str, value_str);
Line
Count
Source
1172
0
#define strprintf tfm::format
91
0
            dump_file.write(line.data(), line.size());
92
0
            hasher << std::span{line};
93
0
        }
94
0
    }
95
96
0
    cursor.reset();
97
0
    batch.reset();
98
99
0
    if (ret) {
100
        // Write the hash
101
0
        tfm::format(dump_file, "checksum,%s\n", HexStr(hasher.GetHash()));
102
0
        dump_file.close();
103
0
    } else {
104
        // Remove the dumpfile on failure
105
0
        dump_file.close();
106
0
        fs::remove(path);
107
0
    }
108
109
0
    return ret;
110
0
}
111
112
// The standard wallet deleter function blocks on the validation interface
113
// queue, which doesn't exist for the bitcoin-wallet. Define our own
114
// deleter here.
115
static void WalletToolReleaseWallet(CWallet* wallet)
116
0
{
117
0
    wallet->WalletLogPrintf("Releasing wallet\n");
118
0
    wallet->Close();
119
0
    delete wallet;
120
0
}
121
122
bool CreateFromDump(const ArgsManager& args, const std::string& name, const fs::path& wallet_path, bilingual_str& error, std::vector<bilingual_str>& warnings)
123
0
{
124
0
    if (name.empty()) {
125
0
        tfm::format(std::cerr, "Wallet name cannot be empty\n");
126
0
        return false;
127
0
    }
128
129
    // Get the dumpfile
130
0
    std::string dump_filename = args.GetArg("-dumpfile", "");
131
0
    if (dump_filename.empty()) {
132
0
        error = _("No dump file provided. To use createfromdump, -dumpfile=<filename> must be provided.");
133
0
        return false;
134
0
    }
135
136
0
    fs::path dump_path = fs::PathFromString(dump_filename);
137
0
    dump_path = fs::absolute(dump_path);
138
0
    if (!fs::exists(dump_path)) {
139
0
        error = strprintf(_("Dump file %s does not exist."), fs::PathToString(dump_path));
Line
Count
Source
1172
0
#define strprintf tfm::format
140
0
        return false;
141
0
    }
142
0
    std::ifstream dump_file{dump_path.std_path()};
143
144
    // Compute the checksum
145
0
    HashWriter hasher{};
146
0
    uint256 checksum;
147
148
    // Check the magic and version
149
0
    std::string magic_key;
150
0
    std::getline(dump_file, magic_key, ',');
151
0
    std::string version_value;
152
0
    std::getline(dump_file, version_value, '\n');
153
0
    if (magic_key != DUMP_MAGIC) {
154
0
        error = strprintf(_("Error: Dumpfile identifier record is incorrect. Got \"%s\", expected \"%s\"."), magic_key, DUMP_MAGIC);
Line
Count
Source
1172
0
#define strprintf tfm::format
155
0
        dump_file.close();
156
0
        return false;
157
0
    }
158
    // Check the version number (value of first record)
159
0
    const auto ver{ToIntegral<uint32_t>(version_value)};
160
0
    if (!ver) {
161
0
        error = strprintf(_("Error: Unable to parse version %u as a uint32_t"), version_value);
Line
Count
Source
1172
0
#define strprintf tfm::format
162
0
        dump_file.close();
163
0
        return false;
164
0
    }
165
0
    if (*ver != DUMP_VERSION) {
166
0
        error = strprintf(_("Error: Dumpfile version is not supported. This version of bitcoin-wallet only supports version 1 dumpfiles. Got dumpfile with version %s"), version_value);
Line
Count
Source
1172
0
#define strprintf tfm::format
167
0
        dump_file.close();
168
0
        return false;
169
0
    }
170
0
    std::string magic_hasher_line = strprintf("%s,%s\n", magic_key, version_value);
Line
Count
Source
1172
0
#define strprintf tfm::format
171
0
    hasher << std::span{magic_hasher_line};
172
173
    // Get the stored file format
174
0
    std::string format_key;
175
0
    std::getline(dump_file, format_key, ',');
176
0
    std::string format_value;
177
0
    std::getline(dump_file, format_value, '\n');
178
0
    if (format_key != "format") {
179
0
        error = strprintf(_("Error: Dumpfile format record is incorrect. Got \"%s\", expected \"format\"."), format_key);
Line
Count
Source
1172
0
#define strprintf tfm::format
180
0
        dump_file.close();
181
0
        return false;
182
0
    }
183
    // Make sure that the dump was created from a sqlite database only as that is the only
184
    // type of database that we still support.
185
    // Other formats such as BDB should not be loaded into a sqlite database since they also
186
    // use a different type of wallet entirely which is no longer compatible with this software.
187
0
    if (format_value != "sqlite") {
188
0
        error = strprintf(_("Error: Dumpfile specifies an unsupported database format (%s). Only sqlite database dumps are supported"), format_value);
Line
Count
Source
1172
0
#define strprintf tfm::format
189
0
        return false;
190
0
    }
191
0
    std::string format_hasher_line = strprintf("%s,%s\n", format_key, format_value);
Line
Count
Source
1172
0
#define strprintf tfm::format
192
0
    hasher << std::span{format_hasher_line};
193
194
0
    DatabaseOptions options;
195
0
    DatabaseStatus status;
196
0
    ReadDatabaseArgs(args, options);
197
0
    options.require_create = true;
198
0
    options.require_format = DatabaseFormat::SQLITE;
199
0
    std::unique_ptr<WalletDatabase> database = MakeDatabase(wallet_path, options, status, error);
200
0
    if (!database) return false;
201
202
    // dummy chain interface
203
0
    bool ret = true;
204
0
    std::shared_ptr<CWallet> wallet(new CWallet(/*chain=*/nullptr, name, std::move(database)), WalletToolReleaseWallet);
205
0
    {
206
        // Get the database handle
207
0
        WalletDatabase& db = wallet->GetDatabase();
208
0
        std::unique_ptr<DatabaseBatch> batch = db.MakeBatch();
209
0
        batch->TxnBegin();
210
211
        // Read the records from the dump file and write them to the database
212
0
        while (dump_file.good()) {
213
0
            std::string key;
214
0
            std::getline(dump_file, key, ',');
215
0
            std::string value;
216
0
            std::getline(dump_file, value, '\n');
217
218
0
            if (key == "checksum") {
219
0
                std::vector<unsigned char> parsed_checksum = ParseHex(value);
220
0
                if (parsed_checksum.size() != checksum.size()) {
221
0
                    error = Untranslated("Error: Checksum is not the correct size");
222
0
                    ret = false;
223
0
                    break;
224
0
                }
225
0
                std::copy(parsed_checksum.begin(), parsed_checksum.end(), checksum.begin());
226
0
                break;
227
0
            }
228
229
0
            std::string line = strprintf("%s,%s\n", key, value);
Line
Count
Source
1172
0
#define strprintf tfm::format
230
0
            hasher << std::span{line};
231
232
0
            if (key.empty() || value.empty()) {
233
0
                continue;
234
0
            }
235
236
0
            if (!IsHex(key)) {
237
0
                error = strprintf(_("Error: Got key that was not hex: %s"), key);
Line
Count
Source
1172
0
#define strprintf tfm::format
238
0
                ret = false;
239
0
                break;
240
0
            }
241
0
            if (!IsHex(value)) {
242
0
                error = strprintf(_("Error: Got value that was not hex: %s"), value);
Line
Count
Source
1172
0
#define strprintf tfm::format
243
0
                ret = false;
244
0
                break;
245
0
            }
246
247
0
            std::vector<unsigned char> k = ParseHex(key);
248
0
            std::vector<unsigned char> v = ParseHex(value);
249
0
            if (!batch->Write(std::span{k}, std::span{v})) {
250
0
                error = strprintf(_("Error: Unable to write record to new wallet"));
Line
Count
Source
1172
0
#define strprintf tfm::format
251
0
                ret = false;
252
0
                break;
253
0
            }
254
0
        }
255
256
0
        if (ret) {
257
0
            uint256 comp_checksum = hasher.GetHash();
258
0
            if (checksum.IsNull()) {
259
0
                error = _("Error: Missing checksum");
260
0
                ret = false;
261
0
            } else if (checksum != comp_checksum) {
262
0
                error = strprintf(_("Error: Dumpfile checksum does not match. Computed %s, expected %s"), HexStr(comp_checksum), HexStr(checksum));
Line
Count
Source
1172
0
#define strprintf tfm::format
263
0
                ret = false;
264
0
            }
265
0
        }
266
267
0
        if (ret) {
268
0
            batch->TxnCommit();
269
0
        } else {
270
0
            batch->TxnAbort();
271
0
        }
272
273
0
        batch.reset();
274
275
0
        dump_file.close();
276
0
    }
277
    // On failure, gather the paths to remove
278
0
    std::vector<fs::path> paths_to_remove = wallet->GetDatabase().Files();
279
0
    if (!name.empty()) paths_to_remove.push_back(wallet_path);
280
281
0
    wallet.reset(); // The pointer deleter will close the wallet for us.
282
283
    // Remove the wallet dir if we have a failure
284
0
    if (!ret) {
285
0
        for (const auto& p : paths_to_remove) {
286
0
            fs::remove(p);
287
0
        }
288
0
    }
289
290
0
    return ret;
291
0
}
292
} // namespace wallet