fuzz coverage

Coverage Report

Created: 2025-06-01 19:34

/Users/eugenesiegel/btc/bitcoin/src/wallet/dump.cpp
Line
Count
Source (jump to first uncovered line)
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);
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 it's 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
    // Get the dumpfile
125
0
    std::string dump_filename = args.GetArg("-dumpfile", "");
126
0
    if (dump_filename.empty()) {
127
0
        error = _("No dump file provided. To use createfromdump, -dumpfile=<filename> must be provided.");
128
0
        return false;
129
0
    }
130
131
0
    fs::path dump_path = fs::PathFromString(dump_filename);
132
0
    dump_path = fs::absolute(dump_path);
133
0
    if (!fs::exists(dump_path)) {
134
0
        error = strprintf(_("Dump file %s does not exist."), fs::PathToString(dump_path));
Line
Count
Source
1172
0
#define strprintf tfm::format
135
0
        return false;
136
0
    }
137
0
    std::ifstream dump_file{dump_path};
138
139
    // Compute the checksum
140
0
    HashWriter hasher{};
141
0
    uint256 checksum;
142
143
    // Check the magic and version
144
0
    std::string magic_key;
145
0
    std::getline(dump_file, magic_key, ',');
146
0
    std::string version_value;
147
0
    std::getline(dump_file, version_value, '\n');
148
0
    if (magic_key != DUMP_MAGIC) {
149
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
150
0
        dump_file.close();
151
0
        return false;
152
0
    }
153
    // Check the version number (value of first record)
154
0
    uint32_t ver;
155
0
    if (!ParseUInt32(version_value, &ver)) {
156
0
        error =strprintf(_("Error: Unable to parse version %u as a uint32_t"), version_value);
Line
Count
Source
1172
0
#define strprintf tfm::format
157
0
        dump_file.close();
158
0
        return false;
159
0
    }
160
0
    if (ver != DUMP_VERSION) {
161
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
162
0
        dump_file.close();
163
0
        return false;
164
0
    }
165
0
    std::string magic_hasher_line = strprintf("%s,%s\n", magic_key, version_value);
Line
Count
Source
1172
0
#define strprintf tfm::format
166
0
    hasher << std::span{magic_hasher_line};
167
168
    // Get the stored file format
169
0
    std::string format_key;
170
0
    std::getline(dump_file, format_key, ',');
171
0
    std::string format_value;
172
0
    std::getline(dump_file, format_value, '\n');
173
0
    if (format_key != "format") {
174
0
        error = strprintf(_("Error: Dumpfile format record is incorrect. Got \"%s\", expected \"format\"."), format_key);
Line
Count
Source
1172
0
#define strprintf tfm::format
175
0
        dump_file.close();
176
0
        return false;
177
0
    }
178
    // Make sure that the dump was created from a sqlite database only as that is the only
179
    // type of database that we still support.
180
    // Other formats such as BDB should not be loaded into a sqlite database since they also
181
    // use a different type of wallet entirely which is no longer compatible with this software.
182
0
    if (format_value != "sqlite") {
183
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
184
0
        return false;
185
0
    }
186
0
    std::string format_hasher_line = strprintf("%s,%s\n", format_key, format_value);
Line
Count
Source
1172
0
#define strprintf tfm::format
187
0
    hasher << std::span{format_hasher_line};
188
189
0
    DatabaseOptions options;
190
0
    DatabaseStatus status;
191
0
    ReadDatabaseArgs(args, options);
192
0
    options.require_create = true;
193
0
    options.require_format = DatabaseFormat::SQLITE;
194
0
    std::unique_ptr<WalletDatabase> database = MakeDatabase(wallet_path, options, status, error);
195
0
    if (!database) return false;
196
197
    // dummy chain interface
198
0
    bool ret = true;
199
0
    std::shared_ptr<CWallet> wallet(new CWallet(/*chain=*/nullptr, name, std::move(database)), WalletToolReleaseWallet);
200
0
    {
201
0
        LOCK(wallet->cs_wallet);
Line
Count
Source
257
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
202
0
        DBErrors load_wallet_ret = wallet->LoadWallet();
203
0
        if (load_wallet_ret != DBErrors::LOAD_OK) {
204
0
            error = strprintf(_("Error creating %s"), name);
Line
Count
Source
1172
0
#define strprintf tfm::format
205
0
            return false;
206
0
        }
207
208
        // Get the database handle
209
0
        WalletDatabase& db = wallet->GetDatabase();
210
0
        std::unique_ptr<DatabaseBatch> batch = db.MakeBatch();
211
0
        batch->TxnBegin();
212
213
        // Read the records from the dump file and write them to the database
214
0
        while (dump_file.good()) {
215
0
            std::string key;
216
0
            std::getline(dump_file, key, ',');
217
0
            std::string value;
218
0
            std::getline(dump_file, value, '\n');
219
220
0
            if (key == "checksum") {
221
0
                std::vector<unsigned char> parsed_checksum = ParseHex(value);
222
0
                if (parsed_checksum.size() != checksum.size()) {
223
0
                    error = Untranslated("Error: Checksum is not the correct size");
224
0
                    ret = false;
225
0
                    break;
226
0
                }
227
0
                std::copy(parsed_checksum.begin(), parsed_checksum.end(), checksum.begin());
228
0
                break;
229
0
            }
230
231
0
            std::string line = strprintf("%s,%s\n", key, value);
Line
Count
Source
1172
0
#define strprintf tfm::format
232
0
            hasher << std::span{line};
233
234
0
            if (key.empty() || value.empty()) {
235
0
                continue;
236
0
            }
237
238
0
            if (!IsHex(key)) {
239
0
                error = strprintf(_("Error: Got key that was not hex: %s"), key);
Line
Count
Source
1172
0
#define strprintf tfm::format
240
0
                ret = false;
241
0
                break;
242
0
            }
243
0
            if (!IsHex(value)) {
244
0
                error = strprintf(_("Error: Got value that was not hex: %s"), value);
Line
Count
Source
1172
0
#define strprintf tfm::format
245
0
                ret = false;
246
0
                break;
247
0
            }
248
249
0
            std::vector<unsigned char> k = ParseHex(key);
250
0
            std::vector<unsigned char> v = ParseHex(value);
251
0
            if (!batch->Write(std::span{k}, std::span{v})) {
252
0
                error = strprintf(_("Error: Unable to write record to new wallet"));
Line
Count
Source
1172
0
#define strprintf tfm::format
253
0
                ret = false;
254
0
                break;
255
0
            }
256
0
        }
257
258
0
        if (ret) {
259
0
            uint256 comp_checksum = hasher.GetHash();
260
0
            if (checksum.IsNull()) {
261
0
                error = _("Error: Missing checksum");
262
0
                ret = false;
263
0
            } else if (checksum != comp_checksum) {
264
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
265
0
                ret = false;
266
0
            }
267
0
        }
268
269
0
        if (ret) {
270
0
            batch->TxnCommit();
271
0
        } else {
272
0
            batch->TxnAbort();
273
0
        }
274
275
0
        batch.reset();
276
277
0
        dump_file.close();
278
0
    }
279
0
    wallet.reset(); // The pointer deleter will close the wallet for us.
280
281
    // Remove the wallet dir if we have a failure
282
0
    if (!ret) {
283
0
        fs::remove_all(wallet_path);
284
0
    }
285
286
0
    return ret;
287
0
}
288
} // namespace wallet