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)
|
// (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484)
|
||||||
static void CoinSelection(benchmark::State& state)
|
static void CoinSelection(benchmark::State& state)
|
||||||
{
|
{
|
||||||
const CWallet wallet("dummy", WalletDatabase::CreateDummy());
|
const CWallet wallet(WalletLocation(), WalletDatabase::CreateDummy());
|
||||||
std::vector<COutput> vCoins;
|
std::vector<COutput> vCoins;
|
||||||
LOCK(wallet.cs_wallet);
|
LOCK(wallet.cs_wallet);
|
||||||
|
|
||||||
|
@ -484,6 +484,10 @@ public:
|
|||||||
}
|
}
|
||||||
bool hdEnabled() override { return m_wallet.IsHDEnabled(); }
|
bool hdEnabled() override { return m_wallet.IsHDEnabled(); }
|
||||||
CoinJoin::Client& coinJoin() override { return m_coinjoin; }
|
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
|
std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) override
|
||||||
{
|
{
|
||||||
return MakeHandler(m_wallet.ShowProgress.connect(fn));
|
return MakeHandler(m_wallet.ShowProgress.connect(fn));
|
||||||
|
@ -249,6 +249,10 @@ public:
|
|||||||
|
|
||||||
virtual CoinJoin::Client& coinJoin() = 0;
|
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.
|
//! Register handler for show progress messages.
|
||||||
using ShowProgressFn = std::function<void(const std::string& title, int progress)>;
|
using ShowProgressFn = std::function<void(const std::string& title, int progress)>;
|
||||||
virtual std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) = 0;
|
virtual std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) = 0;
|
||||||
|
@ -140,9 +140,9 @@ void AskPassphraseDialog::accept()
|
|||||||
if (model->wallet().hdEnabled()) {
|
if (model->wallet().hdEnabled()) {
|
||||||
QMessageBox::warning(this, tr("Wallet encrypted"),
|
QMessageBox::warning(this, tr("Wallet encrypted"),
|
||||||
"<qt>" +
|
"<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 "
|
"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>" +
|
"<br><br><b>" +
|
||||||
tr("IMPORTANT: Any previous backups you have made of your wallet file "
|
tr("IMPORTANT: Any previous backups you have made of your wallet file "
|
||||||
"should be replaced with the newly generated, encrypted wallet file. "
|
"should be replaced with the newly generated, encrypted wallet file. "
|
||||||
@ -152,9 +152,9 @@ void AskPassphraseDialog::accept()
|
|||||||
} else {
|
} else {
|
||||||
QMessageBox::warning(this, tr("Wallet encrypted"),
|
QMessageBox::warning(this, tr("Wallet encrypted"),
|
||||||
"<qt>" +
|
"<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 "
|
"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>" +
|
"<br><br><b>" +
|
||||||
tr("IMPORTANT: Any previous backups you have made of your wallet file "
|
tr("IMPORTANT: Any previous backups you have made of your wallet file "
|
||||||
"should be replaced with the newly generated, encrypted 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.") +
|
"will become useless as soon as you start using the new, encrypted wallet.") +
|
||||||
"</b></qt>");
|
"</b></qt>");
|
||||||
}
|
}
|
||||||
QApplication::quit();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -639,8 +639,19 @@ void BitcoinGUI::createToolBars()
|
|||||||
|
|
||||||
#ifdef ENABLE_WALLET
|
#ifdef ENABLE_WALLET
|
||||||
m_wallet_selector = new QComboBox(this);
|
m_wallet_selector = new QComboBox(this);
|
||||||
m_wallet_selector->setHidden(true);
|
connect(m_wallet_selector, SIGNAL(currentIndexChanged(int)), this, SLOT(setCurrentWalletBySelectorIndex(int)));
|
||||||
connect(m_wallet_selector, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(setCurrentWallet(const QString&)));
|
|
||||||
|
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
|
#endif
|
||||||
|
|
||||||
QLabel *logoLabel = new QLabel();
|
QLabel *logoLabel = new QLabel();
|
||||||
@ -769,25 +780,31 @@ bool BitcoinGUI::addWallet(WalletModel *walletModel)
|
|||||||
if(!walletFrame)
|
if(!walletFrame)
|
||||||
return false;
|
return false;
|
||||||
const QString name = walletModel->getWalletName();
|
const QString name = walletModel->getWalletName();
|
||||||
|
QString display_name = name.isEmpty() ? "["+tr("default wallet")+"]" : name;
|
||||||
setWalletActionsEnabled(true);
|
setWalletActionsEnabled(true);
|
||||||
m_wallet_selector->addItem(name);
|
m_wallet_selector->addItem(display_name, name);
|
||||||
if (m_wallet_selector->count() == 2) {
|
if (m_wallet_selector->count() == 2) {
|
||||||
m_wallet_selector->setHidden(false);
|
m_wallet_selector_action->setVisible(true);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
rpcConsole->addWallet(walletModel);
|
rpcConsole->addWallet(walletModel);
|
||||||
return walletFrame->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)
|
bool BitcoinGUI::setCurrentWallet(const QString& name)
|
||||||
{
|
{
|
||||||
if(!walletFrame)
|
if(!walletFrame)
|
||||||
@ -795,6 +812,12 @@ bool BitcoinGUI::setCurrentWallet(const QString& name)
|
|||||||
return walletFrame->setCurrentWallet(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()
|
void BitcoinGUI::removeAllWallets()
|
||||||
{
|
{
|
||||||
if(!walletFrame)
|
if(!walletFrame)
|
||||||
|
@ -76,6 +76,7 @@ public:
|
|||||||
functionality.
|
functionality.
|
||||||
*/
|
*/
|
||||||
bool addWallet(WalletModel *walletModel);
|
bool addWallet(WalletModel *walletModel);
|
||||||
|
bool removeWallet(WalletModel* walletModel);
|
||||||
void removeAllWallets();
|
void removeAllWallets();
|
||||||
#endif // ENABLE_WALLET
|
#endif // ENABLE_WALLET
|
||||||
bool enableWallet;
|
bool enableWallet;
|
||||||
@ -140,8 +141,8 @@ private:
|
|||||||
QAction *openAction;
|
QAction *openAction;
|
||||||
QAction *showHelpMessageAction;
|
QAction *showHelpMessageAction;
|
||||||
QAction *showCoinJoinHelpAction;
|
QAction *showCoinJoinHelpAction;
|
||||||
|
QAction *m_wallet_selector_action = nullptr;
|
||||||
|
|
||||||
QLabel *m_wallet_selector_label;
|
|
||||||
QComboBox *m_wallet_selector;
|
QComboBox *m_wallet_selector;
|
||||||
|
|
||||||
QSystemTrayIcon *trayIcon;
|
QSystemTrayIcon *trayIcon;
|
||||||
@ -238,6 +239,7 @@ public Q_SLOTS:
|
|||||||
|
|
||||||
#ifdef ENABLE_WALLET
|
#ifdef ENABLE_WALLET
|
||||||
bool setCurrentWallet(const QString& name);
|
bool setCurrentWallet(const QString& name);
|
||||||
|
bool setCurrentWalletBySelectorIndex(int index);
|
||||||
/** Set the UI status indicators based on the currently selected wallet.
|
/** Set the UI status indicators based on the currently selected wallet.
|
||||||
*/
|
*/
|
||||||
void updateWalletStatus();
|
void updateWalletStatus();
|
||||||
|
@ -212,6 +212,7 @@ public Q_SLOTS:
|
|||||||
/// Handle runaway exceptions. Shows a message box with the problem and quits the program.
|
/// Handle runaway exceptions. Shows a message box with the problem and quits the program.
|
||||||
void handleRunawayException(const QString &message);
|
void handleRunawayException(const QString &message);
|
||||||
void addWallet(WalletModel* walletModel);
|
void addWallet(WalletModel* walletModel);
|
||||||
|
void removeWallet();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void requestedInitialize();
|
void requestedInitialize();
|
||||||
@ -454,11 +455,22 @@ void BitcoinApplication::addWallet(WalletModel* walletModel)
|
|||||||
|
|
||||||
connect(walletModel, SIGNAL(coinsSent(WalletModel*, SendCoinsRecipient, QByteArray)),
|
connect(walletModel, SIGNAL(coinsSent(WalletModel*, SendCoinsRecipient, QByteArray)),
|
||||||
paymentServer, SLOT(fetchPaymentACK(WalletModel*, const SendCoinsRecipient&, QByteArray)));
|
paymentServer, SLOT(fetchPaymentACK(WalletModel*, const SendCoinsRecipient&, QByteArray)));
|
||||||
|
connect(walletModel, SIGNAL(unload()), this, SLOT(removeWallet()));
|
||||||
|
|
||||||
m_wallet_models.push_back(walletModel);
|
m_wallet_models.push_back(walletModel);
|
||||||
#endif
|
#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)
|
void BitcoinApplication::initializeResult(bool success)
|
||||||
{
|
{
|
||||||
qDebug() << __func__ << ": Initialization result: " << success;
|
qDebug() << __func__ << ": Initialization result: " << success;
|
||||||
@ -478,8 +490,10 @@ void BitcoinApplication::initializeResult(bool success)
|
|||||||
|
|
||||||
#ifdef ENABLE_WALLET
|
#ifdef ENABLE_WALLET
|
||||||
m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) {
|
m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) {
|
||||||
QMetaObject::invokeMethod(this, "addWallet", Qt::QueuedConnection,
|
WalletModel* wallet_model = new WalletModel(std::move(wallet), m_node, optionsModel, nullptr);
|
||||||
Q_ARG(WalletModel*, new WalletModel(std::move(wallet), m_node, optionsModel)));
|
// 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()) {
|
for (auto& wallet : m_node.getWallets()) {
|
||||||
|
@ -680,6 +680,10 @@ QToolBar > QToolButton:checked {
|
|||||||
color: #c7c7c7;
|
color: #c7c7c7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QToolBar > QToolButton:disabled {
|
||||||
|
color: #4a4a4b;
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************
|
/******************************************************
|
||||||
QToolTip
|
QToolTip
|
||||||
******************************************************/
|
******************************************************/
|
||||||
|
@ -665,6 +665,10 @@ QToolBar > QToolButton:checked {
|
|||||||
color: #555;
|
color: #555;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QToolBar > QToolButton:disabled {
|
||||||
|
color: #a7a7a7;
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************
|
/******************************************************
|
||||||
QToolTip
|
QToolTip
|
||||||
******************************************************/
|
******************************************************/
|
||||||
|
@ -743,7 +743,8 @@ void RPCConsole::addWallet(WalletModel * const walletModel)
|
|||||||
{
|
{
|
||||||
const QString name = walletModel->getWalletName();
|
const QString name = walletModel->getWalletName();
|
||||||
// use name for text and internal data object (to allow to move to a wallet id later)
|
// 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()) {
|
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)
|
// First wallet added, set to default so long as the window isn't presently visible (and potentially in use)
|
||||||
ui->WalletSelector->setCurrentIndex(1);
|
ui->WalletSelector->setCurrentIndex(1);
|
||||||
@ -753,6 +754,16 @@ void RPCConsole::addWallet(WalletModel * const walletModel)
|
|||||||
ui->WalletSelectorLabel->setVisible(true);
|
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
|
#endif
|
||||||
|
|
||||||
static QString categoryClass(int category)
|
static QString categoryClass(int category)
|
||||||
|
@ -49,6 +49,7 @@ public:
|
|||||||
|
|
||||||
void setClientModel(ClientModel *model);
|
void setClientModel(ClientModel *model);
|
||||||
void addWallet(WalletModel * const walletModel);
|
void addWallet(WalletModel * const walletModel);
|
||||||
|
void removeWallet(WalletModel* const walletModel);
|
||||||
|
|
||||||
enum MessageClass {
|
enum MessageClass {
|
||||||
MC_ERROR,
|
MC_ERROR,
|
||||||
|
@ -119,7 +119,7 @@ void TestGUI()
|
|||||||
for (int i = 0; i < 5; ++i) {
|
for (int i = 0; i < 5; ++i) {
|
||||||
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
|
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);
|
AddWallet(wallet);
|
||||||
bool firstRun;
|
bool firstRun;
|
||||||
wallet->LoadWallet(firstRun);
|
wallet->LoadWallet(firstRun);
|
||||||
|
@ -93,6 +93,7 @@ bool WalletFrame::removeWallet(const QString &name)
|
|||||||
|
|
||||||
WalletView *walletView = mapWalletViews.take(name);
|
WalletView *walletView = mapWalletViews.take(name);
|
||||||
walletStack->removeWidget(walletView);
|
walletStack->removeWidget(walletView);
|
||||||
|
delete walletView;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,6 +427,12 @@ int64_t WalletModel::getKeysLeftSinceAutoBackup() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handlers for core signals
|
// Handlers for core signals
|
||||||
|
static void NotifyUnload(WalletModel* walletModel)
|
||||||
|
{
|
||||||
|
qDebug() << "NotifyUnload";
|
||||||
|
QMetaObject::invokeMethod(walletModel, "unload", Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel)
|
static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel)
|
||||||
{
|
{
|
||||||
qDebug() << "NotifyKeyStoreStatusChanged";
|
qDebug() << "NotifyKeyStoreStatusChanged";
|
||||||
@ -485,6 +491,7 @@ static void NotifyWatchonlyChanged(WalletModel *walletmodel, bool fHaveWatchonly
|
|||||||
void WalletModel::subscribeToCoreSignals()
|
void WalletModel::subscribeToCoreSignals()
|
||||||
{
|
{
|
||||||
// Connect signals to wallet
|
// 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_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_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));
|
m_handler_transaction_changed = m_wallet->handleTransactionChanged(boost::bind(NotifyTransactionChanged, this, _1, _2));
|
||||||
@ -497,6 +504,7 @@ void WalletModel::subscribeToCoreSignals()
|
|||||||
void WalletModel::unsubscribeFromCoreSignals()
|
void WalletModel::unsubscribeFromCoreSignals()
|
||||||
{
|
{
|
||||||
// Disconnect signals from wallet
|
// Disconnect signals from wallet
|
||||||
|
m_handler_unload->disconnect();
|
||||||
m_handler_status_changed->disconnect();
|
m_handler_status_changed->disconnect();
|
||||||
m_handler_address_book_changed->disconnect();
|
m_handler_address_book_changed->disconnect();
|
||||||
m_handler_transaction_changed->disconnect();
|
m_handler_transaction_changed->disconnect();
|
||||||
|
@ -214,6 +214,7 @@ public:
|
|||||||
bool isMultiwallet();
|
bool isMultiwallet();
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<interfaces::Wallet> m_wallet;
|
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_status_changed;
|
||||||
std::unique_ptr<interfaces::Handler> m_handler_address_book_changed;
|
std::unique_ptr<interfaces::Handler> m_handler_address_book_changed;
|
||||||
std::unique_ptr<interfaces::Handler> m_handler_transaction_changed;
|
std::unique_ptr<interfaces::Handler> m_handler_transaction_changed;
|
||||||
@ -271,6 +272,9 @@ Q_SIGNALS:
|
|||||||
// Watch-only address added
|
// Watch-only address added
|
||||||
void notifyWatchonlyChanged(bool fHaveWatchonly);
|
void notifyWatchonlyChanged(bool fHaveWatchonly);
|
||||||
|
|
||||||
|
// Signal that wallet is about to be removed
|
||||||
|
void unload();
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
/* Wallet status might have changed */
|
/* Wallet status might have changed */
|
||||||
void updateStatus();
|
void updateStatus();
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#ifdef ENABLE_WALLET
|
#ifdef ENABLE_WALLET
|
||||||
#include <coinjoin/coinjoin-client.h>
|
#include <coinjoin/coinjoin-client.h>
|
||||||
#include <coinjoin/coinjoin-client-options.h>
|
#include <coinjoin/coinjoin-client-options.h>
|
||||||
|
#include <wallet/rpcwallet.h>
|
||||||
#endif // ENABLE_WALLET
|
#endif // ENABLE_WALLET
|
||||||
#include <coinjoin/coinjoin-server.h>
|
#include <coinjoin/coinjoin-server.h>
|
||||||
#include <rpc/server.h>
|
#include <rpc/server.h>
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include <boost/thread.hpp>
|
#include <boost/thread.hpp>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
//! Make sure database has a unique fileid within the environment. If it
|
//! 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
|
//! 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
|
//! 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),
|
//! (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
|
//! so bitcoin should never create different databases with the same fileid, but
|
||||||
//! this error can be triggered if users manually copy database files.
|
//! 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;
|
if (env.IsMock()) return;
|
||||||
|
|
||||||
u_int8_t fileid[DB_FILE_ID_LEN];
|
int ret = db.get_mpf()->get_fileid(fileid.value);
|
||||||
int ret = db.get_mpf()->get_fileid(fileid);
|
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (get_fileid failed with %d)", filename, ret));
|
throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (get_fileid failed with %d)", filename, ret));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& item : env.mapDb) {
|
for (const auto& item : env.m_fileids) {
|
||||||
u_int8_t item_fileid[DB_FILE_ID_LEN];
|
if (fileid == item.second && &fileid != &item.second) {
|
||||||
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);
|
|
||||||
throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (duplicates fileid %s from %s)", filename,
|
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)),
|
HexStr(std::begin(item.second.value), std::end(item.second.value)), item.first));
|
||||||
item_filename ? item_filename : "(unknown database)"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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.
|
std::map<std::string, BerkeleyEnvironment> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to open db environment.
|
||||||
} // namespace
|
} // 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)) {
|
if (fs::is_regular_file(wallet_path)) {
|
||||||
// Special case for backwards compatibility: if wallet path points to an
|
// 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
|
// 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;
|
env_directory = wallet_path;
|
||||||
database_filename = "wallet.dat";
|
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);
|
LOCK(cs_db);
|
||||||
// Note: An ununsed temporary BerkeleyEnvironment object may be created inside the
|
// Note: An ununsed temporary BerkeleyEnvironment object may be created inside the
|
||||||
// emplace function if the key already exists. This is a little inefficient,
|
// emplace function if the key already exists. This is a little inefficient,
|
||||||
@ -90,13 +106,13 @@ void BerkeleyEnvironment::Close()
|
|||||||
|
|
||||||
fDbEnvInit = false;
|
fDbEnvInit = false;
|
||||||
|
|
||||||
for (auto& db : mapDb) {
|
for (auto& db : m_databases) {
|
||||||
auto count = mapFileUseCount.find(db.first);
|
auto count = mapFileUseCount.find(db.first);
|
||||||
assert(count == mapFileUseCount.end() || count->second == 0);
|
assert(count == mapFileUseCount.end() || count->second == 0);
|
||||||
if (db.second) {
|
BerkeleyDatabase& database = db.second.get();
|
||||||
db.second->close(0);
|
if (database.m_db) {
|
||||||
delete db.second;
|
database.m_db->close(0);
|
||||||
db.second = nullptr;
|
database.m_db.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -463,7 +479,7 @@ BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bo
|
|||||||
if (!env->Open(false /* retry */))
|
if (!env->Open(false /* retry */))
|
||||||
throw std::runtime_error("BerkeleyBatch: Failed to open database environment.");
|
throw std::runtime_error("BerkeleyBatch: Failed to open database environment.");
|
||||||
|
|
||||||
pdb = env->mapDb[strFilename];
|
pdb = database.m_db.get();
|
||||||
if (pdb == nullptr) {
|
if (pdb == nullptr) {
|
||||||
int ret;
|
int ret;
|
||||||
std::unique_ptr<Db> pdb_temp = MakeUnique<Db>(env->dbenv.get(), 0);
|
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
|
// versions of BDB have an set_lk_exclusive method for this
|
||||||
// purpose, but the older version we use does not.)
|
// purpose, but the older version we use does not.)
|
||||||
for (auto& env : g_dbenvs) {
|
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();
|
pdb = pdb_temp.release();
|
||||||
env->mapDb[strFilename] = pdb;
|
database.m_db.reset(pdb);
|
||||||
|
|
||||||
if (fCreate && !Exists(std::string("version"))) {
|
if (fCreate && !Exists(std::string("version"))) {
|
||||||
bool fTmp = fReadOnly;
|
bool fTmp = fReadOnly;
|
||||||
@ -556,22 +572,50 @@ void BerkeleyBatch::Close()
|
|||||||
LOCK(cs_db);
|
LOCK(cs_db);
|
||||||
--env->mapFileUseCount[strFile];
|
--env->mapFileUseCount[strFile];
|
||||||
}
|
}
|
||||||
|
env->m_db_in_use.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BerkeleyEnvironment::CloseDb(const std::string& strFile)
|
void BerkeleyEnvironment::CloseDb(const std::string& strFile)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
LOCK(cs_db);
|
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
|
// Close the database handle
|
||||||
Db* pdb = mapDb[strFile];
|
database.m_db->close(0);
|
||||||
pdb->close(0);
|
database.m_db.reset();
|
||||||
delete pdb;
|
|
||||||
mapDb[strFile] = nullptr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
bool BerkeleyBatch::Rewrite(BerkeleyDatabase& database, const char* pszSkip)
|
||||||
{
|
{
|
||||||
if (database.IsDummy()) {
|
if (database.IsDummy()) {
|
||||||
@ -694,8 +738,9 @@ void BerkeleyEnvironment::Flush(bool fShutdown)
|
|||||||
if (mapFileUseCount.empty()) {
|
if (mapFileUseCount.empty()) {
|
||||||
dbenv->log_archive(&listp, DB_ARCH_REMOVE);
|
dbenv->log_archive(&listp, DB_ARCH_REMOVE);
|
||||||
Close();
|
Close();
|
||||||
if (!fMockDb)
|
if (!fMockDb) {
|
||||||
fs::remove_all(fs::path(strPath) / "database");
|
fs::remove_all(fs::path(strPath) / "database");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -794,5 +839,24 @@ void BerkeleyDatabase::Flush(bool shutdown)
|
|||||||
{
|
{
|
||||||
if (!IsDummy()) {
|
if (!IsDummy()) {
|
||||||
env->Flush(shutdown);
|
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 <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <db_cxx.h>
|
#include <db_cxx.h>
|
||||||
@ -25,6 +26,13 @@
|
|||||||
static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
|
static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
|
||||||
static const bool DEFAULT_WALLET_PRIVDB = true;
|
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
|
class BerkeleyEnvironment
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@ -37,7 +45,9 @@ private:
|
|||||||
public:
|
public:
|
||||||
std::unique_ptr<DbEnv> dbenv;
|
std::unique_ptr<DbEnv> dbenv;
|
||||||
std::map<std::string, int> mapFileUseCount;
|
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(const fs::path& env_directory);
|
||||||
~BerkeleyEnvironment();
|
~BerkeleyEnvironment();
|
||||||
@ -46,6 +56,7 @@ public:
|
|||||||
void MakeMock();
|
void MakeMock();
|
||||||
bool IsMock() const { return fMockDb; }
|
bool IsMock() const { return fMockDb; }
|
||||||
bool IsInitialized() const { return fDbEnvInit; }
|
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; }
|
fs::path Directory() const { return strPath; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,6 +86,7 @@ public:
|
|||||||
void CheckpointLSN(const std::string& strFile);
|
void CheckpointLSN(const std::string& strFile);
|
||||||
|
|
||||||
void CloseDb(const std::string& strFile);
|
void CloseDb(const std::string& strFile);
|
||||||
|
void ReloadDbEnv();
|
||||||
|
|
||||||
DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
|
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. */
|
/** Get BerkeleyEnvironment and database filename given a wallet path. */
|
||||||
BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
|
BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
|
||||||
|
|
||||||
@ -106,6 +121,8 @@ public:
|
|||||||
nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0)
|
nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0)
|
||||||
{
|
{
|
||||||
env = GetWalletEnv(wallet_path, strFile);
|
env = GetWalletEnv(wallet_path, strFile);
|
||||||
|
auto inserted = env->m_databases.emplace(strFile, std::ref(*this));
|
||||||
|
assert(inserted.second);
|
||||||
if (mock) {
|
if (mock) {
|
||||||
env->Close();
|
env->Close();
|
||||||
env->Reset();
|
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. */
|
/** Return object for accessing database at specified path. */
|
||||||
static std::unique_ptr<BerkeleyDatabase> Create(const fs::path& path)
|
static std::unique_ptr<BerkeleyDatabase> Create(const fs::path& path)
|
||||||
{
|
{
|
||||||
@ -145,11 +169,16 @@ public:
|
|||||||
|
|
||||||
void IncrementUpdateCounter();
|
void IncrementUpdateCounter();
|
||||||
|
|
||||||
|
void ReloadDbEnv();
|
||||||
|
|
||||||
std::atomic<unsigned int> nUpdateCounter;
|
std::atomic<unsigned int> nUpdateCounter;
|
||||||
unsigned int nLastSeen;
|
unsigned int nLastSeen;
|
||||||
unsigned int nLastFlushed;
|
unsigned int nLastFlushed;
|
||||||
int64_t nLastWalletUpdate;
|
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:
|
private:
|
||||||
/** BerkeleyDB specific */
|
/** BerkeleyDB specific */
|
||||||
BerkeleyEnvironment *env;
|
BerkeleyEnvironment *env;
|
||||||
|
@ -375,15 +375,15 @@ bool WalletInit::Verify() const
|
|||||||
std::set<fs::path> wallet_paths;
|
std::set<fs::path> wallet_paths;
|
||||||
|
|
||||||
for (const auto& wallet_file : wallet_files) {
|
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));
|
return InitError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string error_string;
|
std::string error_string;
|
||||||
std::string warning_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 (!error_string.empty()) InitError(error_string);
|
||||||
if (!warning_string.empty()) InitWarning(warning_string);
|
if (!warning_string.empty()) InitWarning(warning_string);
|
||||||
if (!verify_success) return false;
|
if (!verify_success) return false;
|
||||||
@ -400,7 +400,7 @@ bool WalletInit::Open() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const std::string& walletFile : gArgs.GetArgs("-wallet")) {
|
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) {
|
if (!pwallet) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -43,12 +43,21 @@
|
|||||||
|
|
||||||
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
|
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) {
|
if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) {
|
||||||
// wallet endpoint was used
|
// wallet endpoint was used
|
||||||
std::string requestedWallet = urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
|
wallet_name = urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
|
||||||
std::shared_ptr<CWallet> pwallet = GetWallet(requestedWallet);
|
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");
|
if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
|
||||||
return pwallet;
|
return pwallet;
|
||||||
}
|
}
|
||||||
@ -69,11 +78,6 @@ bool EnsureWalletIsAvailable(CWallet * const pwallet, bool avoidException)
|
|||||||
if (pwallet) return true;
|
if (pwallet) return true;
|
||||||
if (avoidException) return false;
|
if (avoidException) return false;
|
||||||
if (!HasWallets()) {
|
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(
|
throw JSONRPCError(
|
||||||
RPC_METHOD_NOT_FOUND, "Method not found (wallet method is disabled because no wallet is loaded)");
|
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"
|
"will require the passphrase to be set prior the making these calls.\n"
|
||||||
"Use the walletpassphrase call for this, and then walletlock call.\n"
|
"Use the walletpassphrase call for this, and then walletlock call.\n"
|
||||||
"If the wallet is already encrypted, use the walletpassphrasechange call.\n"
|
"If the wallet is already encrypted, use the walletpassphrasechange call.\n"
|
||||||
"Note that this will shutdown the server.\n"
|
|
||||||
"\nArguments:\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"
|
"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"
|
"\nExamples:\n"
|
||||||
@ -2714,11 +2717,7 @@ UniValue encryptwallet(const JSONRPCRequest& request)
|
|||||||
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
|
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// BDB seems to have a bad habit of writing old data into
|
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.";
|
||||||
// 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.";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UniValue lockunspent(const JSONRPCRequest& request)
|
UniValue lockunspent(const JSONRPCRequest& request)
|
||||||
@ -3227,14 +3226,6 @@ UniValue upgradetohd(const JSONRPCRequest& request)
|
|||||||
pwallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, true);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3293,7 +3284,7 @@ UniValue keepass(const JSONRPCRequest& request)
|
|||||||
return "Invalid command";
|
return "Invalid command";
|
||||||
}
|
}
|
||||||
|
|
||||||
UniValue loadwallet(const JSONRPCRequest& request)
|
static UniValue loadwallet(const JSONRPCRequest& request)
|
||||||
{
|
{
|
||||||
if (request.fHelp || request.params.size() != 1)
|
if (request.fHelp || request.params.size() != 1)
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
@ -3312,26 +3303,26 @@ UniValue loadwallet(const JSONRPCRequest& request)
|
|||||||
+ HelpExampleCli("loadwallet", "\"test.dat\"")
|
+ HelpExampleCli("loadwallet", "\"test.dat\"")
|
||||||
+ HelpExampleRpc("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;
|
std::string error;
|
||||||
|
|
||||||
fs::path wallet_path = fs::absolute(wallet_file, GetWalletDir());
|
if (!location.Exists()) {
|
||||||
if (fs::symlink_status(wallet_path).type() == fs::file_not_found) {
|
throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Wallet " + location.GetName() + " not found.");
|
||||||
throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Wallet " + wallet_file + " not found.");
|
} else if (fs::is_directory(location.GetPath())) {
|
||||||
} else if (fs::is_directory(wallet_path)) {
|
|
||||||
// The given filename is a directory. Check that there's a wallet.dat file.
|
// 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) {
|
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;
|
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);
|
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) {
|
if (!wallet) {
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet loading failed.");
|
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet loading failed.");
|
||||||
}
|
}
|
||||||
@ -3345,7 +3336,7 @@ UniValue loadwallet(const JSONRPCRequest& request)
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
UniValue createwallet(const JSONRPCRequest& request)
|
static UniValue createwallet(const JSONRPCRequest& request)
|
||||||
{
|
{
|
||||||
if (request.fHelp || request.params.size() != 1) {
|
if (request.fHelp || request.params.size() != 1) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
@ -3363,21 +3354,20 @@ UniValue createwallet(const JSONRPCRequest& request)
|
|||||||
+ HelpExampleRpc("createwallet", "\"testwallet\"")
|
+ HelpExampleRpc("createwallet", "\"testwallet\"")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
std::string wallet_name = request.params[0].get_str();
|
|
||||||
std::string error;
|
std::string error;
|
||||||
std::string warning;
|
std::string warning;
|
||||||
|
|
||||||
fs::path wallet_path = fs::absolute(wallet_name, GetWalletDir());
|
WalletLocation location(request.params[0].get_str());
|
||||||
if (fs::symlink_status(wallet_path).type() != fs::file_not_found) {
|
if (location.Exists()) {
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet " + wallet_name + " already 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.
|
// 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);
|
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) {
|
if (!wallet) {
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed.");
|
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed.");
|
||||||
}
|
}
|
||||||
@ -3392,6 +3382,55 @@ UniValue createwallet(const JSONRPCRequest& request)
|
|||||||
return obj;
|
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)
|
UniValue resendwallettransactions(const JSONRPCRequest& request)
|
||||||
{
|
{
|
||||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||||
@ -4356,6 +4395,7 @@ static const CRPCCommand commands[] =
|
|||||||
{ "wallet", "setcoinjoinamount", &setcoinjoinamount, {"amount"} },
|
{ "wallet", "setcoinjoinamount", &setcoinjoinamount, {"amount"} },
|
||||||
{ "wallet", "signmessage", &signmessage, {"address","message"} },
|
{ "wallet", "signmessage", &signmessage, {"address","message"} },
|
||||||
{ "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} },
|
{ "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} },
|
||||||
|
{ "wallet", "unloadwallet", &unloadwallet, {"wallet_name"} },
|
||||||
{ "wallet", "upgradetohd", &upgradetohd, {"mnemonic", "mnemonicpassphrase", "walletpassphrase"} },
|
{ "wallet", "upgradetohd", &upgradetohd, {"mnemonic", "mnemonicpassphrase", "walletpassphrase"} },
|
||||||
{ "wallet", "walletlock", &walletlock, {} },
|
{ "wallet", "walletlock", &walletlock, {} },
|
||||||
{ "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} },
|
{ "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} },
|
||||||
|
@ -36,7 +36,7 @@ public:
|
|||||||
CTransactionBuilderTestSetup()
|
CTransactionBuilderTestSetup()
|
||||||
{
|
{
|
||||||
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
|
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
|
||||||
wallet = MakeUnique<CWallet>("mock", WalletDatabase::CreateMock());
|
wallet = MakeUnique<CWallet>(WalletLocation(), WalletDatabase::CreateMock());
|
||||||
bool firstRun;
|
bool firstRun;
|
||||||
wallet->LoadWallet(firstRun);
|
wallet->LoadWallet(firstRun);
|
||||||
AddWallet(wallet);
|
AddWallet(wallet);
|
||||||
|
@ -27,7 +27,7 @@ std::vector<std::unique_ptr<CWalletTx>> wtxn;
|
|||||||
typedef std::set<CInputCoin> CoinSet;
|
typedef std::set<CInputCoin> CoinSet;
|
||||||
|
|
||||||
static std::vector<COutput> vCoins;
|
static std::vector<COutput> vCoins;
|
||||||
static const CWallet testWallet("dummy", WalletDatabase::CreateDummy());
|
static const CWallet testWallet(WalletLocation(), WalletDatabase::CreateDummy());
|
||||||
static CAmount balance = 0;
|
static CAmount balance = 0;
|
||||||
|
|
||||||
CoinEligibilityFilter filter_standard(1, 6, 0);
|
CoinEligibilityFilter filter_standard(1, 6, 0);
|
||||||
|
@ -6,9 +6,10 @@
|
|||||||
|
|
||||||
#include <rpc/server.h>
|
#include <rpc/server.h>
|
||||||
#include <wallet/db.h>
|
#include <wallet/db.h>
|
||||||
|
#include <wallet/rpcwallet.h>
|
||||||
|
|
||||||
WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
|
WalletTestingSetup::WalletTestingSetup(const std::string& chainName):
|
||||||
TestingSetup(chainName), m_wallet("mock", WalletDatabase::CreateMock())
|
TestingSetup(chainName), m_wallet(WalletLocation(), WalletDatabase::CreateMock())
|
||||||
{
|
{
|
||||||
bool fFirstRun;
|
bool fFirstRun;
|
||||||
m_wallet.LoadWallet(fFirstRun);
|
m_wallet.LoadWallet(fFirstRun);
|
||||||
|
@ -49,7 +49,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
|
|||||||
// Verify ScanForWalletTransactions picks up transactions in both the old
|
// Verify ScanForWalletTransactions picks up transactions in both the old
|
||||||
// and new block files.
|
// and new block files.
|
||||||
{
|
{
|
||||||
CWallet wallet("dummy", WalletDatabase::CreateDummy());
|
CWallet wallet(WalletLocation(), WalletDatabase::CreateDummy());
|
||||||
AddKey(wallet, coinbaseKey);
|
AddKey(wallet, coinbaseKey);
|
||||||
WalletRescanReserver reserver(&wallet);
|
WalletRescanReserver reserver(&wallet);
|
||||||
reserver.reserve();
|
reserver.reserve();
|
||||||
@ -64,7 +64,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup)
|
|||||||
// Verify ScanForWalletTransactions only picks transactions in the new block
|
// Verify ScanForWalletTransactions only picks transactions in the new block
|
||||||
// file.
|
// file.
|
||||||
{
|
{
|
||||||
CWallet wallet("dummy", WalletDatabase::CreateDummy());
|
CWallet wallet(WalletLocation(), WalletDatabase::CreateDummy());
|
||||||
AddKey(wallet, coinbaseKey);
|
AddKey(wallet, coinbaseKey);
|
||||||
WalletRescanReserver reserver(&wallet);
|
WalletRescanReserver reserver(&wallet);
|
||||||
reserver.reserve();
|
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
|
// before the missing block, and success for a key whose creation time is
|
||||||
// after.
|
// 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);
|
AddWallet(wallet);
|
||||||
UniValue keys;
|
UniValue keys;
|
||||||
keys.setArray();
|
keys.setArray();
|
||||||
@ -137,7 +137,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
|
|||||||
|
|
||||||
// Import key into wallet and call dumpwallet to create backup file.
|
// 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);
|
LOCK(wallet->cs_wallet);
|
||||||
wallet->mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME;
|
wallet->mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME;
|
||||||
wallet->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
|
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
|
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
|
||||||
// were scanned, and no prior blocks were scanned.
|
// 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;
|
JSONRPCRequest request;
|
||||||
request.params.setArray();
|
request.params.setArray();
|
||||||
@ -183,7 +183,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
|
|||||||
// debit functions.
|
// debit functions.
|
||||||
BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
|
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()));
|
CWalletTx wtx(&wallet, MakeTransactionRef(coinbaseTxns.back()));
|
||||||
LOCK2(cs_main, wallet.cs_wallet);
|
LOCK2(cs_main, wallet.cs_wallet);
|
||||||
wtx.hashBlock = chainActive.Tip()->GetBlockHash();
|
wtx.hashBlock = chainActive.Tip()->GetBlockHash();
|
||||||
@ -276,7 +276,7 @@ public:
|
|||||||
ListCoinsTestingSetup()
|
ListCoinsTestingSetup()
|
||||||
{
|
{
|
||||||
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
|
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
|
||||||
wallet = MakeUnique<CWallet>("mock", WalletDatabase::CreateMock());
|
wallet = MakeUnique<CWallet>(WalletLocation(), WalletDatabase::CreateMock());
|
||||||
bool firstRun;
|
bool firstRun;
|
||||||
wallet->LoadWallet(firstRun);
|
wallet->LoadWallet(firstRun);
|
||||||
AddKey(*wallet, coinbaseKey);
|
AddKey(*wallet, coinbaseKey);
|
||||||
@ -395,7 +395,7 @@ public:
|
|||||||
CreateTransactionTestSetup()
|
CreateTransactionTestSetup()
|
||||||
{
|
{
|
||||||
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
|
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
|
||||||
wallet = MakeUnique<CWallet>("mock", WalletDatabase::CreateMock());
|
wallet = MakeUnique<CWallet>(WalletLocation(), WalletDatabase::CreateMock());
|
||||||
bool firstRun;
|
bool firstRun;
|
||||||
wallet->LoadWallet(firstRun);
|
wallet->LoadWallet(firstRun);
|
||||||
AddWallet(wallet);
|
AddWallet(wallet);
|
||||||
|
@ -28,7 +28,6 @@
|
|||||||
#include <txmempool.h>
|
#include <txmempool.h>
|
||||||
#include <utilmoneystr.h>
|
#include <utilmoneystr.h>
|
||||||
#include <wallet/fees.h>
|
#include <wallet/fees.h>
|
||||||
#include <wallet/walletutil.h>
|
|
||||||
|
|
||||||
#include <coinjoin/coinjoin-client.h>
|
#include <coinjoin/coinjoin-client.h>
|
||||||
#include <coinjoin/coinjoin-client-options.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);
|
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"));
|
const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
|
||||||
|
|
||||||
/** @defgroup mapWallet
|
/** @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);
|
NotifyStatusChanged(this);
|
||||||
|
|
||||||
@ -1551,7 +1564,7 @@ void CWallet::BlockUntilSyncedToCurrentChain() {
|
|||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
const CBlockIndex* initialChainTip = chainActive.Tip();
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4913,7 +4926,7 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
|
|||||||
return values;
|
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:
|
// Do some checking on wallet path. It should be either a:
|
||||||
//
|
//
|
||||||
@ -4922,25 +4935,23 @@ bool CWallet::Verify(std::string wallet_file, bool salvage_wallet, std::string&
|
|||||||
// 3. Path to a symlink to a directory.
|
// 3. Path to a symlink to a directory.
|
||||||
// 4. For backwards compatibility, the name of a data file in -walletdir.
|
// 4. For backwards compatibility, the name of a data file in -walletdir.
|
||||||
LOCK(cs_wallets);
|
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();
|
fs::file_type path_type = fs::symlink_status(wallet_path).type();
|
||||||
if (!(path_type == fs::file_not_found || path_type == fs::directory_file ||
|
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::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(
|
error_string =strprintf(
|
||||||
"Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and "
|
"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, "
|
"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)",
|
"or (for backwards compatibility) the name of an existing data file in -walletdir (%s)",
|
||||||
wallet_file, GetWalletDir());
|
location.GetName(), GetWalletDir());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that the wallet path doesn't clash with an existing wallet path
|
// Make sure that the wallet path doesn't clash with an existing wallet path
|
||||||
for (auto wallet : GetWallets()) {
|
if (IsWalletLoaded(wallet_path)) {
|
||||||
if (fs::absolute(wallet->GetName(), GetWalletDir()) == wallet_path) {
|
error_string = strprintf("Error loading wallet %s. Duplicate -wallet filename specified.", location.GetName());
|
||||||
error_string = strprintf("Error loading wallet %s. Duplicate -wallet filename specified.", wallet_file);
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -4948,18 +4959,18 @@ bool CWallet::Verify(std::string wallet_file, bool salvage_wallet, std::string&
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (const fs::filesystem_error& e) {
|
} 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;
|
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()) {
|
if (!tempWallet->AutoBackupWallet(wallet_path, warning_string, error_string) && !error_string.empty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (salvage_wallet) {
|
if (salvage_wallet) {
|
||||||
// Recover readable keypairs:
|
// Recover readable keypairs:
|
||||||
CWallet dummyWallet("dummy", WalletDatabase::CreateDummy());
|
CWallet dummyWallet(WalletLocation(), WalletDatabase::CreateDummy());
|
||||||
std::string backup_filename;
|
std::string backup_filename;
|
||||||
if (!WalletBatch::Recover(wallet_path, (void *)&dummyWallet, WalletBatch::RecoverKeysOnlyFilter, backup_filename)) {
|
if (!WalletBatch::Recover(wallet_path, (void *)&dummyWallet, WalletBatch::RecoverKeysOnlyFilter, backup_filename)) {
|
||||||
return false;
|
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);
|
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
|
// needed to restore wallet transaction meta data after -zapwallettxes
|
||||||
std::vector<CWalletTx> vWtx;
|
std::vector<CWalletTx> vWtx;
|
||||||
@ -4979,7 +4990,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
|
|||||||
if (gArgs.GetBoolArg("-zapwallettxes", false)) {
|
if (gArgs.GetBoolArg("-zapwallettxes", false)) {
|
||||||
uiInterface.InitMessage(_("Zapping all transactions from wallet..."));
|
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);
|
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
|
||||||
if (nZapWalletRet != DBErrors::LOAD_OK) {
|
if (nZapWalletRet != DBErrors::LOAD_OK) {
|
||||||
InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
|
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();
|
int64_t nStart = GetTimeMillis();
|
||||||
bool fFirstRun = true;
|
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);
|
AddWallet(walletInstance);
|
||||||
auto error = [&](const std::string& strError) {
|
auto error = [&](const std::string& strError) {
|
||||||
RemoveWallet(walletInstance);
|
RemoveWallet(walletInstance);
|
||||||
@ -5028,8 +5041,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uiInterface.LoadWallet(walletInstance);
|
|
||||||
|
|
||||||
if (gArgs.GetBoolArg("-upgradewallet", fFirstRun))
|
if (gArgs.GetBoolArg("-upgradewallet", fFirstRun))
|
||||||
{
|
{
|
||||||
int nMaxVersion = gArgs.GetArg("-upgradewallet", 0);
|
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.
|
// Register with the validation interface. It's ok to do this after rescan since we're still holding cs_main.
|
||||||
RegisterValidationInterface(walletInstance.get());
|
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)
|
bool CWallet::AutoBackupWallet(const fs::path& wallet_path, std::string& strBackupWarningRet, std::string& strBackupErrorRet)
|
||||||
{
|
{
|
||||||
strBackupWarningRet = 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) {
|
if (nWalletBackups <= 0) {
|
||||||
LogPrintf("Automatic wallet backups are disabled!\n");
|
LogPrintf("Automatic wallet backups are disabled!\n");
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
#include <wallet/crypter.h>
|
#include <wallet/crypter.h>
|
||||||
#include <wallet/coinselection.h>
|
#include <wallet/coinselection.h>
|
||||||
#include <wallet/walletdb.h>
|
#include <wallet/walletdb.h>
|
||||||
#include <wallet/rpcwallet.h>
|
#include <wallet/walletutil.h>
|
||||||
|
|
||||||
#include <coinjoin/coinjoin.h>
|
#include <coinjoin/coinjoin.h>
|
||||||
#include <governance/governance-object.h>
|
#include <governance/governance-object.h>
|
||||||
@ -789,12 +789,8 @@ private:
|
|||||||
*/
|
*/
|
||||||
bool AddWatchOnly(const CScript& dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
bool AddWatchOnly(const CScript& dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||||
|
|
||||||
/**
|
/** Wallet location which includes wallet name (see WalletLocation). */
|
||||||
* Wallet filename from wallet=<path> command line or config option.
|
WalletLocation m_location;
|
||||||
* Used in debug logs and to send RPCs to the right wallet instance when
|
|
||||||
* more than one wallet is loaded.
|
|
||||||
*/
|
|
||||||
std::string m_name;
|
|
||||||
|
|
||||||
/** Internal database handle. */
|
/** Internal database handle. */
|
||||||
std::unique_ptr<WalletDatabase> database;
|
std::unique_ptr<WalletDatabase> database;
|
||||||
@ -844,9 +840,11 @@ public:
|
|||||||
return *database;
|
return *database;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const WalletLocation& GetLocation() const { return m_location; }
|
||||||
|
|
||||||
/** Get a name for this wallet for logging/debugging purposes.
|
/** 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);
|
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||||
|
|
||||||
@ -864,7 +862,7 @@ public:
|
|||||||
unsigned int nMasterKeyMaxID = 0;
|
unsigned int nMasterKeyMaxID = 0;
|
||||||
|
|
||||||
/** Construct wallet with specified name and database implementation. */
|
/** 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)
|
//! Flush wallet (bitdb flush)
|
||||||
void Flush(bool shutdown=false);
|
void Flush(bool shutdown=false);
|
||||||
|
|
||||||
|
/** Wallet is about to be unloaded */
|
||||||
|
boost::signals2::signal<void ()> NotifyUnload;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Address book entry changed.
|
* Address book entry changed.
|
||||||
* @note called with lock cs_wallet held.
|
* @note called with lock cs_wallet held.
|
||||||
@ -1204,10 +1205,10 @@ public:
|
|||||||
bool AbandonTransaction(const uint256& hashTx);
|
bool AbandonTransaction(const uint256& hashTx);
|
||||||
|
|
||||||
//! Verify wallet naming and perform salvage on the wallet if required
|
//! 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 */
|
/* 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
|
* Wallet post-init setup
|
||||||
|
@ -25,3 +25,14 @@ fs::path GetWalletDir()
|
|||||||
|
|
||||||
return path;
|
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.
|
//! Get the path of the wallet directory.
|
||||||
fs::path GetWalletDir();
|
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
|
#endif // BITCOIN_WALLET_WALLETUTIL_H
|
||||||
|
@ -463,10 +463,8 @@ class RawTransactionsTest(BitcoinTestFramework):
|
|||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
# locked wallet test
|
# locked wallet test
|
||||||
self.stop_node(0)
|
self.nodes[1].encryptwallet("test")
|
||||||
self.nodes[1].node_encrypt_wallet("test")
|
self.stop_nodes()
|
||||||
self.stop_node(2)
|
|
||||||
self.stop_node(3)
|
|
||||||
|
|
||||||
self.start_nodes()
|
self.start_nodes()
|
||||||
# This test is not meant to test fee estimation and we'd like
|
# This test is not meant to test fee estimation and we'd like
|
||||||
|
@ -439,10 +439,8 @@ class RawTransactionsTest(BitcoinTestFramework):
|
|||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
# locked wallet test
|
# locked wallet test
|
||||||
self.stop_node(0)
|
self.nodes[1].encryptwallet("test")
|
||||||
self.stop_node(2)
|
self.stop_nodes()
|
||||||
self.stop_node(3)
|
|
||||||
self.nodes[1].node_encrypt_wallet("test")
|
|
||||||
|
|
||||||
self.start_nodes()
|
self.start_nodes()
|
||||||
# This test is not meant to test fee estimation and we'd like
|
# 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
|
assert_msg = "dashd should have exited with expected error " + expected_msg
|
||||||
self._raise_assertion_error(assert_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):
|
def add_p2p_connection(self, p2p_conn, *args, **kwargs):
|
||||||
"""Add a p2p connection to the node.
|
"""Add a p2p connection to the node.
|
||||||
|
|
||||||
|
@ -120,8 +120,7 @@ class WalletDumpTest(BitcoinTestFramework):
|
|||||||
assert_equal(found_addr_rsv, 180) # keypool size (external+internal)
|
assert_equal(found_addr_rsv, 180) # keypool size (external+internal)
|
||||||
|
|
||||||
#encrypt wallet, restart, unlock and dump
|
#encrypt wallet, restart, unlock and dump
|
||||||
self.nodes[0].node_encrypt_wallet('test')
|
self.nodes[0].encryptwallet('test')
|
||||||
self.start_node(0)
|
|
||||||
self.nodes[0].walletpassphrase('test', 30)
|
self.nodes[0].walletpassphrase('test', 30)
|
||||||
# Should be a no-op:
|
# Should be a no-op:
|
||||||
self.nodes[0].keypoolrefill()
|
self.nodes[0].keypoolrefill()
|
||||||
|
@ -30,8 +30,7 @@ class WalletEncryptionTest(BitcoinTestFramework):
|
|||||||
assert_equal(len(privkey), 52)
|
assert_equal(len(privkey), 52)
|
||||||
|
|
||||||
# Encrypt the wallet
|
# Encrypt the wallet
|
||||||
self.nodes[0].node_encrypt_wallet(passphrase)
|
self.nodes[0].encryptwallet(passphrase)
|
||||||
self.start_node(0)
|
|
||||||
|
|
||||||
# Test that the wallet is encrypted
|
# Test that the wallet is encrypted
|
||||||
assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address)
|
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
|
nodes = self.nodes
|
||||||
|
|
||||||
# Encrypt wallet and wait to terminate
|
# Encrypt wallet and wait to terminate
|
||||||
nodes[0].node_encrypt_wallet('test')
|
nodes[0].encryptwallet('test')
|
||||||
# Restart node 0
|
|
||||||
self.start_node(0)
|
|
||||||
# Keep creating keys
|
# Keep creating keys
|
||||||
addr = nodes[0].getnewaddress()
|
addr = nodes[0].getnewaddress()
|
||||||
|
|
||||||
|
@ -28,9 +28,7 @@ class KeyPoolTest(BitcoinTestFramework):
|
|||||||
assert(addr_before_encrypting_data['hdchainid'] == wallet_info_old['hdchainid'])
|
assert(addr_before_encrypting_data['hdchainid'] == wallet_info_old['hdchainid'])
|
||||||
|
|
||||||
# Encrypt wallet and wait to terminate
|
# Encrypt wallet and wait to terminate
|
||||||
nodes[0].node_encrypt_wallet('test')
|
nodes[0].encryptwallet('test')
|
||||||
# Restart node 0
|
|
||||||
self.start_node(0)
|
|
||||||
# Keep creating keys
|
# Keep creating keys
|
||||||
addr = nodes[0].getnewaddress()
|
addr = nodes[0].getnewaddress()
|
||||||
addr_data = nodes[0].getaddressinfo(addr)
|
addr_data = nodes[0].getaddressinfo(addr)
|
||||||
|
@ -199,9 +199,16 @@ class MultiWalletTest(BitcoinTestFramework):
|
|||||||
# Fail to load duplicate wallets
|
# 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])
|
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
|
# 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')
|
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
|
# 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')
|
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'))
|
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')
|
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__':
|
if __name__ == '__main__':
|
||||||
MultiWalletTest().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")
|
self.log.info("Same mnemonic, same mnemonic passphrase, encrypt wallet on upgrade, should recover all coins after rescan")
|
||||||
walletpass = "111pass222"
|
walletpass = "111pass222"
|
||||||
# Upgrading and encrypting at the saame time results in a warning
|
# 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.")
|
assert node.upgradetohd(mnemonic, "", walletpass)
|
||||||
# Wallet encryption results in node shutdown
|
node.stop()
|
||||||
node.wait_until_stopped()
|
node.wait_until_stopped()
|
||||||
self.start_node(0, extra_args=['-rescan'])
|
self.start_node(0, extra_args=['-rescan'])
|
||||||
assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", node.dumphdinfo)
|
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")
|
self.log.info("Same mnemonic, same mnemonic passphrase, encrypt wallet first, should recover all coins on upgrade after rescan")
|
||||||
walletpass = "111pass222"
|
walletpass = "111pass222"
|
||||||
node.encryptwallet(walletpass)
|
node.encryptwallet(walletpass)
|
||||||
# Wallet encryption results in node shutdown
|
node.stop()
|
||||||
node.wait_until_stopped()
|
node.wait_until_stopped()
|
||||||
self.start_node(0, extra_args=['-rescan'])
|
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, "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_raises_rpc_error(-14, "The wallet passphrase entered was incorrect", node.upgradetohd, mnemonic, "", "wrongpass")
|
||||||
assert(node.upgradetohd(mnemonic, "", walletpass))
|
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)
|
assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", node.dumphdinfo)
|
||||||
node.walletpassphrase(walletpass, 100)
|
node.walletpassphrase(walletpass, 100)
|
||||||
assert_equal(mnemonic, node.dumphdinfo()['mnemonic'])
|
assert_equal(mnemonic, node.dumphdinfo()['mnemonic'])
|
||||||
|
@ -28,7 +28,6 @@ EXPECTED_CIRCULAR_DEPENDENCIES=(
|
|||||||
"txmempool -> validation -> txmempool"
|
"txmempool -> validation -> txmempool"
|
||||||
"validation -> validationinterface -> validation"
|
"validation -> validationinterface -> validation"
|
||||||
"wallet/fees -> wallet/wallet -> wallet/fees"
|
"wallet/fees -> wallet/wallet -> wallet/fees"
|
||||||
"wallet/rpcwallet -> wallet/wallet -> wallet/rpcwallet"
|
|
||||||
"wallet/wallet -> wallet/walletdb -> wallet/wallet"
|
"wallet/wallet -> wallet/walletdb -> wallet/wallet"
|
||||||
"policy/fees -> policy/policy -> validation -> policy/fees"
|
"policy/fees -> policy/policy -> validation -> policy/fees"
|
||||||
"qt/addressbookpage -> qt/bitcoingui -> qt/walletview -> qt/addressbookpage"
|
"qt/addressbookpage -> qt/bitcoingui -> qt/walletview -> qt/addressbookpage"
|
||||||
|
Loading…
Reference in New Issue
Block a user