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