wallettool.cpp raw

   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      {
  97          std::vector<std::string> details;
  98          if (!args.CheckCommandOptions(command, &details)) {
  99              tfm::format(std::cerr, "Error: Invalid arguments provided:\n%s\n", util::MakeUnorderedList(details));
 100              return false;
 101          }
 102      }
 103      if ((command == "create" || command == "createfromdump") && !args.IsArgSet("-wallet")) {
 104          tfm::format(std::cerr, "Wallet name must be provided when creating a new wallet.\n");
 105          return false;
 106      }
 107      const std::string name = args.GetArg("-wallet", "");
 108      util::Result<fs::path> path_res = GetWalletPath(name);
 109      if (!path_res) {
 110          tfm::format(std::cerr, "%s\n", util::ErrorString(path_res).original);
 111          return false;
 112      }
 113      const fs::path& path = *path_res;
 114  
 115      if (command == "create") {
 116          if (name.empty()) {
 117              tfm::format(std::cerr, "Wallet name cannot be empty\n");
 118              return false;
 119          }
 120          DatabaseOptions options;
 121          ReadDatabaseArgs(args, options);
 122          options.require_create = true;
 123          options.create_flags |= WALLET_FLAG_DESCRIPTORS;
 124          options.require_format = DatabaseFormat::SQLITE;
 125  
 126          const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
 127          if (wallet_instance) {
 128              WalletShowInfo(wallet_instance.get());
 129              wallet_instance->Close();
 130          }
 131      } else if (command == "info") {
 132          DatabaseOptions options;
 133          ReadDatabaseArgs(args, options);
 134          options.require_existing = true;
 135          const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
 136          if (!wallet_instance) return false;
 137          WalletShowInfo(wallet_instance.get());
 138          wallet_instance->Close();
 139      } else if (command == "dump") {
 140          DatabaseOptions options;
 141          ReadDatabaseArgs(args, options);
 142          options.require_existing = true;
 143          DatabaseStatus status;
 144  
 145          if (IsBDBFile(BDBDataFile(path))) {
 146              options.require_format = DatabaseFormat::BERKELEY_RO;
 147          }
 148  
 149          bilingual_str error;
 150          std::unique_ptr<WalletDatabase> database = MakeDatabase(path, options, status, error);
 151          if (!database) {
 152              tfm::format(std::cerr, "%s\n", error.original);
 153              return false;
 154          }
 155  
 156          bool ret = DumpWallet(args, *database, error);
 157          if (!ret && !error.empty()) {
 158              tfm::format(std::cerr, "%s\n", error.original);
 159              return ret;
 160          }
 161          tfm::format(std::cout, "The dumpfile may contain private keys. To ensure the safety of your Bitcoin, do not share the dumpfile.\n");
 162          return ret;
 163      } else if (command == "createfromdump") {
 164          bilingual_str error;
 165          std::vector<bilingual_str> warnings;
 166          bool ret = CreateFromDump(args, name, path, error, warnings);
 167          for (const auto& warning : warnings) {
 168              tfm::format(std::cout, "%s\n", warning.original);
 169          }
 170          if (!ret && !error.empty()) {
 171              tfm::format(std::cerr, "%s\n", error.original);
 172          }
 173          return ret;
 174      } else {
 175          tfm::format(std::cerr, "Invalid command: %s\n", command);
 176          return false;
 177      }
 178  
 179      return true;
 180  }
 181  } // namespace WalletTool
 182  } // namespace wallet
 183