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/qt/guiutil.cpp b/src/qt/guiutil.cpp index 60d9ba1b58..bd0407bbb3 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -469,6 +469,11 @@ QList getEntryData(QAbstractItemView *view, int column) return view->selectionModel()->selectedRows(column); } +QString getDefaultDataDirectory() +{ + return boostPathToQString(GetDefaultDataDir()); +} + QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedSuffixOut) diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 4c81c54635..dd6b4a9d89 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -145,6 +145,11 @@ namespace GUIUtil void setClipboard(const QString& str); + /** + * Determine default data directory for operating system. + */ + QString getDefaultDataDirectory(); + /** Get save filename, mimics QFileDialog::getSaveFileName, except that it appends a default suffix when no suffix is provided by the user. diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 3d720330f4..184da14791 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -170,7 +170,7 @@ QString Intro::getDataDirectory() void Intro::setDataDirectory(const QString &dataDir) { ui->dataDirectory->setText(dataDir); - if(dataDir == getDefaultDataDirectory()) + if(dataDir == GUIUtil::getDefaultDataDirectory()) { ui->dataDirDefault->setChecked(true); ui->dataDirectory->setEnabled(false); @@ -182,11 +182,6 @@ void Intro::setDataDirectory(const QString &dataDir) } } -QString Intro::getDefaultDataDirectory() -{ - return GUIUtil::boostPathToQString(GetDefaultDataDir()); -} - bool Intro::pickDataDirectory(interfaces::Node& node) { QSettings settings; @@ -195,13 +190,13 @@ bool Intro::pickDataDirectory(interfaces::Node& node) if(!gArgs.GetArg("-datadir", "").empty()) return true; /* 1) Default data directory for operating system */ - QString dataDirDefaultCurrent = getDefaultDataDirectory(); + QString dataDirDefaultCurrent = GUIUtil::getDefaultDataDirectory(); /* 2) Allow QSettings to override default dir */ QString dataDir = settings.value("strDataDir", dataDirDefaultCurrent).toString(); /* 3) Check to see if default datadir is the one we expect */ QString dataDirDefaultSettings = settings.value("strDataDirDefault").toString(); - if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || gArgs.GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR) || dataDirDefaultCurrent != dataDirDefaultSettings) + if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || gArgs.GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR) || dataDirDefaultCurrent != dataDirDefaultSettings || settings.value("fReset", false).toBool() || gArgs.GetBoolArg("-resetguisettings", false)) { /* Use selectParams here to guarantee Params() can be used by node interface */ try { @@ -240,12 +235,13 @@ bool Intro::pickDataDirectory(interfaces::Node& node) settings.setValue("strDataDir", dataDir); settings.setValue("strDataDirDefault", dataDirDefaultCurrent); + settings.setValue("fReset", false); } /* Only override -datadir if different from the default, to make it possible to * override -datadir in the dash.conf file in the default data directory * (to be consistent with dashd behavior) */ - if(dataDir != dataDirDefaultCurrent) { + if(dataDir != GUIUtil::getDefaultDataDirectory()) { node.softSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting } return true; @@ -299,7 +295,7 @@ void Intro::on_ellipsisButton_clicked() void Intro::on_dataDirDefault_clicked() { - setDataDirectory(getDefaultDataDirectory()); + setDataDirectory(GUIUtil::getDefaultDataDirectory()); } void Intro::on_dataDirCustom_clicked() diff --git a/src/qt/intro.h b/src/qt/intro.h index 64cda51851..9e178b7a3a 100644 --- a/src/qt/intro.h +++ b/src/qt/intro.h @@ -48,11 +48,6 @@ public: */ static bool pickDataDirectory(interfaces::Node& node); - /** - * Determine default data directory for operating system. - */ - static QString getDefaultDataDirectory(); - Q_SIGNALS: void requestCheck(); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 5509d29437..9af060cf46 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -208,6 +208,9 @@ void OptionsModel::Init(bool resetSettings) if (!m_node.softSetArg("-par", settings.value("nThreadsScriptVerif").toString().toStdString())) addOverriddenOption("-par"); + if (!settings.contains("strDataDir")) + settings.setValue("strDataDir", GUIUtil::getDefaultDataDirectory()); + // Wallet #ifdef ENABLE_WALLET if (!settings.contains("bSpendZeroConfChange")) @@ -306,9 +309,19 @@ void OptionsModel::Reset() // Backup old settings to chain-specific datadir for troubleshooting BackupSettings(GetDataDir(true) / "guisettings.ini.bak", settings); + // Save the strDataDir setting + QString dataDir = GUIUtil::getDefaultDataDirectory(); + dataDir = settings.value("strDataDir", dataDir).toString(); + // Remove all entries from our QSettings object settings.clear(); + // Set strDataDir + settings.setValue("strDataDir", dataDir); + + // Set that this was reset + settings.setValue("fReset", true); + // default setting for OptionsModel::StartAtStartup - disabled if (GUIUtil::GetStartOnSystemStartup()) GUIUtil::SetStartOnSystemStartup(false); 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