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