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