Line data Source code
1 : // Copyright (c) 2016-2021 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 <fs.h>
12 : #include <util/translation.h>
13 : #include <util/system.h>
14 : #include <wallet/dump.h>
15 : #include <wallet/salvage.h>
16 : #include <wallet/wallet.h>
17 : #include <wallet/walletutil.h>
18 :
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 dash-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 : static const bool DEFAULT_USE_HD_WALLET{true};
33 :
34 0 : static void WalletCreate(CWallet* wallet_instance, uint64_t wallet_creation_flags)
35 : {
36 0 : LOCK(wallet_instance->cs_wallet);
37 0 : if (gArgs.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET)) {
38 0 : wallet_instance->SetMinVersion(FEATURE_LATEST);
39 0 : } else {
40 0 : wallet_instance->SetMinVersion(FEATURE_COMPRPUBKEY);
41 : }
42 0 : wallet_instance->InitWalletFlags(wallet_creation_flags);
43 :
44 0 : if (!wallet_instance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
45 : // TODO: use here SetupGeneration instead, such as: spk_man->SetupGeneration(false);
46 : // SetupGeneration is not backported yet
47 0 : wallet_instance->SetupLegacyScriptPubKeyMan();
48 0 : auto spk_man = wallet_instance->GetOrCreateLegacyScriptPubKeyMan();
49 0 : if (gArgs.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET)) {
50 0 : spk_man->GenerateNewHDChain(/*secureMnemonic=*/"", /*secureMnemonicPassphrase=*/"");
51 0 : }
52 0 : } else {
53 0 : wallet_instance->SetupDescriptorScriptPubKeyMans("", "");
54 : }
55 :
56 0 : tfm::format(std::cout, "Topping up keypool...\n");
57 0 : wallet_instance->TopUpKeyPool();
58 0 : }
59 :
60 0 : static std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::path& path, const ArgsManager& args, DatabaseOptions options)
61 : {
62 : DatabaseStatus status;
63 0 : bilingual_str error;
64 0 : std::unique_ptr<WalletDatabase> database = MakeDatabase(path, options, status, error);
65 0 : if (!database) {
66 0 : tfm::format(std::cerr, "%s\n", error.original);
67 0 : return nullptr;
68 : }
69 :
70 : // dummy chain interface
71 0 : std::shared_ptr<CWallet> wallet_instance{new CWallet(/*chain=*/nullptr, /*coinjoin_loader=*/nullptr, name, args, std::move(database)), WalletToolReleaseWallet};
72 : DBErrors load_wallet_ret;
73 : try {
74 0 : load_wallet_ret = wallet_instance->LoadWallet();
75 0 : } catch (const std::runtime_error&) {
76 0 : tfm::format(std::cerr, "Error loading %s. Is wallet being used by another process?\n", name);
77 0 : return nullptr;
78 0 : }
79 :
80 0 : if (load_wallet_ret != DBErrors::LOAD_OK) {
81 0 : if (load_wallet_ret == DBErrors::CORRUPT) {
82 0 : tfm::format(std::cerr, "Error loading %s: Wallet corrupted", name);
83 0 : return nullptr;
84 0 : } else if (load_wallet_ret == DBErrors::NONCRITICAL_ERROR) {
85 0 : tfm::format(std::cerr, "Error reading %s! All keys read correctly, but transaction data"
86 : " or address book entries might be missing or incorrect.",
87 0 : name);
88 0 : } else if (load_wallet_ret == DBErrors::TOO_NEW) {
89 0 : tfm::format(std::cerr, "Error loading %s: Wallet requires newer version of %s",
90 0 : name, PACKAGE_NAME);
91 0 : return nullptr;
92 0 : } else if (load_wallet_ret == DBErrors::NEED_REWRITE) {
93 0 : tfm::format(std::cerr, "Wallet needed to be rewritten: restart %s to complete", PACKAGE_NAME);
94 0 : return nullptr;
95 : } else {
96 0 : tfm::format(std::cerr, "Error loading %s", name);
97 0 : return nullptr;
98 : }
99 0 : }
100 :
101 0 : if (options.require_create) WalletCreate(wallet_instance.get(), options.create_flags);
102 :
103 0 : return wallet_instance;
104 0 : }
105 :
106 0 : static void WalletShowInfo(CWallet* wallet_instance)
107 : {
108 : // lock required because of some AssertLockHeld()
109 0 : LOCK(wallet_instance->cs_wallet);
110 :
111 0 : CHDChain hdChainTmp;
112 0 : tfm::format(std::cout, "Wallet info\n===========\n");
113 0 : tfm::format(std::cout, "Name: %s\n", wallet_instance->GetName());
114 0 : tfm::format(std::cout, "Format: %s\n", wallet_instance->GetDatabase().Format());
115 0 : tfm::format(std::cout, "Descriptors: %s\n", wallet_instance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) ? "yes" : "no");
116 0 : tfm::format(std::cout, "Encrypted: %s\n", wallet_instance->IsCrypted() ? "yes" : "no");
117 0 : tfm::format(std::cout, "HD (hd seed available): %s\n", wallet_instance->IsHDEnabled() ? "yes" : "no");
118 0 : tfm::format(std::cout, "Keypool Size: %u\n", wallet_instance->GetKeyPoolSize());
119 0 : tfm::format(std::cout, "Transactions: %zu\n", wallet_instance->mapWallet.size());
120 0 : tfm::format(std::cout, "Address Book: %zu\n", wallet_instance->m_address_book.size());
121 0 : }
122 :
123 0 : bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
124 : {
125 0 : if (args.IsArgSet("-format") && command != "createfromdump") {
126 0 : tfm::format(std::cerr, "The -format option can only be used with the \"createfromdump\" command.\n");
127 0 : return false;
128 : }
129 0 : if (args.IsArgSet("-dumpfile") && command != "dump" && command != "createfromdump") {
130 0 : tfm::format(std::cerr, "The -dumpfile option can only be used with the \"dump\" and \"createfromdump\" commands.\n");
131 0 : return false;
132 : }
133 0 : if (args.IsArgSet("-descriptors") && command != "create") {
134 0 : tfm::format(std::cerr, "The -descriptors option can only be used with the 'create' command.\n");
135 0 : return false;
136 : }
137 0 : if (args.IsArgSet("-legacy") && command != "create") {
138 0 : tfm::format(std::cerr, "The -legacy option can only be used with the 'create' command.\n");
139 0 : return false;
140 : }
141 0 : if (command == "create" && !args.IsArgSet("-wallet")) {
142 0 : tfm::format(std::cerr, "Wallet name must be provided when creating a new wallet.\n");
143 0 : return false;
144 : }
145 0 : const std::string name = args.GetArg("-wallet", "");
146 0 : const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(name));
147 :
148 0 : if (command == "create") {
149 0 : DatabaseOptions options;
150 0 : ReadDatabaseArgs(args, options);
151 0 : options.require_create = true;
152 : // If -legacy is set, use it. Otherwise default to false.
153 0 : bool make_legacy = args.GetBoolArg("-legacy", false);
154 : // If neither -legacy nor -descriptors is set, default to true. If -descriptors is set, use its value.
155 0 : bool make_descriptors = (!args.IsArgSet("-descriptors") && !args.IsArgSet("-legacy")) || (args.IsArgSet("-descriptors") && args.GetBoolArg("-descriptors", true));
156 0 : if (make_legacy && make_descriptors) {
157 0 : tfm::format(std::cerr, "Only one of -legacy or -descriptors can be set to true, not both\n");
158 0 : return false;
159 : }
160 0 : if (!make_legacy && !make_descriptors) {
161 0 : tfm::format(std::cerr, "One of -legacy or -descriptors must be set to true (or omitted)\n");
162 0 : return false;
163 : }
164 0 : if (make_descriptors) {
165 0 : options.create_flags |= WALLET_FLAG_DESCRIPTORS;
166 0 : options.require_format = DatabaseFormat::SQLITE;
167 0 : }
168 :
169 0 : const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, args, options);
170 0 : if (wallet_instance) {
171 0 : WalletShowInfo(wallet_instance.get());
172 0 : wallet_instance->Close();
173 0 : }
174 0 : } else if (command == "info") {
175 0 : DatabaseOptions options;
176 0 : ReadDatabaseArgs(args, options);
177 0 : options.require_existing = true;
178 0 : const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, args, options);
179 0 : if (!wallet_instance) return false;
180 0 : WalletShowInfo(wallet_instance.get());
181 0 : wallet_instance->Close();
182 0 : } else if (command == "salvage") {
183 : #ifdef USE_BDB
184 0 : bilingual_str error;
185 0 : std::vector<bilingual_str> warnings;
186 0 : bool ret = RecoverDatabaseFile(args, path, error, warnings);
187 0 : if (!ret) {
188 0 : for (const auto& warning : warnings) {
189 0 : tfm::format(std::cerr, "%s\n", warning.original);
190 : }
191 0 : if (!error.empty()) {
192 0 : tfm::format(std::cerr, "%s\n", error.original);
193 0 : }
194 0 : }
195 0 : return ret;
196 : #else
197 : tfm::format(std::cerr, "Salvage command is not available as BDB support is not compiled");
198 : return false;
199 : #endif
200 0 : } else if (command == "wipetxes") {
201 : #ifdef USE_BDB
202 0 : DatabaseOptions options;
203 0 : options.require_existing = true;
204 0 : const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, args, options);
205 0 : if (wallet_instance == nullptr) return false;
206 :
207 0 : std::vector<uint256> vHash;
208 0 : std::vector<uint256> vHashOut;
209 :
210 0 : LOCK(wallet_instance->cs_wallet);
211 :
212 0 : for (auto& [txid, _] : wallet_instance->mapWallet) {
213 0 : vHash.push_back(txid);
214 : }
215 :
216 0 : if (wallet_instance->ZapSelectTx(vHash, vHashOut) != DBErrors::LOAD_OK) {
217 0 : tfm::format(std::cerr, "Could not properly delete transactions");
218 0 : wallet_instance->Close();
219 0 : return false;
220 : }
221 :
222 0 : wallet_instance->Close();
223 0 : return vHashOut.size() == vHash.size();
224 : #else
225 : tfm::format(std::cerr, "Wipetxes command is not available as BDB support is not compiled");
226 : return false;
227 : #endif
228 0 : } else if (command == "dump") {
229 0 : DatabaseOptions options;
230 0 : ReadDatabaseArgs(args, options);
231 0 : options.require_existing = true;
232 : DatabaseStatus status;
233 0 : bilingual_str error;
234 0 : std::unique_ptr<WalletDatabase> database = MakeDatabase(path, options, status, error);
235 0 : if (!database) {
236 0 : tfm::format(std::cerr, "%s\n", error.original);
237 0 : return false;
238 : }
239 :
240 0 : bool ret = DumpWallet(args, *database, error);
241 0 : if (!ret && !error.empty()) {
242 0 : tfm::format(std::cerr, "%s\n", error.original);
243 0 : return ret;
244 : }
245 0 : tfm::format(std::cout, "The dumpfile may contain private keys. To ensure the safety of your Bitcoin, do not share the dumpfile.\n");
246 0 : return ret;
247 0 : } else if (command == "createfromdump") {
248 0 : bilingual_str error;
249 0 : std::vector<bilingual_str> warnings;
250 0 : bool ret = CreateFromDump(args, name, path, error, warnings);
251 0 : for (const auto& warning : warnings) {
252 0 : tfm::format(std::cout, "%s\n", warning.original);
253 : }
254 0 : if (!ret && !error.empty()) {
255 0 : tfm::format(std::cerr, "%s\n", error.original);
256 0 : }
257 0 : return ret;
258 0 : } else {
259 0 : tfm::format(std::cerr, "Invalid command: %s\n", command);
260 0 : return false;
261 : }
262 :
263 0 : return true;
264 0 : }
265 : } // namespace WalletTool
266 : } // namespace wallet
|