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