Coverage Report

Created: 2025-03-18 19:34

/root/bitcoin/src/wallet/load.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 <wallet/load.h>
7
8
#include <common/args.h>
9
#include <interfaces/chain.h>
10
#include <scheduler.h>
11
#include <util/check.h>
12
#include <util/fs.h>
13
#include <util/string.h>
14
#include <util/translation.h>
15
#include <wallet/context.h>
16
#include <wallet/spend.h>
17
#include <wallet/wallet.h>
18
#include <wallet/walletdb.h>
19
20
#include <univalue.h>
21
22
#include <system_error>
23
24
using util::Join;
25
26
namespace wallet {
27
bool VerifyWallets(WalletContext& context)
28
0
{
29
0
    interfaces::Chain& chain = *context.chain;
30
0
    ArgsManager& args = *Assert(context.args);
31
32
0
    if (args.IsArgSet("-walletdir")) {
  Branch (32:9): [True: 0, False: 0]
33
0
        const fs::path wallet_dir{args.GetPathArg("-walletdir")};
34
0
        std::error_code error;
35
        // The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory
36
        // It also lets the fs::exists and fs::is_directory checks below pass on windows, since they return false
37
        // if a path has trailing slashes, and it strips trailing slashes.
38
0
        fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
39
0
        if (error || !fs::exists(canonical_wallet_dir)) {
  Branch (39:13): [True: 0, False: 0]
  Branch (39:22): [True: 0, False: 0]
40
0
            chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), fs::PathToString(wallet_dir)));
41
0
            return false;
42
0
        } else if (!fs::is_directory(canonical_wallet_dir)) {
  Branch (42:20): [True: 0, False: 0]
43
0
            chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), fs::PathToString(wallet_dir)));
44
0
            return false;
45
        // The canonical path transforms relative paths into absolute ones, so we check the non-canonical version
46
0
        } else if (!wallet_dir.is_absolute()) {
  Branch (46:20): [True: 0, False: 0]
47
0
            chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), fs::PathToString(wallet_dir)));
48
0
            return false;
49
0
        }
50
0
        args.ForceSetArg("-walletdir", fs::PathToString(canonical_wallet_dir));
51
0
    }
52
53
0
    LogPrintf("Using wallet directory %s\n", fs::PathToString(GetWalletDir()));
54
55
0
    chain.initMessage(_("Verifying wallet(s)…"));
56
57
    // For backwards compatibility if an unnamed top level wallet exists in the
58
    // wallets directory, include it in the default list of wallets to load.
59
0
    if (!args.IsArgSet("wallet")) {
  Branch (59:9): [True: 0, False: 0]
60
0
        DatabaseOptions options;
61
0
        DatabaseStatus status;
62
0
        ReadDatabaseArgs(args, options);
63
0
        bilingual_str error_string;
64
0
        options.require_existing = true;
65
0
        options.verify = false;
66
0
        if (MakeWalletDatabase("", options, status, error_string)) {
  Branch (66:13): [True: 0, False: 0]
67
0
            common::SettingsValue wallets(common::SettingsValue::VARR);
68
0
            wallets.push_back(""); // Default wallet name is ""
69
            // Pass write=false because no need to write file and probably
70
            // better not to. If unnamed wallet needs to be added next startup
71
            // and the setting is empty, this code will just run again.
72
0
            chain.overwriteRwSetting("wallet", std::move(wallets), interfaces::SettingsAction::SKIP_WRITE);
73
0
        }
74
0
    }
75
76
    // Keep track of each wallet absolute path to detect duplicates.
77
0
    std::set<fs::path> wallet_paths;
78
79
0
    for (const auto& wallet : chain.getSettingsList("wallet")) {
  Branch (79:29): [True: 0, False: 0]
80
0
        if (!wallet.isStr()) {
  Branch (80:13): [True: 0, False: 0]
81
0
            chain.initError(_("Invalid value detected for '-wallet' or '-nowallet'. "
82
0
                              "'-wallet' requires a string value, while '-nowallet' accepts only '1' to disable all wallets"));
83
0
            return false;
84
0
        }
85
0
        const auto& wallet_file = wallet.get_str();
86
0
        const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(wallet_file));
87
88
0
        if (!wallet_paths.insert(path).second) {
  Branch (88:13): [True: 0, False: 0]
89
0
            chain.initWarning(strprintf(_("Ignoring duplicate -wallet %s."), wallet_file));
90
0
            continue;
91
0
        }
92
93
0
        DatabaseOptions options;
94
0
        DatabaseStatus status;
95
0
        ReadDatabaseArgs(args, options);
96
0
        options.require_existing = true;
