diff --git a/.gitignore b/.gitignore --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ src/bitcoin-cli src/bitcoin-seeder src/bitcoin-tx +src/bitcoin-wallet src/test/test_bitcoin src/test/test_bitcoin_fuzzy src/qt/test/test_bitcoin-qt diff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -18,6 +18,7 @@ BITCOIN_CLI_NAME=bitcoin-cli BITCOIN_TX_NAME=bitcoin-tx BITCOIN_SEEDER_NAME=bitcoin-seeder +BITCOIN_WALLET_TOOL_NAME=bitcoin-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]) @@ -410,7 +411,7 @@ AC_ARG_WITH([utils], [AS_HELP_STRING([--with-utils], - [build bitcoin-cli bitcoin-tx (default=yes)])], + [build bitcoin-cli bitcoin-tx bitcoin-wallet (default=yes)])], [build_bitcoin_utils=$withval], [build_bitcoin_utils=yes]) @@ -426,6 +427,12 @@ [build_bitcoin_tx=$enableval], [build_bitcoin_tx=$build_bitcoin_utils]) +AC_ARG_ENABLE([util-wallet], + [AS_HELP_STRING([--enable-util-wallet], + [build bitcoin-wallet])], + [build_bitcoin_wallet=$enableval], + [build_bitcoin_wallet=$build_bitcoin_utils]) + AC_ARG_WITH([libs], [AS_HELP_STRING([--with-libs], [build libraries (default=yes)])], @@ -913,7 +920,7 @@ dnl sets $bitcoin_enable_qt, $bitcoin_enable_qt_test, $bitcoin_enable_qt_dbus BITCOIN_QT_CONFIGURE([$use_pkgconfig]) -if test x$build_bitcoin_cli$build_bitcoin_tx$build_bitcoind$bitcoin_enable_qt$build_bitcoin_seeder$use_tests$use_bench = xnonononononono; then +if test x$build_bitcoin_wallet$build_bitcoin_cli$build_bitcoin_tx$build_bitcoind$bitcoin_enable_qt$build_bitcoin_seeder$use_tests$use_bench = xnononononononono; then use_boost=no else use_boost=yes @@ -1086,7 +1093,7 @@ need_bundled_univalue=yes -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 @@ -1150,6 +1157,10 @@ AM_CONDITIONAL([BUILD_BITCOIN_TX], [test x$build_bitcoin_tx = xyes]) AC_MSG_RESULT($build_bitcoin_tx) +AC_MSG_CHECKING([whether to build bitcoin-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 @@ -1292,8 +1303,8 @@ AC_MSG_RESULT([no]) fi -if test x$build_bitcoin_cli$build_bitcoin_tx$build_bitcoin_libs$build_bitcoind$bitcoin_enable_qt$build_bitcoin_seeder$use_bench$use_tests = xnononononononono; then - AC_MSG_ERROR([No targets! Please specify at least one of: --with-utils --with-libs --with-daemon --with-seeder --with-gui --enable-bench or --enable-tests]) +if test x$build_bitcoin_wallet$build_bitcoin_cli$build_bitcoin_tx$build_bitcoin_libs$build_bitcoind$bitcoin_enable_qt$build_bitcoin_seeder$use_bench$use_tests = xnonononononononono; then + AC_MSG_ERROR([No targets! Please specify at least one of: --with-utils --with-libs --with-daemon --with-gui --with-seeder --enable-bench or --enable-tests]) fi AM_CONDITIONAL([TARGET_DARWIN], [test x$TARGET_OS = xdarwin]) @@ -1335,6 +1346,7 @@ AC_SUBST(BITCOIN_CLI_NAME) AC_SUBST(BITCOIN_TX_NAME) AC_SUBST(BITCOIN_SEEDER_NAME) +AC_SUBST(BITCOIN_WALLET_TOOL_NAME) AC_SUBST(RELDFLAGS) AC_SUBST(DEBUG_CPPFLAGS) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,10 +10,10 @@ option(BUILD_BITCOIN_WALLET "Activate the wallet functionality" ON) option(BUILD_BITCOIN_ZMQ "Activate the ZeroMQ functionalities" ON) -option(BUILD_BITCOIN_SEEDER "Build bitcoin-seeder" ON) option(BUILD_BITCOIN_CLI "Build bitcoin-cli" ON) option(BUILD_BITCOIN_TX "Build bitcoin-tx" ON) option(BUILD_BITCOIN_QT "Build bitcoin-qt" ON) +option(BUILD_BITCOIN_SEEDER "Build bitcoin-seeder" ON) option(BUILD_LIBBITCOINCONSENSUS "Build the bitcoinconsenus shared library" ON) option(ENABLE_BIP70 "Enable BIP70 (payment protocol) support in GUI" ON) option(ENABLE_HARDENING "Harden the executables" ON) @@ -553,6 +553,8 @@ # Benchmark suite. add_subdirectory(bench) +include(BinaryTest) + # Wallet if(BUILD_BITCOIN_WALLET) add_subdirectory(wallet) @@ -560,6 +562,19 @@ # There is a circular dependency between wallet and server, see: # https://github.com/bitcoin/bitcoin/pull/14437#discussion_r226237048 target_link_libraries(wallet server) + + # bitcoin-wallet + add_executable(bitcoin-wallet bitcoin-wallet.cpp) + if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") + target_sources(bitcoin-wallet PRIVATE bitcoin-wallet-res.rc) + endif() + + target_link_libraries(bitcoin-wallet wallet-tool server common bitcoinconsensus util) + + add_to_symbols_check(bitcoin-wallet) + add_to_security_check(bitcoin-wallet) + + install_target(bitcoin-wallet) else() target_sources(server PRIVATE dummywallet.cpp) endif() @@ -579,8 +594,6 @@ add_subdirectory(seeder) endif() -include(BinaryTest) - # bitcoin-cli if(BUILD_BITCOIN_CLI) add_executable(bitcoin-cli bitcoin-cli.cpp) diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -47,6 +47,7 @@ endif if ENABLE_WALLET LIBBITCOIN_WALLET=libbitcoin_wallet.a +LIBBITCOIN_WALLET_TOOL=libbitcoin_wallet_tool.a endif LIBBITCOIN_CRYPTO= $(LIBBITCOIN_CRYPTO_BASE) @@ -77,6 +78,7 @@ $(LIBBITCOIN_CLI) \ $(LIBBITCOIN_SEEDER) \ $(LIBBITCOIN_WALLET) \ + $(LIBBITCOIN_WALLET_TOOL) \ $(LIBBITCOIN_ZMQ) lib_LTLIBRARIES = $(LIBBITCOINCONSENSUS) @@ -100,6 +102,11 @@ if BUILD_BITCOIN_TX bin_PROGRAMS += bitcoin-tx endif +if ENABLE_WALLET +if BUILD_BITCOIN_WALLET + bin_PROGRAMS += bitcoin-wallet +endif +endif .PHONY: FORCE check-symbols check-security # bitcoin core # @@ -242,6 +249,7 @@ wallet/rpcwallet.h \ wallet/wallet.h \ wallet/walletdb.h \ + wallet/wallettool.h \ wallet/walletutil.h \ warnings.h \ zmq/zmqabstractnotifier.h \ @@ -356,6 +364,12 @@ wallet/walletutil.cpp \ $(BITCOIN_CORE_H) +libbitcoin_wallet_tool_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +libbitcoin_wallet_tool_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +libbitcoin_wallet_tool_a_SOURCES = \ + wallet/wallettool.cpp \ + $(BITCOIN_CORE_H) + # crypto primitives library crypto_libbitcoin_crypto_base_a_CPPFLAGS = $(AM_CPPFLAGS) crypto_libbitcoin_crypto_base_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -621,6 +635,32 @@ bitcoin_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) # +# bitcoin-wallet binary # +bitcoin_wallet_SOURCES = bitcoin-wallet.cpp +bitcoin_wallet_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +bitcoin_wallet_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +bitcoin_wallet_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) + +if TARGET_WINDOWS +bitcoin_wallet_SOURCES += bitcoin-wallet-res.rc +endif + +bitcoin_wallet_LDADD = \ + $(LIBBITCOIN_WALLET_TOOL) \ + $(LIBBITCOIN_WALLET) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CRYPTO) \ + $(LIBLEVELDB) \ + $(LIBLEVELDB_SSE42) \ + $(LIBMEMENV) \ + $(LIBSECP256K1) + +bitcoin_wallet_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) +# + # bitcoinconsensus library # if BUILD_BITCOIN_LIBS include_HEADERS = script/bitcoinconsensus.h diff --git a/src/bitcoin-wallet-res.rc b/src/bitcoin-wallet-res.rc new file mode 100644 --- /dev/null +++ b/src/bitcoin-wallet-res.rc @@ -0,0 +1,35 @@ +#include // needed for VERSIONINFO +#include // holds the needed client version information + +#define VER_PRODUCTVERSION CLIENT_VERSION_MAJOR,CLIENT_VERSION_MINOR,CLIENT_VERSION_REVISION +#define VER_PRODUCTVERSION_STR STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) "." STRINGIZE(CLIENT_VERSION_REVISION) +#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", "Bitcoin" + VALUE "FileDescription", "bitcoin-wallet (CLI tool for " PACKAGE_NAME " wallets)" + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", "bitcoin-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", "bitcoin-wallet.exe" + VALUE "ProductName", "bitcoin-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/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp new file mode 100644 --- /dev/null +++ b/src/bitcoin-wallet.cpp @@ -0,0 +1,143 @@ +// 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", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-datadir=", "Specify data directory", false, + OptionsCategory::OPTIONS); + gArgs.AddArg("-wallet=", "Specify wallet name", false, + OptionsCategory::OPTIONS); + gArgs.AddArg("-debug=", + "Output debugging information (default: 0).", false, + OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-printtoconsole", + "Send trace/debug info to console (default: 1 when no -debug " + "is true, 0 otherwise.", + false, OptionsCategory::DEBUG_TEST); + + gArgs.AddArg("info", "Get wallet info", false, OptionsCategory::COMMANDS); + gArgs.AddArg("create", "Create new wallet file", false, + OptionsCategory::COMMANDS); + + // Hidden + gArgs.AddArg("-h", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN); +} + +static bool WalletAppInit(int argc, char *argv[]) { + SetupWalletToolArgs(); + std::string error_message; + if (!gArgs.ParseParameters(argc, argv, error_message)) { + fprintf(stderr, "Error parsing command line arguments: %s\n", + error_message.c_str()); + return false; + } + if (argc < 2 || HelpRequested(gArgs)) { + std::string usage = + strprintf("%s bitcoin-wallet version", PACKAGE_NAME) + " " + + FormatFullVersion() + "\n\n" + + "wallet-tool is an offline tool for creating and interacting with " + "Bitcoin ABC 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" + " bitcoin-wallet [options] \n\n" + + gArgs.GetHelpMessage(); + + fprintf(stdout, "%s", usage.c_str()); + 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))) { + fprintf(stderr, + "Error: Specified data directory \"%s\" does not exist.\n", + gArgs.GetArg("-datadir", "").c_str()); + 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 (const std::exception &e) { + PrintExceptionContinue(&e, "WalletAppInit()"); + return EXIT_FAILURE; + } catch (...) { + PrintExceptionContinue(nullptr, "WalletAppInit()"); + return EXIT_FAILURE; + } + + std::string method{}; + for (int i = 1; i < argc; ++i) { + if (!IsSwitchChar(argv[i][0])) { + if (!method.empty()) { + fprintf(stderr, + "Error: two methods provided (%s and %s). Only one " + "method should be provided.\n", + method.c_str(), argv[i]); + return EXIT_FAILURE; + } + method = argv[i]; + } + } + + if (method.empty()) { + fprintf(stderr, "No method provided. Run `bitcoin-wallet -help` for " + "valid methods.\n"); + return EXIT_FAILURE; + } + + // A name must be provided when creating a file + if (method == "create" && !gArgs.IsArgSet("-wallet")) { + fprintf(stderr, + "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/CMakeLists.txt b/src/wallet/CMakeLists.txt --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -27,3 +27,7 @@ ) target_link_libraries(wallet script univalue Event::event BerkeleyDB::CXX) + +# wallet-tool library +add_library(wallet-tool wallettool.cpp) +target_link_libraries(wallet-tool wallet) diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -134,7 +134,8 @@ //! will encrypt previously unencrypted keys bool EncryptKeys(CKeyingMaterial &vMasterKeyIn); - bool Unlock(const CKeyingMaterial &vMasterKeyIn); + bool Unlock(const CKeyingMaterial &vMasterKeyIn, + bool accept_no_keys = false); CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore); public: diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -199,7 +199,8 @@ return true; } -bool CCryptoKeyStore::Unlock(const CKeyingMaterial &vMasterKeyIn) { +bool CCryptoKeyStore::Unlock(const CKeyingMaterial &vMasterKeyIn, + bool accept_no_keys) { { LOCK(cs_KeyStore); if (!SetCrypted()) { @@ -228,7 +229,7 @@ "not all.\n"); assert(false); } - if (keyFail || !keyPass) { + if (keyFail || (!keyPass && !accept_no_keys)) { return false; } vMasterKey = vMasterKeyIn; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1027,7 +1027,8 @@ //! Lock(). int64_t nRelockTime = 0; - bool Unlock(const SecureString &strWalletPassphrase); + bool Unlock(const SecureString &strWalletPassphrase, + bool accept_no_keys = false); bool ChangeWalletPassphrase(const SecureString &strOldWalletPassphrase, const SecureString &strNewWalletPassphrase); bool EncryptWallet(const SecureString &strWalletPassphrase); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -477,26 +477,28 @@ return CCryptoKeyStore::AddWatchOnly(dest); } -bool CWallet::Unlock(const SecureString &strWalletPassphrase) { +bool CWallet::Unlock(const SecureString &strWalletPassphrase, + bool accept_no_keys) { CCrypter crypter; CKeyingMaterial _vMasterKey; - LOCK(cs_wallet); - for (const MasterKeyMap::value_type &pMasterKey : mapMasterKeys) { - if (!crypter.SetKeyFromPassphrase( - strWalletPassphrase, pMasterKey.second.vchSalt, - pMasterKey.second.nDeriveIterations, - pMasterKey.second.nDerivationMethod)) { - return false; - } - - if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey)) { - // try another master key - continue; - } - - if (CCryptoKeyStore::Unlock(_vMasterKey)) { - return true; + { + LOCK(cs_wallet); + for (const MasterKeyMap::value_type &pMasterKey : mapMasterKeys) { + if (!crypter.SetKeyFromPassphrase( + strWalletPassphrase, pMasterKey.second.vchSalt, + pMasterKey.second.nDeriveIterations, + pMasterKey.second.nDerivationMethod)) { + return false; + } + if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, + _vMasterKey)) { + // try another master key + continue; + } + if (CCryptoKeyStore::Unlock(_vMasterKey, accept_no_keys)) { + return true; + } } } diff --git a/src/wallet/wallettool.h b/src/wallet/wallettool.h new file mode 100644 --- /dev/null +++ b/src/wallet/wallettool.h @@ -0,0 +1,22 @@ +// 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