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