97
0
        options.verify = true;
98
0
        bilingual_str error_string;
99
0
        if (!MakeWalletDatabase(wallet_file, options, status, error_string)) {
  Branch (99:13): [True: 0, False: 0]
100
0
            if (status == DatabaseStatus::FAILED_NOT_FOUND) {
  Branch (100:17): [True: 0, False: 0]
101
0
                chain.initWarning(Untranslated(strprintf("Skipping -wallet path that doesn't exist. %s", error_string.original)));
102
0
            } else {
103
0
                chain.initError(error_string);
104
0
                return false;
105
0
            }
106
0
        }
107
0
    }
108
109
0
    return true;
110
0
}
111
112
bool LoadWallets(WalletContext& context)
113
0
{
114
0
    interfaces::Chain& chain = *context.chain;
115
0
    try {
116
0
        std::set<fs::path> wallet_paths;
117
0
        for (const auto& wallet : chain.getSettingsList("wallet")) {
  Branch (117:33): [True: 0, False: 0]
118
0
            if (!wallet.isStr()) {
  Branch (118:17): [True: 0, False: 0]
119
0
                chain.initError(_("Invalid value detected for '-wallet' or '-nowallet'. "
120
0
                                  "'-wallet' requires a string value, while '-nowallet' accepts only '1' to disable all wallets"));
121
0
                return false;
122
0
            }
123
0
            const auto& name = wallet.get_str();
124
0
            if (!wallet_paths.insert(fs::PathFromString(name)).second) {
  Branch (124:17): [True: 0, False: 0]
125
0
                continue;
126
0
            }
127
0
            DatabaseOptions options;
128
0
            DatabaseStatus status;
129
0
            ReadDatabaseArgs(*context.args, options);
130
0
            options.require_existing = true;
131
0
            options.verify = false; // No need to verify, assuming verified earlier in VerifyWallets()
132
0
            bilingual_str error;
133
0
            std::vector<bilingual_str> warnings;
134
0
            std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
135
0
            if (!database && status == DatabaseStatus::FAILED_NOT_FOUND) {
  Branch (135:17): [True: 0, False: 0]
  Branch (135:30): [True: 0, False: 0]
136
0
                continue;
137
0
            }
138
0
            chain.initMessage(_("Loading wallet…"));
139
0
            std::shared_ptr<CWallet> pwallet = database ? CWallet::Create(context, name, std::move(database), options.create_flags, error, warnings) : nullptr;
  Branch (139:48): [True: 0, False: 0]
140
0
            if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
  Branch (140:17): [True: 0, False: 0]
141
0
            if (!pwallet) {
  Branch (141:17): [True: 0, False: 0]
142
0
                chain.initError(error);
143
0
                return false;
144
0
            }
145
146
0
            NotifyWalletLoaded(context, pwallet);
147
0
            AddWallet(context, pwallet);
148
0
        }
149
0
        return true;
150
0
    } catch (const std::runtime_error& e) {
151
0
        chain.initError(Untranslated(e.what()));
152
0
        return false;
153
0
    }
154
0
}
155
156
void StartWallets(WalletContext& context)
157
0
{
158
0
    for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
  Branch (158:50): [True: 0, False: 0]
159
0
        pwallet->postInitProcess();
160
0
    }
161
162
    // Schedule periodic wallet flushes and tx rebroadcasts
163
0
    if (context.args->GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) {
  Branch (163:9): [True: 0, False: 0]
164
0
        context.scheduler->scheduleEvery([&context] { MaybeCompactWalletDB(context); }, 500ms);
165
0
    }
166
0
    context.scheduler->scheduleEvery([&context] { MaybeResendWalletTxs(context); }, 1min);
167
0
}
168
169
void FlushWallets(WalletContext& context)
170
0
{
171
0
    for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
  Branch (171:50): [True: 0, False: 0]
172
0
        pwallet->Flush();
173
0
    }
174
0
}
175
176
void StopWallets(WalletContext& context)
177
0
{
178
0
    for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
  Branch (178:50): [True: 0, False: 0]
179
0
        pwallet->Close();
180
0
    }
181
0
}
182
183
void UnloadWallets(WalletContext& context)
184
0
{
185
0
    auto wallets = GetWallets(context);
186
0
    while (!wallets.empty()) {
  Branch (186:12): [True: 0, False: 0]
187
0
        auto wallet = wallets.back();
188
0
        wallets.pop_back();
189
0
        std::vector<bilingual_str> warnings;
190
0
        RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt, warnings);
191
0
        WaitForDeleteWallet(std::move(wallet));
192
0
    }
193
0
}
194
} // namespace wallet