Merge pull request #4400 from PastaPastaPasta/backport-new-wallet-pr1

Backport 14941, 15149 and 15210
This commit is contained in:
UdjinM6 2021-09-09 23:33:29 +03:00 committed by GitHub
commit 4dbe320662
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 119 additions and 50 deletions

View File

@ -0,0 +1,5 @@
Miscellaneous RPC changes
------------
- The `unloadwallet` RPC is now synchronous, meaning that it blocks until the
wallet is fully unloaded.

View File

@ -376,6 +376,7 @@ void PrepareShutdown()
LogPrintf("%s: Unable to remove PID file: %s\n", __func__, e.what()); LogPrintf("%s: Unable to remove PID file: %s\n", __func__, e.what());
} }
#endif #endif
g_wallet_init_interface.Close();
UnregisterAllValidationInterfaces(); UnregisterAllValidationInterfaces();
GetMainSignals().UnregisterBackgroundSignalScheduler(); GetMainSignals().UnregisterBackgroundSignalScheduler();
GetMainSignals().UnregisterWithMempoolSignals(mempool); GetMainSignals().UnregisterWithMempoolSignals(mempool);
@ -397,7 +398,6 @@ void Shutdown()
PrepareShutdown(); PrepareShutdown();
} }
// Shutdown part 2: delete wallet instance // Shutdown part 2: delete wallet instance
g_wallet_init_interface.Close();
globalVerifyHandle.reset(); globalVerifyHandle.reset();
ECC_Stop(); ECC_Stop();
LogPrintf("%s: done\n", __func__); LogPrintf("%s: done\n", __func__);

View File

