mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 12:02:48 +01:00
Merge pull request #4081 from xdustinface/pr-fix-issue-3521
backport: Some wallet related PRs
This commit is contained in:
commit
6ba3b2f3b8
@ -33,7 +33,7 @@ static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<CO
|
||||
// (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484)
|
||||
static void CoinSelection(benchmark::State& state)
|
||||
{
|
||||
const CWallet wallet("dummy", WalletDatabase::CreateDummy());
|
||||
const CWallet wallet(WalletLocation(), WalletDatabase::CreateDummy());
|
||||
std::vector<COutput> vCoins;
|
||||
LOCK(wallet.cs_wallet);
|
||||
|
||||
|
@ -484,6 +484,10 @@ public:
|
||||
}
|
||||
bool hdEnabled() override { return m_wallet.IsHDEnabled(); }
|
||||
CoinJoin::Client& coinJoin() override { return m_coinjoin; }
|
||||
std::unique_ptr<Handler> handleUnload(UnloadFn fn) override
|
||||
{
|
||||
return MakeHandler(m_wallet.NotifyUnload.connect(fn));
|
||||
}
|
||||
std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) override
|
||||
{
|
||||
return MakeHandler(m_wallet.ShowProgress.connect(fn));
|
||||
|
@ -249,6 +249,10 @@ public:
|
||||
|
||||
virtual CoinJoin::Client& coinJoin() = 0;
|
||||
|
||||
//! Register handler for unload message.
|
||||
using UnloadFn = std::function<void()>;
|
||||
virtual std::unique_ptr<Handler> handleUnload(UnloadFn fn) = 0;
|
||||
|
||||
//! Register handler for show progress messages.
|
||||
using ShowProgressFn = std::function<void(const std::string& title, int progress)>;
|
||||
virtual std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) = 0;
|
||||
|
@ -140,9 +140,9 @@ void AskPassphraseDialog::accept()
|
||||
if (model->wallet().hdEnabled()) {
|
||||
QMessageBox::warning(this, tr("Wallet encrypted"),
|
||||
"<qt>" +
|
||||
tr("%1 will close now to finish the encryption process. "
|
||||
tr("Your wallet is now encrypted. "
|
||||
"Remember that encrypting your wallet cannot fully protect "
|
||||
"your funds from being stolen by malware infecting your computer.").arg(tr(PACKAGE_NAME)) +
|
||||
"your funds from being stolen by malware infecting your computer.") +
|
||||
"<br><br><b>" +
|
||||
tr("IMPORTANT: Any previous backups you have made of your wallet file "
|
||||
"should be replaced with the newly generated, encrypted wallet file. "
|
||||
@ -152,9 +152,9 @@ void AskPassphraseDialog::accept()
|
||||
} else {
|
||||
QMessageBox::warning(this, tr("Wallet encrypted"),
|
||||
"<qt>" +
|
||||
tr("%1 will close now to finish the encryption process. "
|
||||
tr("Your wallet is now encrypted. "
|
||||
"Remember that encrypting your wallet cannot fully protect "
|
||||
"your funds from being stolen by malware infecting your computer.").arg(tr(PACKAGE_NAME)) +
|
||||
"your funds from being stolen by malware infecting your computer.") +
|
||||
"<br><br><b>" +
|
||||
tr("IMPORTANT: Any previous backups you have made of your wallet file "
|
||||
"should be replaced with the newly generated, encrypted wallet file. "
|
||||
@ -162,7 +162,6 @@ void AskPassphraseDialog::accept()
|
||||
"will become useless as soon as you start using the new, encrypted wallet.") +
|
||||
"</b></qt>");
|
||||
}
|
||||
QApplication::quit();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -639,8 +639,19 @@ void BitcoinGUI::createToolBars()
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
m_wallet_selector = new QComboBox(this);
|
||||
m_wallet_selector->setHidden(true);
|
||||
connect(m_wallet_selector, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(setCurrentWallet(const QString&)));
|
||||
connect(m_wallet_selector, SIGNAL(currentIndexChanged(int)), this, SLOT(setCurrentWalletBySelectorIndex(int)));
|
||||
|
||||
QVBoxLayout* walletSelectorLayout = new QVBoxLayout(this);
|
||||
walletSelectorLayout->addWidget(m_wallet_selector);
|
||||
walletSelectorLayout->setSpacing(0);
|
||||
walletSelectorLayout->setMargin(0);
|
||||
walletSelectorLayout->setContentsMargins(5, 0, 5, 0);
|
||||
QWidget* walletSelector = new QWidget(this);
|
||||
walletSelector->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
walletSelector->setObjectName("walletSelector");
|
||||
walletSelector->setLayout(walletSelectorLayout);
|
||||
m_wallet_selector_action = appToolBar->insertWidget(appToolBarLogoAction, walletSelector);
|
||||
m_wallet_selector_action->setVisible(false);
|
||||
#endif
|
||||
|
||||
QLabel *logoLabel = new QLabel();
|
||||
@ -769,25 +780,31 @@ bool BitcoinGUI::addWallet(WalletModel *walletModel)
|
||||
if(!walletFrame)
|
||||
return false;
|
||||
const QString name = walletModel->getWalletName();
|
||||
QString display_name = name.isEmpty() ? "["+tr("default wallet")+"]" : name;
|
||||
setWalletActionsEnabled(true);
|
||||
m_wallet_selector->addItem(name);
|
||||
m_wallet_selector->addItem(display_name, name);
|
||||
if (m_wallet_selector->count() == 2) {
|
||||
m_wallet_selector->setHidden(false);
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
layout->addWidget(m_wallet_selector);
|
||||
layout->setSpacing(0);
|
||||
layout->setMargin(0);
|
||||
layout->setContentsMargins(5, 0, 5, 0);
|
||||
QWidget* walletSelector = new QWidget(this);
|
||||
walletSelector->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
walletSelector->setObjectName("walletSelector");
|
||||
walletSelector->setLayout(layout);
|
||||
appToolBar->insertWidget(appToolBarLogoAction, walletSelector);
|
||||
m_wallet_selector_action->setVisible(true);
|
||||
}
|
||||
rpcConsole->addWallet(walletModel);
|
||||
return walletFrame->addWallet(walletModel);
|
||||
}
|
||||
|
||||
bool BitcoinGUI::removeWallet(WalletModel* walletModel)
|
||||
{
|
||||
if (!walletFrame) return false;
|
||||
QString name = walletModel->getWalletName();
|
||||
int index = m_wallet_selector->findData(name);
|
||||
m_wallet_selector->removeItem(index);
|
||||
if (m_wallet_selector->count() == 0) {
|
||||
setWalletActionsEnabled(false);
|
||||
} else if (m_wallet_selector->count() == 1) {
|
||||
m_wallet_selector_action->setVisible(false);
|
||||
}
|
||||
rpcConsole->removeWallet(walletModel);
|
||||
return walletFrame->removeWallet(name);
|
||||
}
|
||||
|
||||
bool BitcoinGUI::setCurrentWallet(const QString& name)
|
||||
{
|
||||
if(!walletFrame)
|
||||
@ -795,6 +812,12 @@ bool BitcoinGUI::setCurrentWallet(const QString& name)
|
||||
return walletFrame->setCurrentWallet(name);
|
||||
}
|
||||
|
||||
bool BitcoinGUI::setCurrentWalletBySelectorIndex(int index)
|
||||
{
|
||||
QString internal_name = m_wallet_selector->itemData(index).toString();
|
||||
return setCurrentWallet(internal_name);
|
||||
}
|
||||
|
||||
void BitcoinGUI::removeAllWallets()
|
||||
{
|
||||
if(!walletFrame)
|
||||
|
@ -76,6 +76,7 @@ public:
|
||||
functionality.
|
||||
*/
|
||||
bool addWallet(WalletModel *walletModel);
|
||||
bool removeWallet(WalletModel* walletModel);
|
||||
void removeAllWallets();
|
||||
#endif // ENABLE_WALLET
|
||||
bool enableWallet;
|
||||
@ -140,8 +141,8 @@ private:
|
||||
QAction *openAction;
|
||||
QAction *showHelpMessageAction;
|
||||
QAction *showCoinJoinHelpAction;
|
||||
QAction *m_wallet_selector_action = nullptr;
|
||||
|
||||
QLabel *m_wallet_selector_label;
|
||||
QComboBox *m_wallet_selector;
|
||||
|
||||
QSystemTrayIcon *trayIcon;
|
||||
@ -238,6 +239,7 @@ public Q_SLOTS:
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
bool setCurrentWallet(const QString& name);
|
||||
bool setCurrentWalletBySelectorIndex(int index);
|
||||
/** Set the UI status indicators based on the currently selected wallet.
|
||||
*/
|
||||
void updateWalletStatus();
|
||||
|
@ -212,6 +212,7 @@ public Q_SLOTS:
|
||||
/// Handle runaway exceptions. Shows a message box with the problem and quits the program.
|
||||
void handleRunawayException(const QString &message);
|
||||
void addWallet(WalletModel* walletModel);
|
||||
void removeWallet();
|
||||
|
||||
Q_SIGNALS:
|
||||
void requestedInitialize();
|
||||
@ -454,11 +455,22 @@ void BitcoinApplication::addWallet(WalletModel* walletModel)
|
||||
|
||||
connect(walletModel, SIGNAL(coinsSent(WalletModel*, SendCoinsRecipient, QByteArray)),
|
||||
paymentServer, SLOT(fetchPaymentACK(WalletModel*, const SendCoinsRecipient&, QByteArray)));
|
||||
connect(walletModel, SIGNAL(unload()), this, SLOT(removeWallet()));
|
||||
|
||||
m_wallet_models.push_back(walletModel);
|
||||
#endif
|
||||
}
|
||||
|
||||
void BitcoinApplication::removeWallet()
|
||||
{
|
||||
#ifdef ENABLE_WALLET
|
||||
WalletModel* walletModel = static_cast<WalletModel*>(sender());
|
||||
m_wallet_models.erase(std::find(m_wallet_models.begin(), m_wallet_models.end(), walletModel));
|
||||
window->removeWallet(walletModel);
|
||||
walletModel->deleteLater();
|
||||
#endif
|
||||
}
|
||||
|
||||
void BitcoinApplication::initializeResult(bool success)
|
||||
{
|
||||
qDebug() << __func__ << ": Initialization result: " << success;
|
||||
@ -478,8 +490,10 @@ void BitcoinApplication::initializeResult(bool success)
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) {
|
||||
QMetaObject::invokeMethod(this, "addWallet", Qt::QueuedConnection,
|
||||
Q_ARG(WalletModel*, new WalletModel(std::move(wallet), m_node, optionsModel)));
|
||||
WalletModel* wallet_model = new WalletModel(std::move(wallet), m_node, optionsModel, nullptr);
|
||||
// Fix wallet model thread affinity.
|
||||
wallet_model->moveToThread(thread());
|
||||
QMetaObject::invokeMethod(this, "addWallet", Qt::QueuedConnection, Q_ARG(WalletModel*, wallet_model));
|
||||
});
|
||||
|
||||
for (auto& wallet : m_node.getWallets()) {
|
||||
|
@ -680,6 +680,10 @@ QToolBar > QToolButton:checked {
|
||||
color: #c7c7c7;
|
||||
}
|
||||
|
||||
QToolBar > QToolButton:disabled {
|
||||
color: #4a4a4b;
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
QToolTip
|
||||
******************************************************/
|
||||
|
@ -665,6 +665,10 @@ QToolBar > QToolButton:checked {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
QToolBar > QToolButton:disabled {
|
||||
color: #a7a7a7;
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
QToolTip
|
||||
******************************************************/
|
||||
|
@ -743,7 +743,8 @@ void RPCConsole::addWallet(WalletModel * const walletModel)
|
||||
{
|
||||
const QString name = walletModel->getWalletName();
|
||||
// use name for text and internal data object (to allow to move to a wallet id later)
|
||||
ui->WalletSelector->addItem(name, name);
|
||||
QString display_name = name.isEmpty() ? "["+tr("default wallet")+"]" : name;
|
||||
ui->WalletSelector->addItem(display_name, name);
|
||||
if (ui->WalletSelector->count() == 2 && !isVisible()) {
|
||||
// First wallet added, set to default so long as the window isn't presently visible (and potentially in use)
|
||||
ui->WalletSelector->setCurrentIndex(1);
|
||||
@ -753,6 +754,16 @@ void RPCConsole::addWallet(WalletModel * const walletModel)
|
||||
ui->WalletSelectorLabel->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
void RPCConsole::removeWallet(WalletModel * const walletModel)
|
||||
{
|
||||
const QString name = walletModel->getWalletName();
|
||||
ui->WalletSelector->removeItem(ui->WalletSelector->findData(name));
|
||||
if (ui->WalletSelector->count() == 2) {
|
||||
ui->WalletSelector->setVisible(false);
|
||||
ui->WalletSelectorLabel->setVisible(false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static QString categoryClass(int category)
|
||||
|
@ -49,6 +49,7 @@ public:
|
||||
|
||||
void setClientModel(ClientModel *model);
|
||||
void addWallet(WalletModel * const walletModel);
|
||||
void removeWallet(WalletModel* const walletModel);
|
||||
|
||||
enum MessageClass {
|
||||
MC_ERROR,
|
||||
|
@ -119,7 +119,7 @@ void TestGUI()
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
|
||||
}
|
||||
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>("mock", WalletDatabase::CreateMock());
|
||||
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(WalletLocation(), WalletDatabase::CreateMock());
|
||||
AddWallet(wallet);
|
||||
bool firstRun;
|
||||
wallet->LoadWallet(firstRun);
|
||||
|
@ -93,6 +93,7 @@ bool WalletFrame::removeWallet(const QString &name)
|
||||
|
||||
WalletView *walletView = mapWalletViews.take(name);
|
||||
walletStack->removeWidget(walletView);
|
||||
delete walletView;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -427,6 +427,12 @@ int64_t WalletModel::getKeysLeftSinceAutoBackup() const
|
||||
}
|
||||
|
||||
// Handlers for core signals
|
||||
static void NotifyUnload(WalletModel* walletModel)
|
||||
{
|
||||
qDebug() << "NotifyUnload";
|
||||
QMetaObject::invokeMethod(walletModel, "unload", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel)
|
||||
{
|
||||
qDebug() << "NotifyKeyStoreStatusChanged";
|
||||
@ -485,6 +491,7 @@ static void NotifyWatchonlyChanged(WalletModel *walletmodel, bool fHaveWatchonly
|
||||
void WalletModel::subscribeToCoreSignals()
|
||||
{
|
||||
// Connect signals to wallet
|
||||
m_handler_unload = m_wallet->handleUnload(boost::bind(&NotifyUnload, this));
|
||||
m_handler_status_changed = m_wallet->handleStatusChanged(boost::bind(&NotifyKeyStoreStatusChanged, this));
|
||||
m_handler_address_book_changed = m_wallet->handleAddressBookChanged(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5));
|
||||
m_handler_transaction_changed = m_wallet->handleTransactionChanged(boost::bind(NotifyTransactionChanged, this, _1, _2));
|
||||
@ -497,6 +504,7 @@ void WalletModel::subscribeToCoreSignals()
|
||||
void WalletModel::unsubscribeFromCoreSignals()
|
||||
{
|
||||
// Disconnect signals from wallet
|
||||
m_handler_unload->disconnect();
|
||||
m_handler_status_changed->disconnect();
|
||||
m_handler_address_book_changed->disconnect();
|
||||
m_handler_transaction_changed->disconnect();
|
||||
|
@ -214,6 +214,7 @@ public:
|
||||
bool isMultiwallet();
|
||||
private:
|
||||
std::unique_ptr<interfaces::Wallet> m_wallet;
|
||||
std::unique_ptr<interfaces::Handler> m_handler_unload;
|
||||
std::unique_ptr<interfaces::Handler> m_handler_status_changed;
|
||||
std::unique_ptr<interfaces::Handler> m_handler_address_book_changed;
|
||||
std::unique_ptr<interfaces::Handler> m_handler_transaction_changed;
|
||||
@ -271,6 +272,9 @@ Q_SIGNALS:
|
||||
// Watch-only address added
|
||||
void notifyWatchonlyChanged(bool fHaveWatchonly);
|
||||
|
||||
// Signal that wallet is about to be removed
|
||||
void unload();
|
||||
|
||||
public Q_SLOTS:
|
||||
/* Wallet status might have changed */
|
||||
void updateStatus();
|
||||
|
@ -6,6 +6,7 @@
|
||||
#ifdef ENABLE_WALLET
|
||||
#include <coinjoin/coinjoin-client.h>
|
||||
#include <coinjoin/coinjoin-client-options.h>
|
||||
#include <wallet/rpcwallet.h>
|
||||
#endif // ENABLE_WALLET
|
||||
#include <coinjoin/coinjoin-server.h>
|
||||
#include <rpc/server.h>
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
//! Make sure database has a unique fileid within the environment. If it
|
||||
//! doesn't, throw an error. BDB caches do not work properly when more than one
|
||||
//! open database has the same fileid (values written to one database may show
|
||||
@ -29,25 +30,19 @@ namespace {
|
||||
//! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html),
|
||||
//! so bitcoin should never create different databases with the same fileid, but
|
||||
//! this error can be triggered if users manually copy database files.
|
||||
void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db)
|
||||
void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db, WalletDatabaseFileId& fileid)
|
||||
{
|
||||
if (env.IsMock()) return;
|
||||
|
||||
u_int8_t fileid[DB_FILE_ID_LEN];
|
||||
int ret = db.get_mpf()->get_fileid(fileid);
|
||||
int ret = db.get_mpf()->get_fileid(fileid.value);
|
||||
if (ret != 0) {
|
||||
throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (get_fileid failed with %d)", filename, ret));
|
||||
}
|
||||
|
||||
for (const auto& item : env.mapDb) {
|
||||
u_int8_t item_fileid[DB_FILE_ID_LEN];
|
||||
if (item.second && item.second->get_mpf()->get_fileid(item_fileid) == 0 &&
|
||||
memcmp(fileid, item_fileid, sizeof(fileid)) == 0) {
|
||||
const char* item_filename = nullptr;
|
||||
item.second->get_dbname(&item_filename, nullptr);
|
||||
for (const auto& item : env.m_fileids) {
|
||||
if (fileid == item.second && &fileid != &item.second) {
|
||||
throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (duplicates fileid %s from %s)", filename,
|
||||
HexStr(std::begin(item_fileid), std::end(item_fileid)),
|
||||
item_filename ? item_filename : "(unknown database)"));
|
||||
HexStr(std::begin(item.second.value), std::end(item.second.value)), item.first));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -56,9 +51,13 @@ CCriticalSection cs_db;
|
||||
std::map<std::string, BerkeleyEnvironment> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to open db environment.
|
||||
} // namespace
|
||||
|
||||
BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename)
|
||||
bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
|
||||
{
|
||||
return memcmp(value, &rhs.value, sizeof(value)) == 0;
|
||||
}
|
||||
|
||||
static void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename)
|
||||
{
|
||||
fs::path env_directory;
|
||||
if (fs::is_regular_file(wallet_path)) {
|
||||
// Special case for backwards compatibility: if wallet path points to an
|
||||
// existing file, treat it as the path to a BDB data file in a parent
|
||||
@ -71,6 +70,23 @@ BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& data
|
||||
env_directory = wallet_path;
|
||||
database_filename = "wallet.dat";
|
||||
}
|
||||
}
|
||||
|
||||
bool IsWalletLoaded(const fs::path& wallet_path)
|
||||
{
|
||||
fs::path env_directory;
|
||||
std::string database_filename;
|
||||
SplitWalletPath(wallet_path, env_directory, database_filename);
|
||||
LOCK(cs_db);
|
||||
auto env = g_dbenvs.find(env_directory.string());
|
||||
if (env == g_dbenvs.end()) return false;
|
||||
return env->second.IsDatabaseLoaded(database_filename);
|
||||
}
|
||||
|
||||
BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename)
|
||||
{
|
||||
fs::path env_directory;
|
||||
SplitWalletPath(wallet_path, env_directory, database_filename);
|
||||
LOCK(cs_db);
|
||||
// Note: An ununsed temporary BerkeleyEnvironment object may be created inside the
|
||||
// emplace function if the key already exists. This is a little inefficient,
|
||||
@ -90,13 +106,13 @@ void BerkeleyEnvironment::Close()
|
||||
|
||||
fDbEnvInit = false;
|
||||
|
||||
for (auto& db : mapDb) {
|
||||
for (auto& db : m_databases) {
|
||||
auto count = mapFileUseCount.find(db.first);
|
||||
assert(count == mapFileUseCount.end() || count->second == 0);
|
||||
if (db.second) {
|
||||
db.second->close(0);
|
||||
delete db.second;
|
||||
db.second = nullptr;
|
||||
BerkeleyDatabase& database = db.second.get();
|
||||
if (database.m_db) {
|
||||
database.m_db->close(0);
|
||||
database.m_db.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@ -463,7 +479,7 @@ BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bo
|
||||
if (!env->Open(false /* retry */))
|
||||
throw std::runtime_error("BerkeleyBatch: Failed to open database environment.");
|
||||
|
||||
pdb = env->mapDb[strFilename];
|
||||
pdb = database.m_db.get();
|
||||
if (pdb == nullptr) {
|
||||
int ret;
|
||||
std::unique_ptr<Db> pdb_temp = MakeUnique<Db>(env->dbenv.get(), 0);
|
||||
@ -504,11 +520,11 @@ BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bo
|
||||
// versions of BDB have an set_lk_exclusive method for this
|
||||
// purpose, but the older version we use does not.)
|
||||
for (auto& env : g_dbenvs) {
|
||||
CheckUniqueFileid(env.second, strFilename, *pdb_temp);
|
||||
CheckUniqueFileid(env.second, strFilename, *pdb_temp, this->env->m_fileids[strFilename]);
|
||||
}
|
||||
|
||||
pdb = pdb_temp.release();
|
||||
env->mapDb[strFilename] = pdb;
|
||||
database.m_db.reset(pdb);
|
||||
|
||||
if (fCreate && !Exists(std::string("version"))) {
|
||||
bool fTmp = fReadOnly;
|
||||
@ -556,22 +572,50 @@ void BerkeleyBatch::Close()
|
||||
LOCK(cs_db);
|
||||
--env->mapFileUseCount[strFile];
|
||||
}
|
||||
env->m_db_in_use.notify_all();
|
||||
}
|
||||
|
||||
void BerkeleyEnvironment::CloseDb(const std::string& strFile)
|
||||
{
|
||||
{
|
||||
LOCK(cs_db);
|
||||
if (mapDb[strFile] != nullptr) {
|
||||
auto it = m_databases.find(strFile);
|
||||
assert(it != m_databases.end());
|
||||
BerkeleyDatabase& database = it->second.get();
|
||||
if (database.m_db) {
|
||||
// Close the database handle
|
||||
Db* pdb = mapDb[strFile];
|
||||
pdb->close(0);
|
||||
delete pdb;
|
||||
mapDb[strFile] = nullptr;
|
||||
database.m_db->close(0);
|
||||
database.m_db.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BerkeleyEnvironment::ReloadDbEnv()
|
||||
{
|
||||
// Make sure that no Db's are in use
|
||||
AssertLockNotHeld(cs_db);
|
||||
std::unique_lock<CCriticalSection> lock(cs_db);
|
||||
m_db_in_use.wait(lock, [this](){
|
||||
for (auto& count : mapFileUseCount) {
|
||||
if (count.second > 0) return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
std::vector<std::string> filenames;
|
||||
for (auto it : m_databases) {
|
||||
filenames.push_back(it.first);
|
||||
}
|
||||
// Close the individual Db's
|
||||
for (const std::string& filename : filenames) {
|
||||
CloseDb(filename);
|
||||
}
|
||||
// Reset the environment
|
||||
Flush(true); // This will flush and close the environment
|
||||
Reset();
|
||||
Open(true);
|
||||
}
|
||||
|
||||
bool BerkeleyBatch::Rewrite(BerkeleyDatabase& database, const char* pszSkip)
|
||||
{
|
||||
if (database.IsDummy()) {
|
||||
@ -694,11 +738,12 @@ void BerkeleyEnvironment::Flush(bool fShutdown)
|
||||
if (mapFileUseCount.empty()) {
|
||||
dbenv->log_archive(&listp, DB_ARCH_REMOVE);
|
||||
Close();
|
||||
if (!fMockDb)
|
||||
if (!fMockDb) {
|
||||
fs::remove_all(fs::path(strPath) / "database");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BerkeleyBatch::PeriodicFlush(BerkeleyDatabase& database)
|
||||
@ -794,5 +839,24 @@ void BerkeleyDatabase::Flush(bool shutdown)
|
||||
{
|
||||
if (!IsDummy()) {
|
||||
env->Flush(shutdown);
|
||||
if (shutdown) {
|
||||
LOCK(cs_db);
|
||||
g_dbenvs.erase(env->Directory().string());
|
||||
env = nullptr;
|
||||
} else {
|
||||
// TODO: To avoid g_dbenvs.erase erasing the environment prematurely after the
|
||||
// first database shutdown when multiple databases are open in the same
|
||||
// environment, should replace raw database `env` pointers with shared or weak
|
||||
// pointers, or else separate the database and environment shutdowns so
|
||||
// environments can be shut down after databases.
|
||||
env->m_fileids.erase(strFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BerkeleyDatabase::ReloadDbEnv()
|
||||
{
|
||||
if (!IsDummy()) {
|
||||
env->ReloadDbEnv();
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <db_cxx.h>
|
||||
@ -25,6 +26,13 @@
|
||||
static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
|
||||
static const bool DEFAULT_WALLET_PRIVDB = true;
|
||||
|
||||
struct WalletDatabaseFileId {
|
||||
u_int8_t value[DB_FILE_ID_LEN];
|
||||
bool operator==(const WalletDatabaseFileId& rhs) const;
|
||||
};
|
||||
|
||||
class BerkeleyDatabase;
|
||||
|
||||
class BerkeleyEnvironment
|
||||
{
|
||||
private:
|
||||
@ -37,7 +45,9 @@ private:
|
||||
public:
|
||||
std::unique_ptr<DbEnv> dbenv;
|
||||
std::map<std::string, int> mapFileUseCount;
|
||||
std::map<std::string, Db*> mapDb;
|
||||
std::map<std::string, std::reference_wrapper<BerkeleyDatabase>> m_databases;
|
||||
std::unordered_map<std::string, WalletDatabaseFileId> m_fileids;
|
||||
std::condition_variable_any m_db_in_use;
|
||||
|
||||
BerkeleyEnvironment(const fs::path& env_directory);
|
||||
~BerkeleyEnvironment();
|
||||
@ -46,6 +56,7 @@ public:
|
||||
void MakeMock();
|
||||
bool IsMock() const { return fMockDb; }
|
||||
bool IsInitialized() const { return fDbEnvInit; }
|
||||
bool IsDatabaseLoaded(const std::string& db_filename) const { return m_databases.find(db_filename) != m_databases.end(); }
|
||||
fs::path Directory() const { return strPath; }
|
||||
|
||||
/**
|
||||
@ -75,6 +86,7 @@ public:
|
||||
void CheckpointLSN(const std::string& strFile);
|
||||
|
||||
void CloseDb(const std::string& strFile);
|
||||
void ReloadDbEnv();
|
||||
|
||||
DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
|
||||
{
|
||||
@ -86,6 +98,9 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/** Return whether a wallet database is currently loaded. */
|
||||
bool IsWalletLoaded(const fs::path& wallet_path);
|
||||
|
||||
/** Get BerkeleyEnvironment and database filename given a wallet path. */
|
||||
BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
|
||||
|
||||
@ -106,6 +121,8 @@ public:
|
||||
nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0)
|
||||
{
|
||||
env = GetWalletEnv(wallet_path, strFile);
|
||||
auto inserted = env->m_databases.emplace(strFile, std::ref(*this));
|
||||
assert(inserted.second);
|
||||
if (mock) {
|
||||
env->Close();
|
||||
env->Reset();
|
||||
@ -113,6 +130,13 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
~BerkeleyDatabase() {
|
||||
if (env) {
|
||||
size_t erased = env->m_databases.erase(strFile);
|
||||
assert(erased == 1);
|
||||
}
|
||||
}
|
||||
|
||||
/** Return object for accessing database at specified path. */
|
||||
static std::unique_ptr<BerkeleyDatabase> Create(const fs::path& path)
|
||||
{
|
||||
@ -145,11 +169,16 @@ public:
|
||||
|
||||
void IncrementUpdateCounter();
|
||||
|
||||
void ReloadDbEnv();
|
||||
|
||||
std::atomic<unsigned int> nUpdateCounter;
|
||||
unsigned int nLastSeen;
|
||||
unsigned int nLastFlushed;
|
||||
int64_t nLastWalletUpdate;
|
||||
|
||||
/** Database pointer. This is initialized lazily and reset during flushes, so it can be null. */
|
||||
std::unique_ptr<Db> m_db;
|
||||
|
||||
private:
|
||||
/** BerkeleyDB specific */
|
||||
BerkeleyEnvironment *env;
|
||||
|
@ -375,15 +375,15 @@ bool WalletInit::Verify() const
|
||||
std::set<fs::path> wallet_paths;
|
||||
|
||||
for (const auto& wallet_file : wallet_files) {
|
||||
fs::path wallet_path = fs::absolute(wallet_file, GetWalletDir());
|
||||
WalletLocation location(wallet_file);
|
||||
|
||||
if (!wallet_paths.insert(wallet_path).second) {
|
||||
if (!wallet_paths.insert(location.GetPath()).second) {
|
||||
return InitError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file));
|
||||
}
|
||||
|
||||
std::string error_string;
|
||||
std::string warning_string;
|
||||
bool verify_success = CWallet::Verify(wallet_file, salvage_wallet, error_string, warning_string);
|
||||
bool verify_success = CWallet::Verify(location, salvage_wallet, error_string, warning_string);
|
||||
if (!error_string.empty()) InitError(error_string);
|
||||
if (!warning_string.empty()) InitWarning(warning_string);
|
||||
if (!verify_success) return false;
|
||||
@ -400,7 +400,7 @@ bool WalletInit::Open() const
|
||||
}
|
||||
|
||||
for (const std::string& walletFile : gArgs.GetArgs("-wallet")) {
|
||||
std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(walletFile, fs::absolute(walletFile, GetWalletDir()));
|
||||
std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(WalletLocation(walletFile));
|
||||
if (!pwallet) {
|
||||
return false;
|
||||
}
|
||||
|
@ -43,12 +43,21 @@
|
||||
|
||||
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
|
||||
|
||||
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
|
||||
bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name)
|
||||
{
|
||||
if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) {
|
||||
// wallet endpoint was used
|
||||
std::string requestedWallet = urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
|
||||
std::shared_ptr<CWallet> pwallet = GetWallet(requestedWallet);
|
||||
wallet_name = urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
|
||||
{
|
||||
std::string wallet_name;
|
||||
if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
|
||||
std::shared_ptr<CWallet> pwallet = GetWallet(wallet_name);
|
||||
if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
|
||||
return pwallet;
|
||||
}
|
||||
@ -69,11 +78,6 @@ bool EnsureWalletIsAvailable(CWallet * const pwallet, bool avoidException)
|
||||
if (pwallet) return true;
|
||||
if (avoidException) return false;
|
||||
if (!HasWallets()) {
|
||||
// Note: It isn't currently possible to trigger this error because
|
||||
// wallet RPC methods aren't registered unless a wallet is loaded. But
|
||||
// this error is being kept as a precaution, because it's possible in
|
||||
// the future that wallet RPC methods might get or remain registered
|
||||
// when no wallets are loaded.
|
||||
throw JSONRPCError(
|
||||
RPC_METHOD_NOT_FOUND, "Method not found (wallet method is disabled because no wallet is loaded)");
|
||||
}
|
||||
@ -2676,7 +2680,6 @@ UniValue encryptwallet(const JSONRPCRequest& request)
|
||||
"will require the passphrase to be set prior the making these calls.\n"
|
||||
"Use the walletpassphrase call for this, and then walletlock call.\n"
|
||||
"If the wallet is already encrypted, use the walletpassphrasechange call.\n"
|
||||
"Note that this will shutdown the server.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"passphrase\" (string) The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long.\n"
|
||||
"\nExamples:\n"
|
||||
@ -2714,11 +2717,7 @@ UniValue encryptwallet(const JSONRPCRequest& request)
|
||||
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
|
||||
}
|
||||
|
||||
// BDB seems to have a bad habit of writing old data into
|
||||
// slack space in .dat files; that is bad if the old data is
|
||||
// unencrypted private keys. So:
|
||||
StartShutdown();
|
||||
return "Wallet encrypted; Dash Core server stopping, restart to run with encrypted wallet. The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup.";
|
||||
return "wallet encrypted; The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup.";
|
||||
}
|
||||
|
||||
UniValue lockunspent(const JSONRPCRequest& request)
|
||||
@ -3227,14 +3226,6 @@ UniValue upgradetohd(const JSONRPCRequest& request)
|
||||
pwallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, true);
|
||||
}
|
||||
|
||||
if (!prev_encrypted && pwallet->IsCrypted()) {
|
||||
// BDB seems to have a bad habit of writing old data into
|
||||
// slack space in .dat files; that is bad if the old data is
|
||||
// unencrypted private keys. So:
|
||||
StartShutdown();
|
||||
return "Wallet successfully upgraded and encrypted, Dash Core server is stopping. Remember to make a backup before restarting.";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3293,7 +3284,7 @@ UniValue keepass(const JSONRPCRequest& request)
|
||||
return "Invalid command";
|
||||
}
|
||||
|
||||
UniValue loadwallet(const JSONRPCRequest& request)
|
||||
static UniValue loadwallet(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() != 1)
|
||||
throw std::runtime_error(
|
||||
@ -3312,26 +3303,26 @@ UniValue loadwallet(const JSONRPCRequest& request)
|
||||
+ HelpExampleCli("loadwallet", "\"test.dat\"")
|
||||
+ HelpExampleRpc("loadwallet", "\"test.dat\"")
|
||||
);
|
||||
std::string wallet_file = request.params[0].get_str();
|
||||
|
||||
WalletLocation location(request.params[0].get_str());
|
||||
std::string error;
|
||||
|
||||
fs::path wallet_path = fs::absolute(wallet_file, GetWalletDir());
|
||||
if (fs::symlink_status(wallet_path).type() == fs::file_not_found) {
|
||||
throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Wallet " + wallet_file + " not found.");
|
||||
} else if (fs::is_directory(wallet_path)) {
|
||||
if (!location.Exists()) {
|
||||
throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Wallet " + location.GetName() + " not found.");
|
||||
} else if (fs::is_directory(location.GetPath())) {
|
||||
// The given filename is a directory. Check that there's a wallet.dat file.
|
||||
fs::path wallet_dat_file = wallet_path / "wallet.dat";
|
||||
fs::path wallet_dat_file = location.GetPath() / "wallet.dat";
|
||||
if (fs::symlink_status(wallet_dat_file).type() == fs::file_not_found) {
|
||||
throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Directory " + wallet_file + " does not contain a wallet.dat file.");
|
||||
throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Directory " + location.GetName() + " does not contain a wallet.dat file.");
|
||||
}
|
||||
}
|
||||
|
||||
std::string warning;
|
||||
if (!CWallet::Verify(wallet_file, false, error, warning)) {
|
||||
if (!CWallet::Verify(location, false, error, warning)) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error);
|
||||
}
|
||||
|
||||
std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(wallet_file, fs::absolute(wallet_file, GetWalletDir()));
|
||||
std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(location);
|
||||
if (!wallet) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet loading failed.");
|
||||
}
|
||||
@ -3345,7 +3336,7 @@ UniValue loadwallet(const JSONRPCRequest& request)
|
||||
return obj;
|
||||
}
|
||||
|
||||
UniValue createwallet(const JSONRPCRequest& request)
|
||||
static UniValue createwallet(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() != 1) {
|
||||
throw std::runtime_error(
|
||||
@ -3363,21 +3354,20 @@ UniValue createwallet(const JSONRPCRequest& request)
|
||||
+ HelpExampleRpc("createwallet", "\"testwallet\"")
|
||||
);
|
||||
}
|
||||
std::string wallet_name = request.params[0].get_str();
|
||||
std::string error;
|
||||
std::string warning;
|
||||
|
||||
fs::path wallet_path = fs::absolute(wallet_name, GetWalletDir());
|
||||
if (fs::symlink_status(wallet_path).type() != fs::file_not_found) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet " + wallet_name + " already exists.");
|
||||
WalletLocation location(request.params[0].get_str());
|
||||
if (location.Exists()) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet " + location.GetName() + " already exists.");
|
||||
}
|
||||
|
||||
// Wallet::Verify will check if we're trying to create a wallet with a duplication name.
|
||||
if (!CWallet::Verify(wallet_name, false, error, warning)) {
|
||||
if (!CWallet::Verify(location, false, error, warning)) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error);
|
||||
}
|
||||
|
||||
const auto wallet = CWallet::CreateWalletFromFile(wallet_name, fs::absolute(wallet_name, GetWalletDir()));
|
||||
const auto wallet = CWallet::CreateWalletFromFile(location);
|
||||
if (!wallet) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed.");
|
||||
}
|
||||
@ -3392,6 +3382,55 @@ UniValue createwallet(const JSONRPCRequest& request)
|
||||
return obj;
|
||||
}
|
||||
|
||||
static UniValue unloadwallet(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() > 1) {
|
||||
throw std::runtime_error(
|
||||
"unloadwallet ( \"wallet_name\" )\n"
|
||||
"Unloads the wallet referenced by the request endpoint otherwise unloads the wallet specified in the argument.\n"
|
||||
"Specifying the wallet name on a wallet endpoint is invalid."
|
||||
"\nArguments:\n"
|
||||
"1. \"wallet_name\" (string, optional) The name of the wallet to unload.\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("unloadwallet", "wallet_name")
|
||||
+ HelpExampleRpc("unloadwallet", "wallet_name")
|
||||
);
|
||||
}
|
||||
|
||||
std::string wallet_name;
|
||||
if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
|
||||
if (!request.params[0].isNull()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot unload the requested wallet");
|
||||
}
|
||||
} else {
|
||||
wallet_name = request.params[0].get_str();
|
||||
}
|
||||
|
||||
std::shared_ptr<CWallet> wallet = GetWallet(wallet_name);
|
||||
if (!wallet) {
|
||||
throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
|
||||
}
|
||||
|
||||
// Release the "main" shared pointer and prevent further notifications.
|
||||
// Note that any attempt to load the same wallet would fail until the wallet
|
||||
// is destroyed (see CheckUniqueFileid).
|
||||
if (!RemoveWallet(wallet)) {
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
|
||||
}
|
||||
UnregisterValidationInterface(wallet.get());
|
||||
|
||||
// The wallet can be in use so it's not possible to explicitly unload here.
|
||||
// Just notify the unload intent so that all shared pointers are released.
|
||||
// The wallet will be destroyed once the last shared pointer is released.
|
||||
wallet->NotifyUnload();
|
||||
|
||||
// There's no point in waiting for the wallet to unload.
|
||||
// At this point this method should never fail. The unloading could only
|
||||
// fail due to an unexpected error which would cause a process termination.
|
||||
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
UniValue resendwallettransactions(const JSONRPCRequest& request)
|
||||
{
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
@ -4356,6 +4395,7 @@ static const CRPCCommand commands[] =
|
||||
{ "wallet", "setcoinjoinamount", &setcoinjoinamount, {"amount"} },
|
||||
{ "wallet", "signmessage", &signmessage, {"address","message"} },
|
||||
{ "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} },
|
||||
{ "wallet", "unloadwallet", &unloadwallet, {"wallet_name"} },
|
||||
{ "wallet", "upgradetohd", &upgradetohd, {"mnemonic", "mnemonicpassphrase", "walletpassphrase"} },
|
||||
{ "wallet", "walletlock", &walletlock, {} },
|
||||
{ "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} },
|
||||
|
@ -36,7 +36,7 @@ public:
|
||||
CTransactionBuilderTestSetup()
|
||||
{
|
||||
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
|
||||
wallet = MakeUnique<CWallet>("mock", WalletDatabase::CreateMock());
|
||||
wallet = MakeUnique<CWallet>(WalletLocation(), WalletDatabase::CreateMock());
|
||||
bool firstRun;
|
||||
wallet->LoadWallet(firstRun);
|
||||
AddWallet(wallet);
|
||||
|
@ -27,7 +27,7 @@ std::vector<std::unique_ptr<CWalletTx>> wtxn;
|
||||
typedef std::set<CInputCoin> CoinSet;
|
||||
|
||||
static std::vector<COutput> vCoins;
|
||||
static const CWallet testWallet("dummy", WalletDatabase::CreateDummy());
|
||||
static const CWallet testWallet(WalletLocation(), WalletDatabase::CreateDummy());
|
||||
static CAmount balance = 0;
|
||||
|
||||
CoinEligibilityFilter filter_standard(1, 6, 0);
|
||||
|
@ -6,9 +6,10 @@
|
||||
|
||||
#include <rpc/server.h>
|
||||
#include <wallet/db.h>
|
||||
#include <wallet/rpcwallet.h>
|
||||
|
||||
WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
|
||||
TestingSetup(chainName), m_wallet("mock", WalletDatabase::CreateMock())
|
||||
TestingSetup(chainName), m_wallet(WalletLocation(), WalletDatabase::CreateMock())
|
||||
{
|
||||
bool fFirstRun;
|
||||
m_wallet.LoadWallet(fFirstRun);
|
||||
|
@ -49,7 +49,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
|
||||
// Verify ScanForWalletTransactions picks up transactions in both the old
|
||||
// and new block files.
|
||||
{
|
||||
CWallet wallet("dummy", WalletDatabase::CreateDummy());
|
||||
CWallet wallet(WalletLocation(), WalletDatabase::CreateDummy());
|
||||
AddKey(wallet, coinbaseKey);
|
||||
WalletRescanReserver reserver(&wallet);
|
||||
reserver.reserve();
|
||||
@ -64,7 +64,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
|
||||
// Verify ScanForWalletTransactions only picks transactions in the new block
|
||||
// file.
|
||||
{
|
||||
CWallet wallet("dummy", WalletDatabase::CreateDummy());
|
||||
CWallet wallet(WalletLocation(), WalletDatabase::CreateDummy());
|
||||
AddKey(wallet, coinbaseKey);
|
||||
WalletRescanReserver reserver(&wallet);
|
||||
reserver.reserve();
|
||||
@ -76,7 +76,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
|
||||
// before the missing block, and success for a key whose creation time is
|
||||
// after.
|
||||
{
|
||||
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>("dummy", WalletDatabase::CreateDummy());
|
||||
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(WalletLocation(), WalletDatabase::CreateDummy());
|
||||
AddWallet(wallet);
|
||||
UniValue keys;
|
||||
keys.setArray();
|
||||
@ -137,7 +137,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
|
||||
|
||||
// Import key into wallet and call dumpwallet to create backup file.
|
||||
{
|
||||
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>("dummy", WalletDatabase::CreateDummy());
|
||||
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(WalletLocation(), WalletDatabase::CreateDummy());
|
||||
LOCK(wallet->cs_wallet);
|
||||
wallet->mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME;
|
||||
wallet->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
|
||||
@ -153,7 +153,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
|
||||
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
|
||||
// were scanned, and no prior blocks were scanned.
|
||||
{
|
||||
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>("dummy", WalletDatabase::CreateDummy());
|
||||
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(WalletLocation(), WalletDatabase::CreateDummy());
|
||||
|
||||
JSONRPCRequest request;
|
||||
request.params.setArray();
|
||||
@ -183,7 +183,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
|
||||
// debit functions.
|
||||
BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
|
||||
{
|
||||
CWallet wallet("dummy", WalletDatabase::CreateDummy());
|
||||
CWallet wallet(WalletLocation(), WalletDatabase::CreateDummy());
|
||||
CWalletTx wtx(&wallet, MakeTransactionRef(coinbaseTxns.back()));
|
||||
LOCK2(cs_main, wallet.cs_wallet);
|
||||
wtx.hashBlock = chainActive.Tip()->GetBlockHash();
|
||||
@ -276,7 +276,7 @@ public:
|
||||
ListCoinsTestingSetup()
|
||||
{
|
||||
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
|
||||
wallet = MakeUnique<CWallet>("mock", WalletDatabase::CreateMock());
|
||||
wallet = MakeUnique<CWallet>(WalletLocation(), WalletDatabase::CreateMock());
|
||||
bool firstRun;
|
||||
wallet->LoadWallet(firstRun);
|
||||
AddKey(*wallet, coinbaseKey);
|
||||
@ -395,7 +395,7 @@ public:
|
||||
CreateTransactionTestSetup()
|
||||
{
|
||||
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
|
||||
wallet = MakeUnique<CWallet>("mock", WalletDatabase::CreateMock());
|
||||
wallet = MakeUnique<CWallet>(WalletLocation(), WalletDatabase::CreateMock());
|
||||
bool firstRun;
|
||||
wallet->LoadWallet(firstRun);
|
||||
AddWallet(wallet);
|
||||
|
@ -28,7 +28,6 @@
|
||||
#include <txmempool.h>
|
||||
#include <utilmoneystr.h>
|
||||
#include <wallet/fees.h>
|
||||
#include <wallet/walletutil.h>
|
||||
|
||||
#include <coinjoin/coinjoin-client.h>
|
||||
#include <coinjoin/coinjoin-client-options.h>
|
||||
@ -111,6 +110,15 @@ CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE);
|
||||
|
||||
CFeeRate CWallet::m_discard_rate = CFeeRate(DEFAULT_DISCARD_FEE);
|
||||
|
||||
// Custom deleter for shared_ptr<CWallet>.
|
||||
static void ReleaseWallet(CWallet* wallet)
|
||||
{
|
||||
LogPrintf("Releasing wallet %s\n", wallet->GetName());
|
||||
wallet->BlockUntilSyncedToCurrentChain();
|
||||
wallet->Flush();
|
||||
delete wallet;
|
||||
}
|
||||
|
||||
const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
|
||||
|
||||
/** @defgroup mapWallet
|
||||
@ -913,6 +921,11 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
|
||||
}
|
||||
}
|
||||
|
||||
// BDB seems to have a bad habit of writing old data into
|
||||
// slack space in .dat files; that is bad if the old data is
|
||||
// unencrypted private keys. So:
|
||||
database->ReloadDbEnv();
|
||||
|
||||
}
|
||||
NotifyStatusChanged(this);
|
||||
|
||||
@ -1551,7 +1564,7 @@ void CWallet::BlockUntilSyncedToCurrentChain() {
|
||||
LOCK(cs_main);
|
||||
const CBlockIndex* initialChainTip = chainActive.Tip();
|
||||
|
||||
if (m_last_block_processed->GetAncestor(initialChainTip->nHeight) == initialChainTip) {
|
||||
if (m_last_block_processed && m_last_block_processed->GetAncestor(initialChainTip->nHeight) == initialChainTip) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -4913,7 +4926,7 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
|
||||
return values;
|
||||
}
|
||||
|
||||
bool CWallet::Verify(std::string wallet_file, bool salvage_wallet, std::string& error_string, std::string& warning_string)
|
||||
bool CWallet::Verify(const WalletLocation& location, bool salvage_wallet, std::string& error_string, std::string& warning_string)
|
||||
{
|
||||
// Do some checking on wallet path. It should be either a:
|
||||
//
|
||||
@ -4922,44 +4935,42 @@ bool CWallet::Verify(std::string wallet_file, bool salvage_wallet, std::string&
|
||||
// 3. Path to a symlink to a directory.
|
||||
// 4. For backwards compatibility, the name of a data file in -walletdir.
|
||||
LOCK(cs_wallets);
|
||||
fs::path wallet_path = fs::absolute(wallet_file, GetWalletDir());
|
||||
const fs::path& wallet_path = location.GetPath();
|
||||
fs::file_type path_type = fs::symlink_status(wallet_path).type();
|
||||
if (!(path_type == fs::file_not_found || path_type == fs::directory_file ||
|
||||
(path_type == fs::symlink_file && fs::is_directory(wallet_path)) ||
|
||||
(path_type == fs::regular_file && fs::path(wallet_file).filename() == wallet_file))) {
|
||||
(path_type == fs::regular_file && fs::path(location.GetName()).filename() == location.GetName()))) {
|
||||
error_string =strprintf(
|
||||
"Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and "
|
||||
"database/log.?????????? files can be stored, a location where such a directory could be created, "
|
||||
"or (for backwards compatibility) the name of an existing data file in -walletdir (%s)",
|
||||
wallet_file, GetWalletDir());
|
||||
location.GetName(), GetWalletDir());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure that the wallet path doesn't clash with an existing wallet path
|
||||
for (auto wallet : GetWallets()) {
|
||||
if (fs::absolute(wallet->GetName(), GetWalletDir()) == wallet_path) {
|
||||
error_string = strprintf("Error loading wallet %s. Duplicate -wallet filename specified.", wallet_file);
|
||||
if (IsWalletLoaded(wallet_path)) {
|
||||
error_string = strprintf("Error loading wallet %s. Duplicate -wallet filename specified.", location.GetName());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (!WalletBatch::VerifyEnvironment(wallet_path, error_string)) {
|
||||
return false;
|
||||
}
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
error_string = strprintf("Error loading wallet %s. %s", wallet_file, e.what());
|
||||
error_string = strprintf("Error loading wallet %s. %s", location.GetName(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(wallet_file, WalletDatabase::Create(wallet_path));
|
||||
std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(location, WalletDatabase::Create(wallet_path));
|
||||
if (!tempWallet->AutoBackupWallet(wallet_path, warning_string, error_string) && !error_string.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (salvage_wallet) {
|
||||
// Recover readable keypairs:
|
||||
CWallet dummyWallet("dummy", WalletDatabase::CreateDummy());
|
||||
CWallet dummyWallet(WalletLocation(), WalletDatabase::CreateDummy());
|
||||
std::string backup_filename;
|
||||
if (!WalletBatch::Recover(wallet_path, (void *)&dummyWallet, WalletBatch::RecoverKeysOnlyFilter, backup_filename)) {
|
||||
return false;
|
||||
@ -4969,9 +4980,9 @@ bool CWallet::Verify(std::string wallet_file, bool salvage_wallet, std::string&
|
||||
return WalletBatch::VerifyDatabaseFile(wallet_path, warning_string, error_string);
|
||||
}
|
||||
|
||||
std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path)
|
||||
std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const WalletLocation& location)
|
||||
{
|
||||
const std::string& walletFile = name;
|
||||
const std::string& walletFile = location.GetName();
|
||||
|
||||
// needed to restore wallet transaction meta data after -zapwallettxes
|
||||
std::vector<CWalletTx> vWtx;
|
||||
@ -4979,7 +4990,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
|
||||
if (gArgs.GetBoolArg("-zapwallettxes", false)) {
|
||||
uiInterface.InitMessage(_("Zapping all transactions from wallet..."));
|
||||
|
||||
std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(name, WalletDatabase::Create(path));
|
||||
std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(location, WalletDatabase::Create(location.GetPath()));
|
||||
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
|
||||
if (nZapWalletRet != DBErrors::LOAD_OK) {
|
||||
InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
|
||||
@ -4991,7 +5002,9 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
|
||||
|
||||
int64_t nStart = GetTimeMillis();
|
||||
bool fFirstRun = true;
|
||||
std::shared_ptr<CWallet> walletInstance = std::make_shared<CWallet>(name, WalletDatabase::Create(path));
|
||||
// TODO: Can't use std::make_shared because we need a custom deleter but
|
||||
// should be possible to use std::allocate_shared.
|
||||
std::shared_ptr<CWallet> walletInstance(new CWallet(location, WalletDatabase::Create(location.GetPath())), ReleaseWallet);
|
||||
AddWallet(walletInstance);
|
||||
auto error = [&](const std::string& strError) {
|
||||
RemoveWallet(walletInstance);
|
||||
@ -5028,8 +5041,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
|
||||
}
|
||||
}
|
||||
|
||||
uiInterface.LoadWallet(walletInstance);
|
||||
|
||||
if (gArgs.GetBoolArg("-upgradewallet", fFirstRun))
|
||||
{
|
||||
int nMaxVersion = gArgs.GetArg("-upgradewallet", 0);
|
||||
@ -5204,6 +5215,8 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
|
||||
}
|
||||
}
|
||||
|
||||
uiInterface.LoadWallet(walletInstance);
|
||||
|
||||
// Register with the validation interface. It's ok to do this after rescan since we're still holding cs_main.
|
||||
RegisterValidationInterface(walletInstance.get());
|
||||
|
||||
@ -5249,7 +5262,10 @@ bool CWallet::BackupWallet(const std::string& strDest)
|
||||
bool CWallet::AutoBackupWallet(const fs::path& wallet_path, std::string& strBackupWarningRet, std::string& strBackupErrorRet)
|
||||
{
|
||||
strBackupWarningRet = strBackupErrorRet = "";
|
||||
std::string strWalletName = m_name.empty() ? "wallet.dat" : m_name;
|
||||
std::string strWalletName = GetName();
|
||||
if (strWalletName.empty()) {
|
||||
strWalletName = "wallet.dat";
|
||||
}
|
||||
|
||||
if (nWalletBackups <= 0) {
|
||||
LogPrintf("Automatic wallet backups are disabled!\n");
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include <wallet/crypter.h>
|
||||
#include <wallet/coinselection.h>
|
||||
#include <wallet/walletdb.h>
|
||||
#include <wallet/rpcwallet.h>
|
||||
#include <wallet/walletutil.h>
|
||||
|
||||
#include <coinjoin/coinjoin.h>
|
||||
#include <governance/governance-object.h>
|
||||
@ -789,12 +789,8 @@ private:
|
||||
*/
|
||||
bool AddWatchOnly(const CScript& dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
|
||||
/**
|
||||
* Wallet filename from wallet=<path> command line or config option.
|
||||
* Used in debug logs and to send RPCs to the right wallet instance when
|
||||
* more than one wallet is loaded.
|
||||
*/
|
||||
std::string m_name;
|
||||
/** Wallet location which includes wallet name (see WalletLocation). */
|
||||
WalletLocation m_location;
|
||||
|
||||
/** Internal database handle. */
|
||||
std::unique_ptr<WalletDatabase> database;
|
||||
@ -844,9 +840,11 @@ public:
|
||||
return *database;
|
||||
}
|
||||
|
||||
const WalletLocation& GetLocation() const { return m_location; }
|
||||
|
||||
/** Get a name for this wallet for logging/debugging purposes.
|
||||
*/
|
||||
const std::string& GetName() const { return m_name; }
|
||||
const std::string& GetName() const { return m_location.GetName(); }
|
||||
|
||||
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
|
||||
@ -864,7 +862,7 @@ public:
|
||||
unsigned int nMasterKeyMaxID = 0;
|
||||
|
||||
/** Construct wallet with specified name and database implementation. */
|
||||
CWallet(std::string name, std::unique_ptr<WalletDatabase> database) : m_name(std::move(name)), database(std::move(database))
|
||||
CWallet(const WalletLocation& location, std::unique_ptr<WalletDatabase> database) : m_location(location), database(std::move(database))
|
||||
{
|
||||
}
|
||||
|
||||
@ -1164,6 +1162,9 @@ public:
|
||||
//! Flush wallet (bitdb flush)
|
||||
void Flush(bool shutdown=false);
|
||||
|
||||
/** Wallet is about to be unloaded */
|
||||
boost::signals2::signal<void ()> NotifyUnload;
|
||||
|
||||
/**
|
||||
* Address book entry changed.
|
||||
* @note called with lock cs_wallet held.
|
||||
@ -1204,10 +1205,10 @@ public:
|
||||
bool AbandonTransaction(const uint256& hashTx);
|
||||
|
||||
//! Verify wallet naming and perform salvage on the wallet if required
|
||||
static bool Verify(std::string wallet_file, bool salvage_wallet, std::string& error_string, std::string& warning_string);
|
||||
static bool Verify(const WalletLocation& location, bool salvage_wallet, std::string& error_string, std::string& warning_string);
|
||||
|
||||
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
|
||||
static std::shared_ptr<CWallet> CreateWalletFromFile(const std::string& name, const fs::path& path);
|
||||
static std::shared_ptr<CWallet> CreateWalletFromFile(const WalletLocation& location);
|
||||
|
||||
/**
|
||||
* Wallet post-init setup
|
||||
|
@ -25,3 +25,14 @@ fs::path GetWalletDir()
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
WalletLocation::WalletLocation(const std::string& name)
|
||||
: m_name(name)
|
||||
, m_path(fs::absolute(name, GetWalletDir()))
|
||||
{
|
||||
}
|
||||
|
||||
bool WalletLocation::Exists() const
|
||||
{
|
||||
return fs::symlink_status(m_path).type() != fs::file_not_found;
|
||||
}
|
||||
|
@ -11,4 +11,24 @@
|
||||
//! Get the path of the wallet directory.
|
||||
fs::path GetWalletDir();
|
||||
|
||||
//! The WalletLocation class provides wallet information.
|
||||
class WalletLocation final
|
||||
{
|
||||
std::string m_name;
|
||||
fs::path m_path;
|
||||
|
||||
public:
|
||||
explicit WalletLocation() {}
|
||||
explicit WalletLocation(const std::string& name);
|
||||
|
||||
//! Get wallet name.
|
||||
const std::string& GetName() const { return m_name; }
|
||||
|
||||
//! Get wallet absolute path.
|
||||
const fs::path& GetPath() const { return m_path; }
|
||||
|
||||
//! Return whether the wallet exists.
|
||||
bool Exists() const;
|
||||
};
|
||||
|
||||
#endif // BITCOIN_WALLET_WALLETUTIL_H
|
||||
|
@ -463,10 +463,8 @@ class RawTransactionsTest(BitcoinTestFramework):
|
||||
|
||||
############################################################
|
||||
# locked wallet test
|
||||
self.stop_node(0)
|
||||
self.nodes[1].node_encrypt_wallet("test")
|
||||
self.stop_node(2)
|
||||
self.stop_node(3)
|
||||
self.nodes[1].encryptwallet("test")
|
||||
self.stop_nodes()
|
||||
|
||||
self.start_nodes()
|
||||
# This test is not meant to test fee estimation and we'd like
|
||||
|
@ -439,10 +439,8 @@ class RawTransactionsTest(BitcoinTestFramework):
|
||||
|
||||
############################################################
|
||||
# locked wallet test
|
||||
self.stop_node(0)
|
||||
self.stop_node(2)
|
||||
self.stop_node(3)
|
||||
self.nodes[1].node_encrypt_wallet("test")
|
||||
self.nodes[1].encryptwallet("test")
|
||||
self.stop_nodes()
|
||||
|
||||
self.start_nodes()
|
||||
# This test is not meant to test fee estimation and we'd like
|
||||
|
@ -264,14 +264,6 @@ class TestNode():
|
||||
assert_msg = "dashd should have exited with expected error " + expected_msg
|
||||
self._raise_assertion_error(assert_msg)
|
||||
|
||||
def node_encrypt_wallet(self, passphrase):
|
||||
""""Encrypts the wallet.
|
||||
|
||||
This causes dashd to shutdown, so this method takes
|
||||
care of cleaning up resources."""
|
||||
self.encryptwallet(passphrase)
|
||||
self.wait_until_stopped()
|
||||
|
||||
def add_p2p_connection(self, p2p_conn, *args, **kwargs):
|
||||
"""Add a p2p connection to the node.
|
||||
|
||||
|
@ -120,8 +120,7 @@ class WalletDumpTest(BitcoinTestFramework):
|
||||
assert_equal(found_addr_rsv, 180) # keypool size (external+internal)
|
||||
|
||||
#encrypt wallet, restart, unlock and dump
|
||||
self.nodes[0].node_encrypt_wallet('test')
|
||||
self.start_node(0)
|
||||
self.nodes[0].encryptwallet('test')
|
||||
self.nodes[0].walletpassphrase('test', 30)
|
||||
# Should be a no-op:
|
||||
self.nodes[0].keypoolrefill()
|
||||
|
@ -30,8 +30,7 @@ class WalletEncryptionTest(BitcoinTestFramework):
|
||||
assert_equal(len(privkey), 52)
|
||||
|
||||
# Encrypt the wallet
|
||||
self.nodes[0].node_encrypt_wallet(passphrase)
|
||||
self.start_node(0)
|
||||
self.nodes[0].encryptwallet(passphrase)
|
||||
|
||||
# Test that the wallet is encrypted
|
||||
assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address)
|
||||
|
@ -16,9 +16,7 @@ class KeyPoolTest(BitcoinTestFramework):
|
||||
nodes = self.nodes
|
||||
|
||||
# Encrypt wallet and wait to terminate
|
||||
nodes[0].node_encrypt_wallet('test')
|
||||
# Restart node 0
|
||||
self.start_node(0)
|
||||
nodes[0].encryptwallet('test')
|
||||
# Keep creating keys
|
||||
addr = nodes[0].getnewaddress()
|
||||
|
||||
|
@ -28,9 +28,7 @@ class KeyPoolTest(BitcoinTestFramework):
|
||||
assert(addr_before_encrypting_data['hdchainid'] == wallet_info_old['hdchainid'])
|
||||
|
||||
# Encrypt wallet and wait to terminate
|
||||
nodes[0].node_encrypt_wallet('test')
|
||||
# Restart node 0
|
||||
self.start_node(0)
|
||||
nodes[0].encryptwallet('test')
|
||||
# Keep creating keys
|
||||
addr = nodes[0].getnewaddress()
|
||||
addr_data = nodes[0].getaddressinfo(addr)
|
||||
|
@ -199,9 +199,16 @@ class MultiWalletTest(BitcoinTestFramework):
|
||||
# Fail to load duplicate wallets
|
||||
assert_raises_rpc_error(-4, 'Wallet file verification failed: Error loading wallet w1. Duplicate -wallet filename specified.', self.nodes[0].loadwallet, wallet_names[0])
|
||||
|
||||
# Fail to load duplicate wallets by different ways (directory and filepath)
|
||||
assert_raises_rpc_error(-4, "Wallet file verification failed: Error loading wallet wallet.dat. Duplicate -wallet filename specified.", self.nodes[0].loadwallet, 'wallet.dat')
|
||||
|
||||
# Fail to load if one wallet is a copy of another
|
||||
assert_raises_rpc_error(-1, "BerkeleyBatch: Can't open database w8_copy (duplicates fileid", self.nodes[0].loadwallet, 'w8_copy')
|
||||
|
||||
# Fail to load if one wallet is a copy of another, test this twice to make sure that we don't re-introduce #14304
|
||||
assert_raises_rpc_error(-1, "BerkeleyBatch: Can't open database w8_copy (duplicates fileid", self.nodes[0].loadwallet, 'w8_copy')
|
||||
|
||||
|
||||
# Fail to load if wallet file is a symlink
|
||||
assert_raises_rpc_error(-4, "Wallet file verification failed: Invalid -wallet path 'w8_symlink'", self.nodes[0].loadwallet, 'w8_symlink')
|
||||
|
||||
@ -232,5 +239,32 @@ class MultiWalletTest(BitcoinTestFramework):
|
||||
os.mkdir(wallet_dir('empty_wallet_dir'))
|
||||
assert_raises_rpc_error(-18, "Directory empty_wallet_dir does not contain a wallet.dat file", self.nodes[0].loadwallet, 'empty_wallet_dir')
|
||||
|
||||
self.log.info("Test dynamic wallet unloading")
|
||||
|
||||
# Test `unloadwallet` errors
|
||||
assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].unloadwallet)
|
||||
assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", self.nodes[0].unloadwallet, "dummy")
|
||||
assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", node.get_wallet_rpc("dummy").unloadwallet)
|
||||
assert_raises_rpc_error(-8, "Cannot unload the requested wallet", w1.unloadwallet, "w2"),
|
||||
|
||||
# Successfully unload the specified wallet name
|
||||
self.nodes[0].unloadwallet("w1")
|
||||
assert 'w1' not in self.nodes[0].listwallets()
|
||||
|
||||
# Successfully unload the wallet referenced by the request endpoint
|
||||
w2.unloadwallet()
|
||||
assert 'w2' not in self.nodes[0].listwallets()
|
||||
|
||||
# Successfully unload all wallets
|
||||
for wallet_name in self.nodes[0].listwallets():
|
||||
self.nodes[0].unloadwallet(wallet_name)
|
||||
assert_equal(self.nodes[0].listwallets(), [])
|
||||
assert_raises_rpc_error(-32601, "Method not found (wallet method is disabled because no wallet is loaded)", self.nodes[0].getwalletinfo)
|
||||
|
||||
# Successfully load a previously unloaded wallet
|
||||
self.nodes[0].loadwallet('w1')
|
||||
assert_equal(self.nodes[0].listwallets(), ['w1'])
|
||||
assert_equal(w1.getwalletinfo()['walletname'], 'w1')
|
||||
|
||||
if __name__ == '__main__':
|
||||
MultiWalletTest().main()
|
||||
|
@ -139,8 +139,8 @@ class WalletUpgradeToHDTest(BitcoinTestFramework):
|
||||
self.log.info("Same mnemonic, same mnemonic passphrase, encrypt wallet on upgrade, should recover all coins after rescan")
|
||||
walletpass = "111pass222"
|
||||
# Upgrading and encrypting at the saame time results in a warning
|
||||
assert_equal(node.upgradetohd(mnemonic, "", walletpass), "Wallet successfully upgraded and encrypted, Dash Core server is stopping. Remember to make a backup before restarting.")
|
||||
# Wallet encryption results in node shutdown
|
||||
assert node.upgradetohd(mnemonic, "", walletpass)
|
||||
node.stop()
|
||||
node.wait_until_stopped()
|
||||
self.start_node(0, extra_args=['-rescan'])
|
||||
assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", node.dumphdinfo)
|
||||
@ -161,13 +161,12 @@ class WalletUpgradeToHDTest(BitcoinTestFramework):
|
||||
self.log.info("Same mnemonic, same mnemonic passphrase, encrypt wallet first, should recover all coins on upgrade after rescan")
|
||||
walletpass = "111pass222"
|
||||
node.encryptwallet(walletpass)
|
||||
# Wallet encryption results in node shutdown
|
||||
node.stop()
|
||||
node.wait_until_stopped()
|
||||
self.start_node(0, extra_args=['-rescan'])
|
||||
assert_raises_rpc_error(-14, "Cannot upgrade encrypted wallet to HD without the wallet passphrase", node.upgradetohd, mnemonic)
|
||||
assert_raises_rpc_error(-14, "The wallet passphrase entered was incorrect", node.upgradetohd, mnemonic, "", "wrongpass")
|
||||
assert(node.upgradetohd(mnemonic, "", walletpass))
|
||||
# Note: upgrading an already encrypted wallet does not result in node shutdown
|
||||
assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", node.dumphdinfo)
|
||||
node.walletpassphrase(walletpass, 100)
|
||||
assert_equal(mnemonic, node.dumphdinfo()['mnemonic'])
|
||||
|
@ -28,7 +28,6 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
|
||||
"txmempool -> validation -> txmempool"
|
||||
"validation -> validationinterface -> validation"
|
||||
"wallet/fees -> wallet/wallet -> wallet/fees"
|
||||
"wallet/rpcwallet -> wallet/wallet -> wallet/rpcwallet"
|
||||
"wallet/wallet -> wallet/walletdb -> wallet/wallet"
|
||||
"policy/fees -> policy/policy -> validation -> policy/fees"
|
||||
"qt/addressbookpage -> qt/bitcoingui -> qt/walletview -> qt/addressbookpage"
|
||||
|
Loading…
Reference in New Issue
Block a user