/ src / wallet / wallettool.cpp
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