From cc1f8db72584b2880f1d1a7849f624084c6e5647 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Thu, 31 Jan 2019 11:07:45 -0500 Subject: [PATCH] Merge #13926: [Tools] bitcoin-wallet - a tool for creating and managing wallets offline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 3c3e31c3a4 [tests] Add wallet-tool test (João Barbosa) 49d2374acf [tools] Add wallet inspection and modification tool (Jonas Schnelli) Pull request description: Adds an offline tool `bitcoin-wallet-tool` for wallet creation and maintenance. Currently this tool can create a new wallet file, display information on an existing wallet, and run the salvage and zapwallettxes maintenance tasks on an existing wallet. It can later be extended to support other common wallet maintenance tasks. Doing wallet maintenance tasks in an offline tool makes much more sense (and is potentially safer) than having to spin up a full node. Tree-SHA512: 75a28b8a58858d9d76c7532db40eacdefc5714ea5aab536fb1dc9756e2f7d750d69d68d59c50a68e633ce38fb5b8c3e3d4880db30fe01561e07ce58d42bceb2b --- .gitignore | 1 + configure.ac | 20 ++- src/Makefile.am | 45 ++++++ src/dash-wallet-res.rc | 35 +++++ src/dash-wallet.cpp | 118 +++++++++++++++ src/wallet/crypter.cpp | 4 +- src/wallet/crypter.h | 2 +- src/wallet/wallet.cpp | 9 +- src/wallet/wallet.h | 2 +- src/wallet/wallettool.cpp | 140 ++++++++++++++++++ src/wallet/wallettool.h | 20 +++ .../test_framework/test_framework.py | 1 + test/functional/test_runner.py | 3 +- test/functional/tool_wallet.py | 114 ++++++++++++++ 14 files changed, 502 insertions(+), 12 deletions(-) create mode 100644 src/dash-wallet-res.rc create mode 100644 src/dash-wallet.cpp create mode 100644 src/wallet/wallettool.cpp create mode 100644 src/wallet/wallettool.h create mode 100755 test/functional/tool_wallet.py diff --git a/.gitignore b/.gitignore index 40cdce86e0..57c426256b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ src/dash src/dashd src/dash-cli src/dash-tx +src/dash-wallet src/test/fuzz !src/test/fuzz/*.* src/test/test_dash diff --git a/configure.ac b/configure.ac index 7a373f8e28..9cc7ac018c 100644 --- a/configure.ac +++ b/configure.ac @@ -18,6 +18,7 @@ BITCOIN_DAEMON_NAME=dashd BITCOIN_GUI_NAME=dash-qt BITCOIN_CLI_NAME=dash-cli BITCOIN_TX_NAME=dash-tx +BITCOIN_WALLET_TOOL_NAME=dash-wallet dnl Unless the user specified ARFLAGS, force it to be cr AC_ARG_VAR(ARFLAGS, [Flags for the archiver, defaults to if not set]) @@ -537,7 +538,7 @@ CPPFLAGS="$CPPFLAGS -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS" AC_ARG_WITH([utils], [AS_HELP_STRING([--with-utils], - [build dash-cli dash-tx (default=yes)])], + [build dash-cli dash-tx dash-wallet (default=yes)])], [build_bitcoin_utils=$withval], [build_bitcoin_utils=yes]) @@ -553,6 +554,12 @@ AC_ARG_ENABLE([util-tx], [build_bitcoin_tx=$enableval], [build_bitcoin_tx=$build_bitcoin_utils]) +AC_ARG_ENABLE([util-wallet], + [AS_HELP_STRING([--enable-util-wallet], + [build dash-wallet])], + [build_bitcoin_wallet=$enableval], + [build_bitcoin_wallet=$build_bitcoin_utils]) + AC_ARG_WITH([libs], [AS_HELP_STRING([--with-libs], [build libraries (default=yes)])], @@ -1161,7 +1168,7 @@ if test x$suppress_external_warnings != xno ; then QT_TEST_INCLUDES=SUPPRESS_WARNINGS($QT_TEST_INCLUDES) fi -if test x$build_bitcoin_cli$build_bitcoin_tx$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnononononono; then +if test x$build_bitcoin_wallet$build_bitcoin_cli$build_bitcoin_tx$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononononono; then use_boost=no else use_boost=yes @@ -1387,7 +1394,7 @@ AC_CHECK_LIB([gmp], [__gmpz_init],GMP_LIBS=-lgmp, AC_MSG_ERROR(libgmp missing)) dnl check if immer headers-only library is present AC_CHECK_HEADER([immer/map.hpp],, AC_MSG_ERROR(immer map headers missing)) -if test x$build_bitcoin_cli$build_bitcoin_tx$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnononononono; then +if test x$build_bitcoin_wallet$build_bitcoin_cli$build_bitcoin_tx$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononononono; then need_bundled_univalue=no else @@ -1449,6 +1456,10 @@ AC_MSG_CHECKING([whether to build dash-tx]) AM_CONDITIONAL([BUILD_BITCOIN_TX], [test x$build_bitcoin_tx = xyes]) AC_MSG_RESULT($build_bitcoin_tx) +AC_MSG_CHECKING([whether to build dash-wallet]) +AM_CONDITIONAL([BUILD_BITCOIN_WALLET], [test x$build_bitcoin_wallet = xyes]) +AC_MSG_RESULT($build_bitcoin_wallet) + AC_MSG_CHECKING([whether to build libraries]) AM_CONDITIONAL([BUILD_BITCOIN_LIBS], [test x$build_bitcoin_libs = xyes]) if test x$build_bitcoin_libs = xyes; then @@ -1602,7 +1613,7 @@ else fi AC_MSG_RESULT($dsymutil_needs_flat) -if test x$build_bitcoin_cli$build_bitcoin_tx$build_bitcoin_libs$build_bitcoind$bitcoin_enable_qt$use_bench$use_tests = xnonononononono; then +if test x$build_bitcoin_wallet$build_bitcoin_cli$build_bitcoin_tx$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononononono; then AC_MSG_ERROR([No targets! Please specify at least one of: --with-utils --with-libs --with-daemon --with-gui --enable-bench or --enable-tests]) fi @@ -1651,6 +1662,7 @@ AC_SUBST(BITCOIN_DAEMON_NAME) AC_SUBST(BITCOIN_GUI_NAME) AC_SUBST(BITCOIN_CLI_NAME) AC_SUBST(BITCOIN_TX_NAME) +AC_SUBST(BITCOIN_WALLET_TOOL_NAME) AC_SUBST(RELDFLAGS) AC_SUBST(DEBUG_CPPFLAGS) diff --git a/src/Makefile.am b/src/Makefile.am index e14ea60f29..2c47470262 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -64,6 +64,7 @@ LIBBITCOINCONSENSUS=libdashconsensus.la endif if ENABLE_WALLET LIBBITCOIN_WALLET=libdash_wallet.a +LIBBITCOIN_WALLET_TOOL=libdash_wallet_tool.a endif LIBBITCOIN_CRYPTO= $(LIBBITCOIN_CRYPTO_BASE) @@ -93,6 +94,7 @@ EXTRA_LIBRARIES += \ $(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_CLI) \ $(LIBBITCOIN_WALLET) \ + $(LIBBITCOIN_WALLET_TOOL) \ $(LIBBITCOIN_ZMQ) lib_LTLIBRARIES = $(LIBBITCOINCONSENSUS) @@ -112,6 +114,11 @@ endif if BUILD_BITCOIN_TX bin_PROGRAMS += dash-tx endif +if ENABLE_WALLET +if BUILD_BITCOIN_WALLET + bin_PROGRAMS += dash-wallet +endif +endif .PHONY: FORCE check-symbols check-security # dash core # @@ -305,6 +312,7 @@ BITCOIN_CORE_H = \ wallet/rpcwallet.h \ wallet/wallet.h \ wallet/walletdb.h \ + wallet/wallettool.h \ wallet/walletutil.h \ wallet/coinselection.h \ warnings.h \ @@ -462,6 +470,12 @@ libdash_wallet_a_SOURCES = \ wallet/coinselection.cpp \ $(BITCOIN_CORE_H) +libdash_wallet_tool_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +libdash_wallet_tool_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +libdash_wallet_tool_a_SOURCES = \ + wallet/wallettool.cpp \ + $(BITCOIN_CORE_H) + # crypto primitives library crypto_libdash_crypto_base_a_CPPFLAGS = $(AM_CPPFLAGS) $(PIC_FLAGS) crypto_libdash_crypto_base_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) $(PIC_FLAGS) @@ -739,6 +753,37 @@ dash_tx_LDADD = \ dash_tx_LDADD += $(BACKTRACE_LIB) $(BOOST_LIBS) $(CRYPTO_LIBS) $(BLS_LIBS) $(GMP_LIBS) # +# dash-wallet binary # +dash_wallet_SOURCES = dash-wallet.cpp +dash_wallet_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +dash_wallet_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +dash_wallet_LDFLAGS = $(LDFLAGS_WRAP_EXCEPTIONS) $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) + +if TARGET_WINDOWS +dash_wallet_SOURCES += dash-wallet-res.rc +endif + +# Libraries below may be listed more than once to resolve circular dependencies (see +# https://eli.thegreenplace.net/2013/07/09/library-order-in-static-linking#circular-dependency) +dash_wallet_LDADD = \ + $(LIBBITCOIN_WALLET_TOOL) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_WALLET) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_ZMQ) \ + $(LIBLEVELDB) \ + $(LIBLEVELDB_SSE42) \ + $(LIBMEMENV) \ + $(LIBSECP256K1) \ + $(LIBUNIVALUE) + +dash_wallet_LDADD += $(BACKTRACE_LIB) $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(ZMQ_LIBS) $(BLS_LIBS) $(GMP_LIBS) +# + # dashconsensus library # if BUILD_BITCOIN_LIBS include_HEADERS = script/dashconsensus.h diff --git a/src/dash-wallet-res.rc b/src/dash-wallet-res.rc new file mode 100644 index 0000000000..31d6b6e7e9 --- /dev/null +++ b/src/dash-wallet-res.rc @@ -0,0 +1,35 @@ +#include // needed for VERSIONINFO +#include "clientversion.h" // holds the needed client version information + +#define VER_PRODUCTVERSION CLIENT_VERSION_MAJOR,CLIENT_VERSION_MINOR,CLIENT_VERSION_REVISION,CLIENT_VERSION_BUILD +#define VER_PRODUCTVERSION_STR STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) "." STRINGIZE(CLIENT_VERSION_REVISION) "." STRINGIZE(CLIENT_VERSION_BUILD) +#define VER_FILEVERSION VER_PRODUCTVERSION +#define VER_FILEVERSION_STR VER_PRODUCTVERSION_STR + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" // U.S. English - multilingual (hex) + BEGIN + VALUE "CompanyName", "Dash Core" + VALUE "FileDescription", "dash-wallet (CLI tool for " PACKAGE_NAME " wallets)" + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", "dash-wallet" + VALUE "LegalCopyright", COPYRIGHT_STR + VALUE "LegalTrademarks1", "Distributed under the MIT software license, see the accompanying file COPYING or http://www.opensource.org/licenses/mit-license.php." + VALUE "OriginalFilename", "dash-wallet.exe" + VALUE "ProductName", "dash-wallet" + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1252 // language neutral - multilingual (decimal) + END +END diff --git a/src/dash-wallet.cpp b/src/dash-wallet.cpp new file mode 100644 index 0000000000..697839197c --- /dev/null +++ b/src/dash-wallet.cpp @@ -0,0 +1,118 @@ +// Copyright (c) 2016-2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#if defined(HAVE_CONFIG_H) +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +const std::function G_TRANSLATION_FUN = nullptr; + +static void SetupWalletToolArgs() +{ + SetupChainParamsBaseOptions(); + + gArgs.AddArg("-?", "This help message", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + gArgs.AddArg("-datadir=", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + gArgs.AddArg("-wallet=", "Specify wallet name", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + gArgs.AddArg("-debug=", "Output debugging information (default: 0).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise.", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + + gArgs.AddArg("info", "Get wallet info", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS); + gArgs.AddArg("create", "Create new wallet file", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS); + + // Hidden + gArgs.AddArg("-h", "", ArgsManager::ALLOW_ANY, OptionsCategory::HIDDEN); + gArgs.AddArg("-help", "", ArgsManager::ALLOW_ANY, OptionsCategory::HIDDEN); +} + +static bool WalletAppInit(int argc, char* argv[]) +{ + SetupWalletToolArgs(); + std::string error_message; + if (!gArgs.ParseParameters(argc, argv, error_message)) { + tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error_message); + return false; + } + if (argc < 2 || HelpRequested(gArgs)) { + std::string usage = strprintf("%s dash-wallet version", PACKAGE_NAME) + " " + FormatFullVersion() + "\n\n" + + "wallet-tool is an offline tool for creating and interacting with Dash Core wallet files.\n" + + "By default wallet-tool will act on wallets in the default mainnet wallet directory in the datadir.\n" + + "To change the target wallet, use the -datadir, -wallet and -testnet/-regtest arguments.\n\n" + + "Usage:\n" + + " dash-wallet [options] \n\n" + + gArgs.GetHelpMessage(); + + tfm::format(std::cout, "%s", usage); + return false; + } + + // check for printtoconsole, allow -debug + LogInstance().m_print_to_console = gArgs.GetBoolArg("-printtoconsole", gArgs.GetBoolArg("-debug", false)); + + if (!fs::is_directory(GetDataDir(false))) { + tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "")); + return false; + } + // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause) + SelectParams(gArgs.GetChainName()); + + return true; +} + +int main(int argc, char* argv[]) +{ +#ifdef WIN32 + util::WinCmdLineArgs winArgs; + std::tie(argc, argv) = winArgs.get(); +#endif + SetupEnvironment(); + RandomInit(); + try { + if (!WalletAppInit(argc, argv)) return EXIT_FAILURE; + } catch (...) { + PrintExceptionContinue(std::current_exception(), "WalletAppInit()"); + return EXIT_FAILURE; + } + + std::string method {}; + for(int i = 1; i < argc; ++i) { + if (!IsSwitchChar(argv[i][0])) { + if (!method.empty()) { + tfm::format(std::cerr, "Error: two methods provided (%s and %s). Only one method should be provided.\n", method, argv[i]); + return EXIT_FAILURE; + } + method = argv[i]; + } + } + + if (method.empty()) { + tfm::format(std::cerr, "No method provided. Run `dash-wallet -help` for valid methods.\n"); + return EXIT_FAILURE; + } + + // A name must be provided when creating a file + if (method == "create" && !gArgs.IsArgSet("-wallet")) { + tfm::format(std::cerr, "Wallet name must be provided when creating a new wallet.\n"); + return EXIT_FAILURE; + } + + std::string name = gArgs.GetArg("-wallet", ""); + + ECCVerifyHandle globalVerifyHandle; + ECC_Start(); + if (!WalletTool::ExecuteWalletToolFunc(method, name)) + return EXIT_FAILURE; + ECC_Stop(); + return EXIT_SUCCESS; +} diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index 45914ce1bd..7d2f9780cc 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -242,7 +242,7 @@ bool CCryptoKeyStore::Lock(bool fAllowMixing) return true; } -bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn, bool fForMixingOnly) +bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn, bool fForMixingOnly, bool accept_no_keys) { { LOCK(cs_KeyStore); @@ -271,7 +271,7 @@ bool CCryptoKeyStore::Unlock(const CKeyingMaterial& vMasterKeyIn, bool fForMixin LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.\n"); throw std::runtime_error("Error unlocking wallet: some keys decrypt but not all. Your wallet file may be corrupt."); } - if (keyFail || (!keyPass && cryptedHDChain.IsNull())) + if (keyFail || (!keyPass && cryptedHDChain.IsNull() && !accept_no_keys)) return false; vMasterKey = vMasterKeyIn; diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index 9f8fb3b4c1..aac08a379a 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -140,7 +140,7 @@ protected: bool SetHDChain(const CHDChain& chain); bool SetCryptedHDChain(const CHDChain& chain); - bool Unlock(const CKeyingMaterial& vMasterKeyIn, bool fForMixingOnly = false); + bool Unlock(const CKeyingMaterial& vMasterKeyIn, bool fForMixingOnly = false, bool accept_no_keys = false); CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore); public: diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 87f187ea2a..949b24513a 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -579,7 +579,7 @@ bool CWallet::LoadWatchOnly(const CScript &dest) return CCryptoKeyStore::AddWatchOnly(dest); } -bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool fForMixingOnly) +bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool fForMixingOnly, bool accept_no_keys) { SecureString strWalletPassphraseFinal; @@ -609,7 +609,7 @@ bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool fForMixingOnl return false; if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey)) continue; // try another master key - if (CCryptoKeyStore::Unlock(_vMasterKey, fForMixingOnly)) { + if (CCryptoKeyStore::Unlock(_vMasterKey, fForMixingOnly, accept_no_keys)) { if(nWalletBackups == -2) { TopUpKeyPool(); WalletLogPrintf("Keypool replenished, re-initializing automatic backups.\n"); @@ -4154,7 +4154,10 @@ bool CWallet::NewKeyPool() batch.ErasePool(nIndex); } setExternalKeyPool.clear(); - coinJoinClientManagers.at(GetName())->StopMixing(); + auto it = coinJoinClientManagers.find(GetName()); + if (it != coinJoinClientManagers.end()) { + it->second->StopMixing(); + } nKeysLeftSinceAutoBackup = 0; m_pool_key_to_index.clear(); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 7d9cfd7fff..637e07ac65 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -979,7 +979,7 @@ public: //! Holds a timestamp at which point the wallet is scheduled (externally) to be relocked. Caller must arrange for actual relocking to occur via Lock(). int64_t nRelockTime = 0; - bool Unlock(const SecureString& strWalletPassphrase, bool fForMixingOnly = false); + bool Unlock(const SecureString& strWalletPassphrase, bool fForMixingOnly = false, bool accept_no_keys = false); bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); bool EncryptWallet(const SecureString& strWalletPassphrase); diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp new file mode 100644 index 0000000000..04a502913e --- /dev/null +++ b/src/wallet/wallettool.cpp @@ -0,0 +1,140 @@ +// Copyright (c) 2016-2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include +#include +#include + +namespace WalletTool { + +// The standard wallet deleter function blocks on the validation interface +// queue, which doesn't exist for the dash-wallet. Define our own +// deleter here. +static void WalletToolReleaseWallet(CWallet* wallet) +{ + wallet->WalletLogPrintf("Releasing wallet\n"); + wallet->Flush(); + delete wallet; +} + +static std::shared_ptr CreateWallet(const std::string& name, const fs::path& path) +{ + if (fs::exists(path)) { + tfm::format(std::cerr, "Error: File exists already\n"); + return nullptr; + } + // dummy chain interface + auto chain = interfaces::MakeChain(); + std::shared_ptr wallet_instance(new CWallet(*chain, WalletLocation(name), WalletDatabase::Create(path)), WalletToolReleaseWallet); + bool first_run = true; + DBErrors load_wallet_ret = wallet_instance->LoadWallet(first_run); + if (load_wallet_ret != DBErrors::LOAD_OK) { + tfm::format(std::cerr, "Error creating %s", name); + return nullptr; + } + + wallet_instance->SetMinVersion(FEATURE_HD); + + // generate a new HD seed + // NOTE: we do not yet create HD wallets by default + // wallet_instance->GenerateNewHDChain("", ""); + + tfm::format(std::cout, "Topping up keypool...\n"); + wallet_instance->TopUpKeyPool(); + return wallet_instance; +} + +static std::shared_ptr LoadWallet(const std::string& name, const fs::path& path) +{ + if (!fs::exists(path)) { + tfm::format(std::cerr, "Error: Wallet files does not exist\n"); + return nullptr; + } + + // dummy chain interface + auto chain = interfaces::MakeChain(); + std::shared_ptr wallet_instance(new CWallet(*chain, WalletLocation(name), WalletDatabase::Create(path)), WalletToolReleaseWallet); + DBErrors load_wallet_ret; + try { + bool first_run; + load_wallet_ret = wallet_instance->LoadWallet(first_run); + } catch (const std::runtime_error) { + tfm::format(std::cerr, "Error loading %s. Is wallet being used by another process?\n", name); + return nullptr; + } + + if (load_wallet_ret != DBErrors::LOAD_OK) { + wallet_instance = nullptr; + if (load_wallet_ret == DBErrors::CORRUPT) { + tfm::format(std::cerr, "Error loading %s: Wallet corrupted", name); + return nullptr; + } else if (load_wallet_ret == DBErrors::NONCRITICAL_ERROR) { + tfm::format(std::cerr, "Error reading %s! All keys read correctly, but transaction data" + " or address book entries might be missing or incorrect.", + name); + } else if (load_wallet_ret == DBErrors::TOO_NEW) { + tfm::format(std::cerr, "Error loading %s: Wallet requires newer version of %s", + name, PACKAGE_NAME); + return nullptr; + } else if (load_wallet_ret == DBErrors::NEED_REWRITE) { + tfm::format(std::cerr, "Wallet needed to be rewritten: restart %s to complete", PACKAGE_NAME); + return nullptr; + } else { + tfm::format(std::cerr, "Error loading %s", name); + return nullptr; + } + } + + return wallet_instance; +} + +static void WalletShowInfo(CWallet* wallet_instance) +{ + // lock required because of some AssertLockHeld() + LOCK(wallet_instance->cs_wallet); + + CHDChain hdChainTmp; + tfm::format(std::cout, "Wallet info\n===========\n"); + tfm::format(std::cout, "Encrypted: %s\n", wallet_instance->IsCrypted() ? "yes" : "no"); + tfm::format(std::cout, "HD (hd seed available): %s\n", wallet_instance->GetHDChain(hdChainTmp) ? "yes" : "no"); + tfm::format(std::cout, "Keypool Size: %u\n", wallet_instance->GetKeyPoolSize()); + tfm::format(std::cout, "Transactions: %zu\n", wallet_instance->mapWallet.size()); + tfm::format(std::cout, "Address Book: %zu\n", wallet_instance->mapAddressBook.size()); +} + +bool ExecuteWalletToolFunc(const std::string& command, const std::string& name) +{ + fs::path path = fs::absolute(name, GetWalletDir()); + + if (command == "create") { + std::shared_ptr wallet_instance = CreateWallet(name, path); + if (wallet_instance) { + WalletShowInfo(wallet_instance.get()); + wallet_instance->Flush(); + } + } else if (command == "info") { + if (!fs::exists(path)) { + tfm::format(std::cerr, "Error: no wallet file at %s\n", name); + return false; + } + std::string error; + if (!WalletBatch::VerifyEnvironment(path, error)) { + tfm::format(std::cerr, "Error loading %s. Is wallet being used by other process?\n", name); + return false; + } + std::shared_ptr wallet_instance = LoadWallet(name, path); + if (!wallet_instance) return false; + WalletShowInfo(wallet_instance.get()); + wallet_instance->Flush(); + } else { + tfm::format(std::cerr, "Invalid command: %s\n", command); + return false; + } + + return true; +} +} // namespace WalletTool diff --git a/src/wallet/wallettool.h b/src/wallet/wallettool.h new file mode 100644 index 0000000000..5b06fd1792 --- /dev/null +++ b/src/wallet/wallettool.h @@ -0,0 +1,20 @@ +// Copyright (c) 2016-2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_WALLET_WALLETTOOL_H +#define BITCOIN_WALLET_WALLETTOOL_H + +#include