@ -73,7 +73,8 @@ const std::string BitcoinGUI::DEFAULT_UIPLATFORM =
BitcoinGUI::BitcoinGUI(interfaces::Node& node, const NetworkStyle* networkStyle, QWidget* parent) : BitcoinGUI::BitcoinGUI(interfaces::Node& node, const NetworkStyle* networkStyle, QWidget* parent) :
QMainWindow(parent), QMainWindow(parent),
m_node(node) m_node(node),
m_network_style(networkStyle)
{ {
GUIUtil::loadTheme(true); GUIUtil::loadTheme(true);
@ -83,22 +84,12 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const NetworkStyle* networkStyle,
move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center()); move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center());
} }
QString windowTitle = tr(PACKAGE_NAME) + " - ";
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
enableWallet = WalletModel::isWalletEnabled(); enableWallet = WalletModel::isWalletEnabled();
#endif // ENABLE_WALLET #endif // ENABLE_WALLET
if(enableWallet) QApplication::setWindowIcon(m_network_style->getTrayAndWindowIcon());
{ setWindowIcon(m_network_style->getTrayAndWindowIcon());
windowTitle += tr("Wallet"); updateWindowTitle();
} else {
windowTitle += tr("Node");
}
QString userWindowTitle = QString::fromStdString(gArgs.GetArg("-windowtitle", ""));
if(!userWindowTitle.isEmpty()) windowTitle += " - " + userWindowTitle;
windowTitle += " " + networkStyle->getTitleAddText();
QApplication::setWindowIcon(networkStyle->getTrayAndWindowIcon());
setWindowIcon(networkStyle->getTrayAndWindowIcon());
setWindowTitle(windowTitle);
rpcConsole = new RPCConsole(node, this, enableWallet ? Qt::Window : Qt::Widget); rpcConsole = new RPCConsole(node, this, enableWallet ? Qt::Window : Qt::Widget);
helpMessageDialog = new HelpMessageDialog(node, this, HelpMessageDialog::cmdline); helpMessageDialog = new HelpMessageDialog(node, this, HelpMessageDialog::cmdline);
@ -131,7 +122,7 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const NetworkStyle* networkStyle,
// Create system tray icon and notification // Create system tray icon and notification
if (QSystemTrayIcon::isSystemTrayAvailable()) { if (QSystemTrayIcon::isSystemTrayAvailable()) {
createTrayIcon(networkStyle); createTrayIcon();
} }
notificator = new Notificator(QApplication::applicationName(), trayIcon, this); notificator = new Notificator(QApplication::applicationName(), trayIcon, this);
@ -754,10 +745,9 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel)
} }
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
bool BitcoinGUI::addWallet(WalletModel *walletModel) void BitcoinGUI::addWallet(WalletModel* walletModel)
{ {
if(!walletFrame) if (!walletFrame) return;
return false;
const QString display_name = walletModel->getDisplayName(); const QString display_name = walletModel->getDisplayName();
setWalletActionsEnabled(true); setWalletActionsEnabled(true);
m_wallet_selector->addItem(display_name, QVariant::fromValue(walletModel)); m_wallet_selector->addItem(display_name, QVariant::fromValue(walletModel));
@ -765,12 +755,12 @@ bool BitcoinGUI::addWallet(WalletModel *walletModel)
m_wallet_selector_action->setVisible(true); m_wallet_selector_action->setVisible(true);
} }
rpcConsole->addWallet(walletModel); rpcConsole->addWallet(walletModel);
return walletFrame->addWallet(walletModel); walletFrame->addWallet(walletModel);
} }
bool BitcoinGUI::removeWallet(WalletModel* walletModel) void BitcoinGUI::removeWallet(WalletModel* walletModel)
{ {
if (!walletFrame) return false; if (!walletFrame) return;
int index = m_wallet_selector->findData(QVariant::fromValue(walletModel)); int index = m_wallet_selector->findData(QVariant::fromValue(walletModel));
m_wallet_selector->removeItem(index); m_wallet_selector->removeItem(index);
if (m_wallet_selector->count() == 0) { if (m_wallet_selector->count() == 0) {
@ -779,20 +769,21 @@ bool BitcoinGUI::removeWallet(WalletModel* walletModel)
m_wallet_selector_action->setVisible(false); m_wallet_selector_action->setVisible(false);
} }
rpcConsole->removeWallet(walletModel); rpcConsole->removeWallet(walletModel);
return walletFrame->removeWallet(walletModel); walletFrame->removeWallet(walletModel);
updateWindowTitle();
} }
bool BitcoinGUI::setCurrentWallet(WalletModel* wallet_model) void BitcoinGUI::setCurrentWallet(WalletModel* wallet_model)
{ {
if(!walletFrame) if (!walletFrame) return;
return false; walletFrame->setCurrentWallet(wallet_model);
return walletFrame->setCurrentWallet(wallet_model); updateWindowTitle();
} }
bool BitcoinGUI::setCurrentWalletBySelectorIndex(int index) void BitcoinGUI::setCurrentWalletBySelectorIndex(int index)
{ {
WalletModel* wallet_model = m_wallet_selector->itemData(index).value<WalletModel*>(); WalletModel* wallet_model = m_wallet_selector->itemData(index).value<WalletModel*>();
return setCurrentWallet(wallet_model); setCurrentWallet(wallet_model);
} }
void BitcoinGUI::removeAllWallets() void BitcoinGUI::removeAllWallets()
@ -836,12 +827,12 @@ void BitcoinGUI::setWalletActionsEnabled(bool enabled)
openAction->setEnabled(enabled); openAction->setEnabled(enabled);
} }
void BitcoinGUI::createTrayIcon(const NetworkStyle *networkStyle) void BitcoinGUI::createTrayIcon()
{ {
assert(QSystemTrayIcon::isSystemTrayAvailable()); assert(QSystemTrayIcon::isSystemTrayAvailable());
trayIcon = new QSystemTrayIcon(networkStyle->getTrayAndWindowIcon(), this); trayIcon = new QSystemTrayIcon(m_network_style->getTrayAndWindowIcon(), this);
QString toolTip = tr("%1 client").arg(tr(PACKAGE_NAME)) + " " + networkStyle->getTitleAddText(); QString toolTip = tr("%1 client").arg(tr(PACKAGE_NAME)) + " " + m_network_style->getTitleAddText();
trayIcon->setToolTip(toolTip); trayIcon->setToolTip(toolTip);
} }
@ -1710,6 +1701,27 @@ void BitcoinGUI::updateProxyIcon()
} }
} }
void BitcoinGUI::updateWindowTitle()
{
QString window_title = tr(PACKAGE_NAME);
#ifdef ENABLE_WALLET
if (walletFrame) {
WalletModel* const wallet_model = walletFrame->currentWalletModel();
QString userWindowTitle = QString::fromStdString(gArgs.GetArg("-windowtitle", ""));
if (!userWindowTitle.isEmpty()) {
window_title += " - " + userWindowTitle;
}
if (wallet_model && !wallet_model->getWalletName().isEmpty()) {
window_title += " - " + wallet_model->getDisplayName();
}
}
#endif
if (!m_network_style->getTitleAddText().isEmpty()) {
window_title += " - " + m_network_style->getTitleAddText();
}
setWindowTitle(window_title);
}
void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden) void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden)
{ {
if(!clientModel) if(!clientModel)

View File

@ -80,8 +80,8 @@ public:
The wallet model represents a bitcoin wallet, and offers access to the list of transactions, address book and sending The wallet model represents a bitcoin wallet, and offers access to the list of transactions, address book and sending
functionality. functionality.
*/ */
bool addWallet(WalletModel *walletModel); void addWallet(WalletModel* walletModel);
bool removeWallet(WalletModel* walletModel); void removeWallet(WalletModel* walletModel);
void removeAllWallets(); void removeAllWallets();
#endif // ENABLE_WALLET #endif // ENABLE_WALLET
bool enableWallet = false; bool enableWallet = false;
@ -197,6 +197,7 @@ private:
/** Timer to update custom css styling in -debug-ui mode periodically */ /** Timer to update custom css styling in -debug-ui mode periodically */
QTimer* timerCustomCss = nullptr; QTimer* timerCustomCss = nullptr;
const NetworkStyle* const m_network_style;
/** Create the main UI actions. */ /** Create the main UI actions. */
void createActions(); void createActions();
@ -205,7 +206,7 @@ private:
/** Create the toolbars */ /** Create the toolbars */
void createToolBars(); void createToolBars();
/** Create system tray icon and notification */ /** Create system tray icon and notification */
void createTrayIcon(const NetworkStyle *networkStyle); void createTrayIcon();
/** Create system tray menu (or setup the dock menu) */ /** Create system tray menu (or setup the dock menu) */
void createIconMenu(QMenu *pmenu); void createIconMenu(QMenu *pmenu);
@ -252,8 +253,8 @@ public Q_SLOTS:
void message(const QString &title, const QString &message, unsigned int style, bool *ret = nullptr); void message(const QString &title, const QString &message, unsigned int style, bool *ret = nullptr);
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
bool setCurrentWallet(WalletModel* wallet_model); void setCurrentWallet(WalletModel* wallet_model);
bool setCurrentWalletBySelectorIndex(int index); void 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();
@ -282,6 +283,7 @@ public Q_SLOTS:
private: private:
/** Set the proxy-enabled icon as shown in the UI. */ /** Set the proxy-enabled icon as shown in the UI. */
void updateProxyIcon(); void updateProxyIcon();
void updateWindowTitle();
public Q_SLOTS: public Q_SLOTS:
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET

View File

@ -247,11 +247,17 @@ void WalletFrame::usedReceivingAddresses()
walletView->usedReceivingAddresses(); walletView->usedReceivingAddresses();
} }
WalletView *WalletFrame::currentWalletView() WalletView* WalletFrame::currentWalletView() const
{ {
return qobject_cast<WalletView*>(walletStack->currentWidget()); return qobject_cast<WalletView*>(walletStack->currentWidget());
} }
WalletModel* WalletFrame::currentWalletModel() const
{
WalletView* wallet_view = currentWalletView();
return wallet_view ? wallet_view->getWalletModel() : nullptr;
}
void WalletFrame::outOfSyncWarningClicked() void WalletFrame::outOfSyncWarningClicked()
{ {
Q_EMIT requestedSyncWarningInfo(); Q_EMIT requestedSyncWarningInfo();

View File

@ -59,7 +59,8 @@ private:
bool bOutOfSync; bool bOutOfSync;
public: public:
WalletView *currentWalletView(); WalletView* currentWalletView() const;
WalletModel* currentWalletModel() const;
public Q_SLOTS: public Q_SLOTS:
/** Switch to overview (home) page */ /** Switch to overview (home) page */

View File

@ -402,8 +402,12 @@ void WalletInit::Stop() const
void WalletInit::Close() const void WalletInit::Close() const
{ {
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) { auto wallets = GetWallets();
RemoveWallet(pwallet); while (!wallets.empty()) {
auto wallet = wallets.back();
wallets.pop_back();
RemoveWallet(wallet);
UnloadWallet(std::move(wallet));
} }
} }

View File

@ -3487,16 +3487,8 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
if (!RemoveWallet(wallet)) { if (!RemoveWallet(wallet)) {
throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded"); 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. UnloadWallet(std::move(wallet));
// 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; return NullUniValue;
} }

View File

@ -98,14 +98,52 @@ std::shared_ptr<CWallet> GetWallet(const std::string& name)
return nullptr; return nullptr;
} }
static Mutex g_wallet_release_mutex;
static std::condition_variable g_wallet_release_cv;
static std::set<CWallet*> g_unloading_wallet_set;
// Custom deleter for shared_ptr<CWallet>. // Custom deleter for shared_ptr<CWallet>.
static void ReleaseWallet(CWallet* wallet) static void ReleaseWallet(CWallet* wallet)
{ {
// Unregister and delete the wallet right after BlockUntilSyncedToCurrentChain
// so that it's in sync with the current chainstate.
wallet->WalletLogPrintf("Releasing wallet\n"); wallet->WalletLogPrintf("Releasing wallet\n");
wallet->BlockUntilSyncedToCurrentChain(); wallet->BlockUntilSyncedToCurrentChain();
wallet->Flush(); wallet->Flush();
UnregisterValidationInterface(wallet);
delete wallet; delete wallet;
// Wallet is now released, notify UnloadWallet, if any.
{
LOCK(g_wallet_release_mutex);
if (g_unloading_wallet_set.erase(wallet) == 0) {
// UnloadWallet was not called for this wallet, all done.
return;
}
}
g_wallet_release_cv.notify_all();
}
void UnloadWallet(std::shared_ptr<CWallet>&& wallet)
{
// Mark wallet for unloading.
CWallet* pwallet = wallet.get();
{
LOCK(g_wallet_release_mutex);
auto it = g_unloading_wallet_set.insert(pwallet);
assert(it.second);
}
// The wallet can be in use so it's not possible to explicitly unload here.
// Notify the unload intent so that all remaining shared pointers are
// released.
pwallet->NotifyUnload();
// Time to ditch our shared_ptr and wait for ReleaseWallet call.
wallet.reset();
{
WAIT_LOCK(g_wallet_release_mutex, lock);
while (g_unloading_wallet_set.count(pwallet) == 1) {
g_wallet_release_cv.wait(lock);
}
}
} }
const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));

View File

@ -37,6 +37,13 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
//! Explicitly unload and delete the wallet.
// Blocks the current thread after signaling the unload intent so that all
// wallet clients release the wallet.
// Note that, when blocking is not required, the wallet is implicitly unloaded
// by the shared pointer deleter.
void UnloadWallet(std::shared_ptr<CWallet>&& wallet);
bool AddWallet(const std::shared_ptr<CWallet>& wallet); bool AddWallet(const std::shared_ptr<CWallet>& wallet);
bool RemoveWallet(const std::shared_ptr<CWallet>& wallet); bool RemoveWallet(const std::shared_ptr<CWallet>& wallet);
bool HasWallets(); bool HasWallets();
@ -891,6 +898,8 @@ public:
~CWallet() ~CWallet()
{ {
// Should not have slots connected at this point.
assert(NotifyUnload.empty());
delete encrypted_batch; delete encrypted_batch;
encrypted_batch = nullptr; encrypted_batch = nullptr;
} }