Branch data Line data Source code
1 : : // Copyright (c) 2016-2022 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 : : #if defined(HAVE_CONFIG_H)
6 : : #include <config/bitcoin-config.h>
7 : : #endif
8 : :
9 : : #include <wallet/wallettool.h>
10 : :
11 : : #include <common/args.h>
12 : : #include <util/fs.h>
13 : : #include <util/translation.h>
14 : : #include <wallet/dump.h>
15 : : #include <wallet/salvage.h>
16 : : #include <wallet/wallet.h>
17 : 0 : #include <wallet/walletutil.h>
18 : 0 :
19 : : namespace wallet {
20 : : namespace WalletTool {
21 : :
22 : : // The standard wallet deleter function blocks on the validation interface
23 : : // queue, which doesn't exist for the bitcoin-wallet. Define our own
24 : : // deleter here.
25 : 0 : static void WalletToolReleaseWallet(CWallet* wallet)
26 : : {
27 : 0 : wallet->WalletLogPrintf("Releasing wallet\n");
28 : 0 : wallet->Close();
29 : 0 : delete wallet;
30 : 0 : }
31 : :
32 : 0 : static void WalletCreate(CWallet* wallet_instance, uint64_t wallet_creation_flags)
33 : : {
34 : 0 : LOCK(wallet_instance->cs_wallet);
35 : :
36 : 0 : wallet_instance->SetMinVersion(FEATURE_LATEST);
37 : 0 : wallet_instance->InitWalletFlags(wallet_creation_flags);
38 : :
39 : 0 : if (!wallet_instance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
40 : 0 : auto spk_man = wallet_instance->GetOrCreateLegacyScriptPubKeyMan();
41 : 0 : spk_man->SetupGeneration(false);
42 : 0 : } else {
43 : 0 : wallet_instance->SetupDescriptorScriptPubKeyMans();
44 : : }
45 : :
46 : 0 : tfm::format(std::cout, "Topping up keypool...\n");
47 : 0 : wallet_instance->TopUpKeyPool();
48 : 0 : }
49 : :
50 : 0 : static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::path& path, DatabaseOptions options)
51 : : {
52 : : DatabaseStatus status;
53 : 0 : bilingual_str error;
54 : 0 : std::unique_ptr<WalletDatabase> database = MakeDatabase(path, options, status, error);
55 : 0 : if (!database) {
56 : 0 : tfm::format(std::cerr, "%s\n", error.original);
57 : 0 : return nullptr;
58 : : }
59 : :
60 : : // dummy chain interface
61 : 0 : std::shared_ptr<CWallet> wallet_instance{new CWallet(/*chain=*/nullptr, name, std::move(database)), WalletToolReleaseWallet};
62 : : DBErrors load_wallet_ret;
63 : : try {
64 : 0 : load_wallet_ret = wallet_instance->LoadWallet();
65 : 0 : } catch (const std::runtime_error&) {
66 : 0 : tfm::format(std::cerr, "Error loading %s. Is wallet being used by another process?\n", name);
67 : 0 : return nullptr;
68 : 0 : }
69 : :
70 : 0 : if (load_wallet_ret != DBErrors::LOAD_OK) {
71 : 0 : wallet_instance = nullptr;
72 : 0 : if (load_wallet_ret == DBErrors::CORRUPT) {
73 : 0 : tfm::format(std::cerr, "Error loading %s: Wallet corrupted", name);
74 : 0 : return nullptr;
75 : 0 : } else if (load_wallet_ret == DBErrors::NONCRITICAL_ERROR) {
76 : 0 : tfm::format(std::cerr, "Error reading %s! All keys read correctly, but transaction data"
77 : : " or address book entries might be missing or incorrect.",
78 : 0 : name);
79 : 0 : } else if (load_wallet_ret == DBErrors::TOO_NEW) {
80 : 0 : tfm::format(std::cerr, "Error loading %s: Wallet requires newer version of %s",
81 : 0 : name, PACKAGE_NAME);
82 : 0 : return nullptr;
83 : 0 : } else if (load_wallet_ret == DBErrors::NEED_REWRITE) {
84 : 0 : tfm::format(std::cerr, "Wallet needed to be rewritten: restart %s to complete", PACKAGE_NAME);
85 : 0 : return nullptr;
86 : 0 : } else if (load_wallet_ret == DBErrors::NEED_RESCAN) {
87 : 0 : tfm::format(std::cerr, "Error reading %s! Some transaction data might be missing or"
88 : : " incorrect. Wallet requires a rescan.",
89 : 0 : name);
90 : 0 : } else {
91 : 0 : tfm::format(std::cerr, "Error loading %s", name);
92 : 0 : return nullptr;
93 : : }
94 : 0 : }
95 : :
96 : 0 : if (options.require_create) WalletCreate(wallet_instance.get(), options.create_flags);
97 : :
98 : 0 : return wallet_instance;
99 : 0 : }
100 : :
101 : 0 : static void WalletShowInfo(CWallet* wallet_instance)
102 : : {
103 : 0 : LOCK(wallet_instance->cs_wallet);
104 : :
105 : 0 : tfm::format(std::cout, "Wallet info\n===========\n");
106 : 0 : tfm::format(std::cout, "Name: %s\n", wallet_instance->GetName());
107 : 0 : tfm::format(std::cout, "Format: %s\n", wallet_instance->GetDatabase().Format());
108 : 0 : tfm::format(std::cout, "Descriptors: %s\n", wallet_instance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) ? "yes" : "no");
109 : 0 : tfm::format(std::cout, "Encrypted: %s\n", wallet_instance->IsCrypted() ? "yes" : "no");
110 : 0 : tfm::format(std::cout, "HD (hd seed available): %s\n", wallet_instance->IsHDEnabled() ? "yes" : "no");
111 : 0 : tfm::format(std::cout, "Keypool Size: %u\n", wallet_instance->GetKeyPoolSize());
112 : 0 : tfm::format(std::cout, "Transactions: %zu\n", wallet_instance->mapWallet.size());
113 : 0 : tfm::format(std::cout, "Address Book: %zu\n", wallet_instance->m_address_book.size());
114 : 0 : }
115 : :
116 : 0 : bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
117 : : {
118 : 0 : if (args.IsArgSet("-format") && command != "createfromdump") {
119 : 0 : tfm::format(std::cerr, "The -format option can only be used with the \"createfromdump\" command.\n");
120 : 0 : return false;
121 : : }
122 : 0 : if (args.IsArgSet("-dumpfile") && command != "dump" && command != "createfromdump") {
123 : 0 : tfm::format(std::cerr, "The -dumpfile option can only be used with the \"dump\" and \"createfromdump\" commands.\n");
124 : 0 : return false;
125 : : }
126 : 0 : if (args.IsArgSet("-descriptors") && command != "create") {
127 : 0 : tfm::format(std::cerr, "The -descriptors option can only be used with the 'create' command.\n");
128 : 0 : return false;
129 : : }
130 : 0 : if (args.IsArgSet("-legacy") && command != "create") {
131 : 0 : tfm::format(std::cerr, "The -legacy option can only be used with the 'create' command.\n");
132 : 0 : return false;
133 : : }
134 : 0 : if (command == "create" && !args.IsArgSet("-wallet")) {
135 : 0 : tfm::format(std::cerr, "Wallet name must be provided when creating a new wallet.\n");
136 : 0 : return false;
137 : : }
138 : 0 : const std::string name = args.GetArg("-wallet", "");
139 : 0 : const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(name));
140 : :
141 : 0 : if (command == "create") {
142 : 0 : DatabaseOptions options;
143 : 0 : ReadDatabaseArgs(args, options);
144 : 0 : options.require_create = true;
145 : : // If -legacy is set, use it. Otherwise default to false.
146 : 0 : bool make_legacy = args.GetBoolArg("-legacy", false);
147 : : // If neither -legacy nor -descriptors is set, default to true. If -descriptors is set, use its value.
148 : 0 : bool make_descriptors = (!args.IsArgSet("-descriptors") && !args.IsArgSet("-legacy")) || (args.IsArgSet("-descriptors") && args.GetBoolArg("-descriptors", true));
149 : 0 : if (make_legacy && make_descriptors) {
150 : 0 : tfm::format(std::cerr, "Only one of -legacy or -descriptors can be set to true, not both\n");
151 : 0 : return false;
152 : : }
153 : 0 : if (!make_legacy && !make_descriptors) {
154 : 0 : tfm::format(std::cerr, "One of -legacy or -descriptors must be set to true (or omitted)\n");
155 : 0 : return false;
156 : : }
157 : 0 : if (make_descriptors) {
158 : 0 : options.create_flags |= WALLET_FLAG_DESCRIPTORS;
159 : 0 : options.require_format = DatabaseFormat::SQLITE;
160 : 0 : }
161 : :
162 : 0 : const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
163 : 0 : if (wallet_instance) {
164 : 0 : WalletShowInfo(wallet_instance.get());
165 : 0 : wallet_instance->Close();
166 : 0 : }
167 : 0 : } else if (command == "info") {
168 : 0 : DatabaseOptions options;
169 : 0 : ReadDatabaseArgs(args, options);
170 : 0 : options.require_existing = true;
171 : 0 : const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
172 : 0 : if (!wallet_instance) return false;
173 : 0 : WalletShowInfo(wallet_instance.get());
174 : 0 : wallet_instance->Close();
175 : 0 : } else if (command == "salvage") {
176 : : #ifdef USE_BDB
177 : : bilingual_str error;
178 : : std::vector<bilingual_str> warnings;
179 : : bool ret = RecoverDatabaseFile(args, path, error, warnings);
180 : : if (!ret) {
181 : : for (const auto& warning : warnings) {
182 : : tfm::format(std::cerr, "%s\n", warning.original);
183 : : }
184 : : if (!error.empty()) {
185 : : tfm::format(std::cerr, "%s\n", error.original);
186 : : }
187 : : }
188 : : return ret;
189 : : #else
190 : 0 : tfm::format(std::cerr, "Salvage command is not available as BDB support is not compiled");
191 : 0 : return false;
192 : : #endif
193 : 0 : } else if (command == "dump") {
194 : 0 : DatabaseOptions options;
195 : 0 : ReadDatabaseArgs(args, options);
196 : 0 : options.require_existing = true;
197 : 0 : const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
198 : 0 : if (!wallet_instance) return false;
199 : 0 : bilingual_str error;
200 : 0 : bool ret = DumpWallet(args, *wallet_instance, error);
201 : 0 : if (!ret && !error.empty()) {
202 : 0 : tfm::format(std::cerr, "%s\n", error.original);
203 : 0 : return ret;
204 : : }
205 : 0 : tfm::format(std::cout, "The dumpfile may contain private keys. To ensure the safety of your Bitcoin, do not share the dumpfile.\n");
206 : 0 : return ret;
207 : 0 : } else if (command == "createfromdump") {
208 : 0 : bilingual_str error;
209 : 0 : std::vector<bilingual_str> warnings;
210 : 0 : bool ret = CreateFromDump(args, name, path, error, warnings);
211 : 0 : for (const auto& warning : warnings) {
212 : 0 : tfm::format(std::cout, "%s\n", warning.original);
213 : : }
214 : 0 : if (!ret && !error.empty()) {
215 : 0 : tfm::format(std::cerr, "%s\n", error.original);
216 : 0 : }
217 : 0 : return ret;
218 : 0 : } else {
219 : 0 : tfm::format(std::cerr, "Invalid command: %s\n", command);
220 : 0 : return false;
221 : : }
222 : :
223 : 0 : return true;
224 : 0 : }
225 : : } // namespace WalletTool
226 : : } // namespace wallet
|