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