mirror of
https://github.com/dashpay/dash.git
synced 2024-12-24 19:42:46 +01:00
Merge #13926: [Tools] bitcoin-wallet - a tool for creating and managing wallets offline
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
This commit is contained in:
parent
305b96e008
commit
cc1f8db725
1
.gitignore
vendored
1
.gitignore
vendored
@ -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
|
||||
|
20
configure.ac
20
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 <cr> 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)
|
||||
|
@ -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
|
||||
|
35
src/dash-wallet-res.rc
Normal file
35
src/dash-wallet-res.rc
Normal file
@ -0,0 +1,35 @@
|
||||
#include <windows.h> // 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
|
118
src/dash-wallet.cpp
Normal file
118
src/dash-wallet.cpp
Normal file
@ -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 <config/dash-config.h>
|
||||
#endif
|
||||
|
||||
#include <chainparams.h>
|
||||
#include <chainparamsbase.h>
|
||||
#include <consensus/consensus.h>
|
||||
#include <logging.h>
|
||||
#include <util/system.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <wallet/wallettool.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
|
||||
|
||||
static void SetupWalletToolArgs()
|
||||
{
|
||||
SetupChainParamsBaseOptions();
|
||||
|
||||
gArgs.AddArg("-?", "This help message", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
gArgs.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
gArgs.AddArg("-wallet=<wallet-name>", "Specify wallet name", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
gArgs.AddArg("-debug=<category>", "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] <command>\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;
|
||||
}
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
140
src/wallet/wallettool.cpp
Normal file
140
src/wallet/wallettool.cpp
Normal file
@ -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 <base58.h>
|
||||
#include <fs.h>
|
||||
#include <interfaces/chain.h>
|
||||
#include <util/system.h>
|
||||
#include <wallet/wallet.h>
|
||||
#include <wallet/walletutil.h>
|
||||
|
||||
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<CWallet> 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<CWallet> 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<CWallet> 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<CWallet> 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<CWallet> 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<CWallet> 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
|
20
src/wallet/wallettool.h
Normal file
20
src/wallet/wallettool.h
Normal file
@ -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 <script/ismine.h>
|
||||
#include <wallet/wallet.h>
|
||||
|
||||
namespace WalletTool {
|
||||
|
||||
std::shared_ptr<CWallet> CreateWallet(const std::string& name, const fs::path& path);
|
||||
std::shared_ptr<CWallet> LoadWallet(const std::string& name, const fs::path& path);
|
||||
void WalletShowInfo(CWallet* wallet_instance);
|
||||
bool ExecuteWalletToolFunc(const std::string& command, const std::string& file);
|
||||
|
||||
} // namespace WalletTool
|
||||
|
||||
#endif // BITCOIN_WALLET_WALLETTOOL_H
|
@ -179,6 +179,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read_file(open(self.options.configfile))
|
||||
self.config = config
|
||||
self.options.bitcoind = os.getenv("BITCOIND", default=config["environment"]["BUILDDIR"] + '/src/dashd' + config["environment"]["EXEEXT"])
|
||||
self.options.bitcoincli = os.getenv("BITCOINCLI", default=config["environment"]["BUILDDIR"] + '/src/dash-cli' + config["environment"]["EXEEXT"])
|
||||
|
||||
|
@ -120,6 +120,7 @@ BASE_SCRIPTS = [
|
||||
'interface_bitcoin_cli.py',
|
||||
'mempool_resurrect.py',
|
||||
'wallet_txn_doublespend.py --mineblock',
|
||||
'tool_wallet.py',
|
||||
'wallet_txn_clone.py',
|
||||
'rpc_getchaintips.py',
|
||||
'rpc_misc.py',
|
||||
@ -606,7 +607,7 @@ class TestResult():
|
||||
def check_script_prefixes():
|
||||
"""Check that test scripts start with one of the allowed name prefixes."""
|
||||
|
||||
good_prefixes_re = re.compile("(example|feature|interface|mempool|mining|p2p|rpc|wallet)_")
|
||||
good_prefixes_re = re.compile("(example|feature|interface|mempool|mining|p2p|rpc|wallet|tool)_")
|
||||
bad_script_names = [script for script in ALL_SCRIPTS if good_prefixes_re.match(script) is None]
|
||||
|
||||
if bad_script_names:
|
||||
|
114
test/functional/tool_wallet.py
Executable file
114
test/functional/tool_wallet.py
Executable file
@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 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.
|
||||
"""Test dash-wallet."""
|
||||
import subprocess
|
||||
import textwrap
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import assert_equal
|
||||
|
||||
class ToolWalletTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 1
|
||||
self.setup_clean_chain = True
|
||||
|
||||
def skip_test_if_missing_module(self):
|
||||
self.skip_if_no_wallet()
|
||||
|
||||
def dash_wallet_process(self, *args):
|
||||
binary = self.config["environment"]["BUILDDIR"] + '/src/dash-wallet' + self.config["environment"]["EXEEXT"]
|
||||
args = ['-datadir={}'.format(self.nodes[0].datadir), '-regtest'] + list(args)
|
||||
return subprocess.Popen([binary] + args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
|
||||
|
||||
def assert_raises_tool_error(self, error, *args):
|
||||
p = self.dash_wallet_process(*args)
|
||||
stdout, stderr = p.communicate()
|
||||
assert_equal(p.poll(), 1)
|
||||
assert_equal(stdout, '')
|
||||
assert_equal(stderr.strip(), error)
|
||||
|
||||
def assert_tool_output(self, output, *args):
|
||||
p = self.dash_wallet_process(*args)
|
||||
stdout, stderr = p.communicate()
|
||||
assert_equal(p.poll(), 0)
|
||||
assert_equal(stderr, '')
|
||||
assert_equal(stdout, output)
|
||||
|
||||
def run_test(self):
|
||||
|
||||
self.assert_raises_tool_error('Invalid command: foo', 'foo')
|
||||
# `dash-wallet help` is an error. Use `dash-wallet -help`
|
||||
self.assert_raises_tool_error('Invalid command: help', 'help')
|
||||
self.assert_raises_tool_error('Error: two methods provided (info and create). Only one method should be provided.', 'info', 'create')
|
||||
self.assert_raises_tool_error('Error parsing command line arguments: Invalid parameter -foo', '-foo')
|
||||
self.assert_raises_tool_error('Error loading wallet.dat. Is wallet being used by other process?', '-wallet=wallet.dat', 'info')
|
||||
self.assert_raises_tool_error('Error: no wallet file at nonexistent.dat', '-wallet=nonexistent.dat', 'info')
|
||||
|
||||
# stop the node to close the wallet to call info command
|
||||
self.stop_node(0)
|
||||
|
||||
out = textwrap.dedent('''\
|
||||
Wallet info
|
||||
===========
|
||||
Encrypted: no
|
||||
HD (hd seed available): no
|
||||
Keypool Size: 1
|
||||
Transactions: 0
|
||||
Address Book: 0
|
||||
''')
|
||||
self.assert_tool_output(out, '-wallet=wallet.dat', 'info')
|
||||
|
||||
self.start_node(0)
|
||||
self.nodes[0].upgradetohd()
|
||||
self.stop_node(0)
|
||||
|
||||
out = textwrap.dedent('''\
|
||||
Wallet info
|
||||
===========
|
||||
Encrypted: no
|
||||
HD (hd seed available): yes
|
||||
Keypool Size: 2
|
||||
Transactions: 0
|
||||
Address Book: 0
|
||||
''')
|
||||
self.assert_tool_output(out, '-wallet=wallet.dat', 'info')
|
||||
|
||||
# mutate the wallet to check the info command output changes accordingly
|
||||
self.start_node(0)
|
||||
self.nodes[0].generate(1)
|
||||
self.stop_node(0)
|
||||
|
||||
out = textwrap.dedent('''\
|
||||
Wallet info
|
||||
===========
|
||||
Encrypted: no
|
||||
HD (hd seed available): yes
|
||||
Keypool Size: 1
|
||||
Transactions: 1
|
||||
Address Book: 0
|
||||
''')
|
||||
self.assert_tool_output(out, '-wallet=wallet.dat', 'info')
|
||||
|
||||
out = textwrap.dedent('''\
|
||||
Topping up keypool...
|
||||
Wallet info
|
||||
===========
|
||||
Encrypted: no
|
||||
HD (hd seed available): no
|
||||
Keypool Size: 1000
|
||||
Transactions: 0
|
||||
Address Book: 0
|
||||
''')
|
||||
self.assert_tool_output(out, '-wallet=foo', 'create')
|
||||
|
||||
self.start_node(0, ['-wallet=foo'])
|
||||
out = self.nodes[0].getwalletinfo()
|
||||
self.stop_node(0)
|
||||
|
||||
assert_equal(0, out['txcount'])
|
||||
assert_equal(1000, out['keypoolsize'])
|
||||
|
||||
if __name__ == '__main__':
|
||||
ToolWalletTest().main()
|
Loading…
Reference in New Issue
Block a user