aboutsummaryrefslogtreecommitdiff
path: root/src/qt
diff options
context:
space:
mode:
Diffstat (limited to 'src/qt')
-rw-r--r--src/qt/bitcoin.cpp118
-rw-r--r--src/qt/bitcoin.h20
-rw-r--r--src/qt/bitcoin.qrc74
-rw-r--r--src/qt/bitcoinamountfield.cpp8
-rw-r--r--src/qt/bitcoingui.cpp47
-rw-r--r--src/qt/bitcoingui.h9
-rw-r--r--src/qt/bitcoinunits.cpp2
-rw-r--r--src/qt/bitcoinunits.h14
-rw-r--r--src/qt/clientmodel.cpp18
-rw-r--r--src/qt/coincontroldialog.cpp4
-rw-r--r--src/qt/forms/debugwindow.ui75
-rw-r--r--src/qt/forms/optionsdialog.ui4
-rw-r--r--src/qt/forms/psbtoperationsdialog.ui148
-rw-r--r--src/qt/forms/receivecoinsdialog.ui6
-rw-r--r--src/qt/guiutil.cpp31
-rw-r--r--src/qt/guiutil.h15
-rw-r--r--src/qt/intro.cpp9
-rw-r--r--src/qt/intro.h2
-rw-r--r--src/qt/modaloverlay.cpp2
-rw-r--r--src/qt/modaloverlay.h10
-rw-r--r--src/qt/optionsmodel.cpp26
-rw-r--r--src/qt/optionsmodel.h12
-rw-r--r--src/qt/overviewpage.cpp34
-rw-r--r--src/qt/paymentserver.cpp10
-rw-r--r--src/qt/paymentserver.h2
-rw-r--r--src/qt/psbtoperationsdialog.cpp268
-rw-r--r--src/qt/psbtoperationsdialog.h54
-rw-r--r--src/qt/receivecoinsdialog.cpp16
-rw-r--r--src/qt/receivecoinsdialog.h3
-rw-r--r--src/qt/recentrequeststablemodel.cpp2
-rwxr-xr-xsrc/qt/res/animation/makespinner.sh (renamed from src/qt/res/movies/makespinner.sh)0
-rw-r--r--src/qt/res/animation/spinner-000.png (renamed from src/qt/res/movies/spinner-000.png)bin1794 -> 1794 bytes
-rw-r--r--src/qt/res/animation/spinner-001.png (renamed from src/qt/res/movies/spinner-001.png)bin2376 -> 2376 bytes
-rw-r--r--src/qt/res/animation/spinner-002.png (renamed from src/qt/res/movies/spinner-002.png)bin2376 -> 2376 bytes
-rw-r--r--src/qt/res/animation/spinner-003.png (renamed from src/qt/res/movies/spinner-003.png)bin2355 -> 2355 bytes
-rw-r--r--src/qt/res/animation/spinner-004.png (renamed from src/qt/res/movies/spinner-004.png)bin2349 -> 2349 bytes
-rw-r--r--src/qt/res/animation/spinner-005.png (renamed from src/qt/res/movies/spinner-005.png)bin2305 -> 2305 bytes
-rw-r--r--src/qt/res/animation/spinner-006.png (renamed from src/qt/res/movies/spinner-006.png)bin2304 -> 2304 bytes
-rw-r--r--src/qt/res/animation/spinner-007.png (renamed from src/qt/res/movies/spinner-007.png)bin2283 -> 2283 bytes
-rw-r--r--src/qt/res/animation/spinner-008.png (renamed from src/qt/res/movies/spinner-008.png)bin2312 -> 2312 bytes
-rw-r--r--src/qt/res/animation/spinner-009.png (renamed from src/qt/res/movies/spinner-009.png)bin1810 -> 1810 bytes
-rw-r--r--src/qt/res/animation/spinner-010.png (renamed from src/qt/res/movies/spinner-010.png)bin2305 -> 2305 bytes
-rw-r--r--src/qt/res/animation/spinner-011.png (renamed from src/qt/res/movies/spinner-011.png)bin2338 -> 2338 bytes
-rw-r--r--src/qt/res/animation/spinner-012.png (renamed from src/qt/res/movies/spinner-012.png)bin2352 -> 2352 bytes
-rw-r--r--src/qt/res/animation/spinner-013.png (renamed from src/qt/res/movies/spinner-013.png)bin2377 -> 2377 bytes
-rw-r--r--src/qt/res/animation/spinner-014.png (renamed from src/qt/res/movies/spinner-014.png)bin2358 -> 2358 bytes
-rw-r--r--src/qt/res/animation/spinner-015.png (renamed from src/qt/res/movies/spinner-015.png)bin2405 -> 2405 bytes
-rw-r--r--src/qt/res/animation/spinner-016.png (renamed from src/qt/res/movies/spinner-016.png)bin2429 -> 2429 bytes
-rw-r--r--src/qt/res/animation/spinner-017.png (renamed from src/qt/res/movies/spinner-017.png)bin2408 -> 2408 bytes
-rw-r--r--src/qt/res/animation/spinner-018.png (renamed from src/qt/res/movies/spinner-018.png)bin1831 -> 1831 bytes
-rw-r--r--src/qt/res/animation/spinner-019.png (renamed from src/qt/res/movies/spinner-019.png)bin2380 -> 2380 bytes
-rw-r--r--src/qt/res/animation/spinner-020.png (renamed from src/qt/res/movies/spinner-020.png)bin2366 -> 2366 bytes
-rw-r--r--src/qt/res/animation/spinner-021.png (renamed from src/qt/res/movies/spinner-021.png)bin2368 -> 2368 bytes
-rw-r--r--src/qt/res/animation/spinner-022.png (renamed from src/qt/res/movies/spinner-022.png)bin2356 -> 2356 bytes
-rw-r--r--src/qt/res/animation/spinner-023.png (renamed from src/qt/res/movies/spinner-023.png)bin2311 -> 2311 bytes
-rw-r--r--src/qt/res/animation/spinner-024.png (renamed from src/qt/res/movies/spinner-024.png)bin2315 -> 2315 bytes
-rw-r--r--src/qt/res/animation/spinner-025.png (renamed from src/qt/res/movies/spinner-025.png)bin2298 -> 2298 bytes
-rw-r--r--src/qt/res/animation/spinner-026.png (renamed from src/qt/res/movies/spinner-026.png)bin2291 -> 2291 bytes
-rw-r--r--src/qt/res/animation/spinner-027.png (renamed from src/qt/res/movies/spinner-027.png)bin1816 -> 1816 bytes
-rw-r--r--src/qt/res/animation/spinner-028.png (renamed from src/qt/res/movies/spinner-028.png)bin2308 -> 2308 bytes
-rw-r--r--src/qt/res/animation/spinner-029.png (renamed from src/qt/res/movies/spinner-029.png)bin2356 -> 2356 bytes
-rw-r--r--src/qt/res/animation/spinner-030.png (renamed from src/qt/res/movies/spinner-030.png)bin2346 -> 2346 bytes
-rw-r--r--src/qt/res/animation/spinner-031.png (renamed from src/qt/res/movies/spinner-031.png)bin2380 -> 2380 bytes
-rw-r--r--src/qt/res/animation/spinner-032.png (renamed from src/qt/res/movies/spinner-032.png)bin2345 -> 2345 bytes
-rw-r--r--src/qt/res/animation/spinner-033.png (renamed from src/qt/res/movies/spinner-033.png)bin2401 -> 2401 bytes
-rw-r--r--src/qt/res/animation/spinner-034.png (renamed from src/qt/res/movies/spinner-034.png)bin2422 -> 2422 bytes
-rw-r--r--src/qt/res/animation/spinner-035.png (renamed from src/qt/res/movies/spinner-035.png)bin2406 -> 2406 bytes
-rw-r--r--src/qt/rpcconsole.cpp22
-rw-r--r--src/qt/rpcconsole.h2
-rw-r--r--src/qt/sendcoinsdialog.cpp4
-rw-r--r--src/qt/splashscreen.cpp32
-rw-r--r--src/qt/splashscreen.h8
-rw-r--r--src/qt/test/addressbooktests.cpp6
-rw-r--r--src/qt/test/apptests.cpp11
-rw-r--r--src/qt/test/test_main.cpp15
-rw-r--r--src/qt/test/wallettests.cpp11
-rw-r--r--src/qt/transactionrecord.cpp2
-rw-r--r--src/qt/transactiontablemodel.cpp21
-rw-r--r--src/qt/transactiontablemodel.h2
-rw-r--r--src/qt/transactionview.cpp2
-rw-r--r--src/qt/utilitydialog.cpp2
-rw-r--r--src/qt/utilitydialog.h6
-rw-r--r--src/qt/walletcontroller.cpp21
-rw-r--r--src/qt/walletcontroller.h1
-rw-r--r--src/qt/walletframe.cpp4
-rw-r--r--src/qt/walletframe.h2
-rw-r--r--src/qt/walletmodel.cpp11
-rw-r--r--src/qt/walletmodel.h3
-rw-r--r--src/qt/walletview.cpp99
-rw-r--r--src/qt/walletview.h2
90 files changed, 934 insertions, 398 deletions
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 6ae34aac72..c290ad1316 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -27,15 +27,19 @@
#include <qt/walletmodel.h>
#endif // ENABLE_WALLET
+#include <init.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
+#include <node/context.h>
+#include <node/ui_interface.h>
#include <noui.h>
-#include <ui_interface.h>
#include <uint256.h>
#include <util/system.h>
#include <util/threadnames.h>
+#include <util/translation.h>
#include <validation.h>
+#include <boost/signals2/connection.hpp>
#include <memory>
#include <QApplication>
@@ -80,6 +84,7 @@ static void RegisterMetaTypes()
qRegisterMetaType<std::function<void()>>("std::function<void()>");
qRegisterMetaType<QMessageBox::Icon>("QMessageBox::Icon");
+ qRegisterMetaType<interfaces::BlockAndHeaderTipInfo>("interfaces::BlockAndHeaderTipInfo");
}
static QString GetLangTerritory()
@@ -154,7 +159,7 @@ BitcoinCore::BitcoinCore(interfaces::Node& node) :
void BitcoinCore::handleRunawayException(const std::exception *e)
{
PrintExceptionContinue(e, "Runaway exception");
- Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings()));
+ Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings().translated));
}
void BitcoinCore::initialize()
@@ -163,8 +168,9 @@ void BitcoinCore::initialize()
{
qDebug() << __func__ << ": Running initialization in thread";
util::ThreadRename("qt-init");
- bool rv = m_node.appInitMain();
- Q_EMIT initializeResult(rv);
+ interfaces::BlockAndHeaderTipInfo tip_info;
+ bool rv = m_node.appInitMain(&tip_info);
+ Q_EMIT initializeResult(rv, tip_info);
} catch (const std::exception& e) {
handleRunawayException(&e);
} catch (...) {
@@ -190,10 +196,9 @@ void BitcoinCore::shutdown()
static int qt_argc = 1;
static const char* qt_argv = "bitcoin-qt";
-BitcoinApplication::BitcoinApplication(interfaces::Node& node):
+BitcoinApplication::BitcoinApplication():
QApplication(qt_argc, const_cast<char **>(&qt_argv)),
coreThread(nullptr),
- m_node(node),
optionsModel(nullptr),
clientModel(nullptr),
window(nullptr),
@@ -244,12 +249,12 @@ void BitcoinApplication::createPaymentServer()
void BitcoinApplication::createOptionsModel(bool resetSettings)
{
- optionsModel = new OptionsModel(m_node, this, resetSettings);
+ optionsModel = new OptionsModel(this, resetSettings);
}
void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
{
- window = new BitcoinGUI(m_node, platformStyle, networkStyle, nullptr);
+ window = new BitcoinGUI(node(), platformStyle, networkStyle, nullptr);
pollShutdownTimer = new QTimer(window);
connect(pollShutdownTimer, &QTimer::timeout, window, &BitcoinGUI::detectShutdown);
@@ -257,17 +262,26 @@ void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle)
{
- SplashScreen *splash = new SplashScreen(m_node, nullptr, networkStyle);
+ assert(!m_splash);
+ m_splash = new SplashScreen(nullptr, networkStyle);
// We don't hold a direct pointer to the splash screen after creation, but the splash
// screen will take care of deleting itself when finish() happens.
- splash->show();
- connect(this, &BitcoinApplication::splashFinished, splash, &SplashScreen::finish);
- connect(this, &BitcoinApplication::requestedShutdown, splash, &QWidget::close);
+ m_splash->show();
+ connect(this, &BitcoinApplication::splashFinished, m_splash, &SplashScreen::finish);
+ connect(this, &BitcoinApplication::requestedShutdown, m_splash, &QWidget::close);
+}
+
+void BitcoinApplication::setNode(interfaces::Node& node)
+{
+ assert(!m_node);
+ m_node = &node;
+ if (optionsModel) optionsModel->setNode(*m_node);
+ if (m_splash) m_splash->setNode(*m_node);
}
bool BitcoinApplication::baseInitialize()
{
- return m_node.baseInitialize();
+ return node().baseInitialize();
}
void BitcoinApplication::startThread()
@@ -275,7 +289,7 @@ void BitcoinApplication::startThread()
if(coreThread)
return;
coreThread = new QThread(this);
- BitcoinCore *executor = new BitcoinCore(m_node);
+ BitcoinCore *executor = new BitcoinCore(node());
executor->moveToThread(coreThread);
/* communication to and from thread */
@@ -296,8 +310,8 @@ void BitcoinApplication::parameterSetup()
// print to the console unnecessarily.
gArgs.SoftSetBoolArg("-printtoconsole", false);
- m_node.initLogging();
- m_node.initParameterInteraction();
+ InitLogging(gArgs);
+ InitParameterInteraction(gArgs);
}
void BitcoinApplication::InitializePruneSetting(bool prune)
@@ -329,7 +343,7 @@ void BitcoinApplication::requestShutdown()
window->unsubscribeFromCoreSignals();
// Request node shutdown, which can interrupt long operations, like
// rescanning a wallet.
- m_node.startShutdown();
+ node().startShutdown();
// Unsetting the client model can cause the current thread to wait for node
// to complete an operation, like wait for a RPC execution to complete.
window->setClientModel(nullptr);
@@ -342,7 +356,7 @@ void BitcoinApplication::requestShutdown()
Q_EMIT requestedShutdown();
}
-void BitcoinApplication::initializeResult(bool success)
+void BitcoinApplication::initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info)
{
qDebug() << __func__ << ": Initialization result: " << success;
// Set exit result.
@@ -351,8 +365,8 @@ void BitcoinApplication::initializeResult(bool success)
{
// Log this only after AppInitMain finishes, as then logging setup is guaranteed complete
qInfo() << "Platform customization:" << platformStyle->getName();
- clientModel = new ClientModel(m_node, optionsModel);
- window->setClientModel(clientModel);
+ clientModel = new ClientModel(node(), optionsModel);
+ window->setClientModel(clientModel, &tip_info);
#ifdef ENABLE_WALLET
if (WalletModel::isWalletEnabled()) {
m_wallet_controller = new WalletController(*clientModel, platformStyle, this);
@@ -400,7 +414,7 @@ void BitcoinApplication::shutdownResult()
void BitcoinApplication::handleRunawayException(const QString &message)
{
- QMessageBox::critical(nullptr, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. %1 can no longer continue safely and will quit.").arg(PACKAGE_NAME) + QString("\n\n") + message);
+ QMessageBox::critical(nullptr, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. %1 can no longer continue safely and will quit.").arg(PACKAGE_NAME) + QString("<br><br>") + message);
::exit(EXIT_FAILURE);
}
@@ -412,14 +426,14 @@ WId BitcoinApplication::getMainWinId() const
return window->winId();
}
-static void SetupUIArgs()
+static void SetupUIArgs(ArgsManager& argsman)
{
- gArgs.AddArg("-choosedatadir", strprintf("Choose data directory on startup (default: %u)", DEFAULT_CHOOSE_DATADIR), ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
- gArgs.AddArg("-lang=<lang>", "Set language, for example \"de_DE\" (default: system locale)", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
- gArgs.AddArg("-min", "Start minimized", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
- gArgs.AddArg("-resetguisettings", "Reset all settings changed in the GUI", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
- gArgs.AddArg("-splash", strprintf("Show splash screen on startup (default: %u)", DEFAULT_SPLASHSCREEN), ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
- gArgs.AddArg("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::GUI);
+ argsman.AddArg("-choosedatadir", strprintf("Choose data directory on startup (default: %u)", DEFAULT_CHOOSE_DATADIR), ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
+ argsman.AddArg("-lang=<lang>", "Set language, for example \"de_DE\" (default: system locale)", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
+ argsman.AddArg("-min", "Start minimized", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
+ argsman.AddArg("-resetguisettings", "Reset all settings changed in the GUI", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
+ argsman.AddArg("-splash", strprintf("Show splash screen on startup (default: %u)", DEFAULT_SPLASHSCREEN), ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
+ argsman.AddArg("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::GUI);
}
int GuiMain(int argc, char* argv[])
@@ -431,12 +445,13 @@ int GuiMain(int argc, char* argv[])
SetupEnvironment();
util::ThreadSetInternalName("main");
- std::unique_ptr<interfaces::Node> node = interfaces::MakeNode();
+ NodeContext node_context;
+ std::unique_ptr<interfaces::Node> node = interfaces::MakeNode(&node_context);
// Subscribe to global signals from core
- std::unique_ptr<interfaces::Handler> handler_message_box = node->handleMessageBox(noui_ThreadSafeMessageBox);
- std::unique_ptr<interfaces::Handler> handler_question = node->handleQuestion(noui_ThreadSafeQuestion);
- std::unique_ptr<interfaces::Handler> handler_init_message = node->handleInitMessage(noui_InitMessage);
+ boost::signals2::scoped_connection handler_message_box = ::uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBox);
+ boost::signals2::scoped_connection handler_question = ::uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestion);
+ boost::signals2::scoped_connection handler_init_message = ::uiInterface.InitMessage_connect(noui_InitMessage);
// Do not refer to data directory yet, this can be overridden by Intro::pickDataDirectory
@@ -450,15 +465,15 @@ int GuiMain(int argc, char* argv[])
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
- BitcoinApplication app(*node);
+ BitcoinApplication app;
/// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these
// Command-line options take precedence:
- node->setupServerArgs();
- SetupUIArgs();
+ SetupServerArgs(node_context);
+ SetupUIArgs(gArgs);
std::string error;
- if (!node->parseParameters(argc, argv, error)) {
- node->initError(strprintf("Error parsing command line arguments: %s\n", error));
+ if (!gArgs.ParseParameters(argc, argv, error)) {
+ InitError(strprintf(Untranslated("Error parsing command line arguments: %s\n"), error));
// Create a message box, because the gui has neither been created nor has subscribed to core signals
QMessageBox::critical(nullptr, PACKAGE_NAME,
// message can not be translated because translations have not been initialized
@@ -484,7 +499,7 @@ int GuiMain(int argc, char* argv[])
// Show help message immediately after parsing command-line options (for "-lang") and setting locale,
// but before showing splash screen.
if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
- HelpMessageDialog help(*node, nullptr, gArgs.IsArgSet("-version"));
+ HelpMessageDialog help(nullptr, gArgs.IsArgSet("-version"));
help.showOrPrint();
return EXIT_SUCCESS;
}
@@ -494,18 +509,18 @@ int GuiMain(int argc, char* argv[])
bool did_show_intro = false;
bool prune = false; // Intro dialog prune check box
// Gracefully exit if the user cancels
- if (!Intro::showIfNeeded(*node, did_show_intro, prune)) return EXIT_SUCCESS;
+ if (!Intro::showIfNeeded(did_show_intro, prune)) return EXIT_SUCCESS;
/// 6. Determine availability of data directory and parse bitcoin.conf
/// - Do not call GetDataDir(true) before this step finishes
if (!CheckDataDirOption()) {
- node->initError(strprintf("Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "")));
+ InitError(strprintf(Untranslated("Specified data directory \"%s\" does not exist.\n"), gArgs.GetArg("-datadir", "")));
QMessageBox::critical(nullptr, PACKAGE_NAME,
QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(gArgs.GetArg("-datadir", ""))));
return EXIT_FAILURE;
}
- if (!node->readConfigFiles(error)) {
- node->initError(strprintf("Error reading configuration file: %s\n", error));
+ if (!gArgs.ReadConfigFiles(error, true)) {
+ InitError(strprintf(Untranslated("Error reading configuration file: %s\n"), error));
QMessageBox::critical(nullptr, PACKAGE_NAME,
QObject::tr("Error: Cannot parse configuration file: %1.").arg(QString::fromStdString(error)));
return EXIT_FAILURE;
@@ -519,16 +534,21 @@ int GuiMain(int argc, char* argv[])
// Check for -chain, -testnet or -regtest parameter (Params() calls are only valid after this clause)
try {
- node->selectParams(gArgs.GetChainName());
+ SelectParams(gArgs.GetChainName());
} catch(std::exception &e) {
- node->initError(strprintf("%s\n", e.what()));
+ InitError(Untranslated(strprintf("%s\n", e.what())));
QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error: %1").arg(e.what()));
return EXIT_FAILURE;
}
#ifdef ENABLE_WALLET
// Parse URIs on command line -- this can affect Params()
- PaymentServer::ipcParseCommandLine(*node, argc, argv);
+ PaymentServer::ipcParseCommandLine(argc, argv);
#endif
+ if (!gArgs.InitSettings(error)) {
+ InitError(Untranslated(error));
+ QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error initializing settings: %1").arg(QString::fromStdString(error)));
+ return EXIT_FAILURE;
+ }
QScopedPointer<const NetworkStyle> networkStyle(NetworkStyle::instantiate(Params().NetworkIDString()));
assert(!networkStyle.isNull());
@@ -557,6 +577,8 @@ int GuiMain(int argc, char* argv[])
/// 9. Main GUI initialization
// Install global event filter that makes sure that long tooltips can be word-wrapped
app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app));
+ // Install global event filter that makes sure that out-of-focus labels do not contain text cursor.
+ app.installEventFilter(new GUIUtil::LabelOutOfFocusEventFilter(&app));
#if defined(Q_OS_WIN)
// Install global event filter for processing Windows session related Windows messages (WM_QUERYENDSESSION and WM_ENDSESSION)
qApp->installNativeEventFilter(new WinShutdownMonitor());
@@ -577,6 +599,8 @@ int GuiMain(int argc, char* argv[])
if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false))
app.createSplashScreen(networkStyle.data());
+ app.setNode(*node);
+
int rv = EXIT_SUCCESS;
try
{
@@ -599,10 +623,10 @@ int GuiMain(int argc, char* argv[])
}
} catch (const std::exception& e) {
PrintExceptionContinue(&e, "Runaway exception");
- app.handleRunawayException(QString::fromStdString(node->getWarnings()));
+ app.handleRunawayException(QString::fromStdString(app.node().getWarnings().translated));
} catch (...) {
PrintExceptionContinue(nullptr, "Runaway exception");
- app.handleRunawayException(QString::fromStdString(node->getWarnings()));
+ app.handleRunawayException(QString::fromStdString(app.node().getWarnings().translated));
}
return rv;
}
diff --git a/src/qt/bitcoin.h b/src/qt/bitcoin.h
index 077a37fde5..69e0a5921e 100644
--- a/src/qt/bitcoin.h
+++ b/src/qt/bitcoin.h
@@ -10,21 +10,21 @@
#endif
#include <QApplication>
+#include <assert.h>
#include <memory>
+#include <interfaces/node.h>
+
class BitcoinGUI;
class ClientModel;
class NetworkStyle;
class OptionsModel;
class PaymentServer;
class PlatformStyle;
+class SplashScreen;
class WalletController;
class WalletModel;
-namespace interfaces {
-class Handler;
-class Node;
-} // namespace interfaces
/** Class encapsulating Bitcoin Core startup and shutdown.
* Allows running startup and shutdown in a different thread from the UI thread.
@@ -40,7 +40,7 @@ public Q_SLOTS:
void shutdown();
Q_SIGNALS:
- void initializeResult(bool success);
+ void initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info);
void shutdownResult();
void runawayException(const QString &message);
@@ -56,7 +56,7 @@ class BitcoinApplication: public QApplication
{
Q_OBJECT
public:
- explicit BitcoinApplication(interfaces::Node& node);
+ explicit BitcoinApplication();
~BitcoinApplication();
#ifdef ENABLE_WALLET
@@ -90,8 +90,11 @@ public:
/// Setup platform style
void setupPlatformStyle();
+ interfaces::Node& node() const { assert(m_node); return *m_node; }
+ void setNode(interfaces::Node& node);
+
public Q_SLOTS:
- void initializeResult(bool success);
+ void initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info);
void shutdownResult();
/// Handle runaway exceptions. Shows a message box with the problem and quits the program.
void handleRunawayException(const QString &message);
@@ -104,7 +107,6 @@ Q_SIGNALS:
private:
QThread *coreThread;
- interfaces::Node& m_node;
OptionsModel *optionsModel;
ClientModel *clientModel;
BitcoinGUI *window;
@@ -116,6 +118,8 @@ private:
int returnValue;
const PlatformStyle *platformStyle;
std::unique_ptr<QWidget> shutdownWindow;
+ SplashScreen* m_splash = nullptr;
+ interfaces::Node* m_node = nullptr;
void startThread();
};
diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc
index 037b23e4b2..7115459808 100644
--- a/src/qt/bitcoin.qrc
+++ b/src/qt/bitcoin.qrc
@@ -45,42 +45,42 @@
<file alias="network_disabled">res/icons/network_disabled.png</file>
<file alias="proxy">res/icons/proxy.png</file>
</qresource>
- <qresource prefix="/movies">
- <file alias="spinner-000">res/movies/spinner-000.png</file>
- <file alias="spinner-001">res/movies/spinner-001.png</file>
- <file alias="spinner-002">res/movies/spinner-002.png</file>
- <file alias="spinner-003">res/movies/spinner-003.png</file>
- <file alias="spinner-004">res/movies/spinner-004.png</file>
- <file alias="spinner-005">res/movies/spinner-005.png</file>
- <file alias="spinner-006">res/movies/spinner-006.png</file>
- <file alias="spinner-007">res/movies/spinner-007.png</file>
- <file alias="spinner-008">res/movies/spinner-008.png</file>
- <file alias="spinner-009">res/movies/spinner-009.png</file>
- <file alias="spinner-010">res/movies/spinner-010.png</file>
- <file alias="spinner-011">res/movies/spinner-011.png</file>
- <file alias="spinner-012">res/movies/spinner-012.png</file>
- <file alias="spinner-013">res/movies/spinner-013.png</file>
- <file alias="spinner-014">res/movies/spinner-014.png</file>
- <file alias="spinner-015">res/movies/spinner-015.png</file>
- <file alias="spinner-016">res/movies/spinner-016.png</file>
- <file alias="spinner-017">res/movies/spinner-017.png</file>
- <file alias="spinner-018">res/movies/spinner-018.png</file>
- <file alias="spinner-019">res/movies/spinner-019.png</file>
- <file alias="spinner-020">res/movies/spinner-020.png</file>
- <file alias="spinner-021">res/movies/spinner-021.png</file>
- <file alias="spinner-022">res/movies/spinner-022.png</file>
- <file alias="spinner-023">res/movies/spinner-023.png</file>
- <file alias="spinner-024">res/movies/spinner-024.png</file>
- <file alias="spinner-025">res/movies/spinner-025.png</file>
- <file alias="spinner-026">res/movies/spinner-026.png</file>
- <file alias="spinner-027">res/movies/spinner-027.png</file>
- <file alias="spinner-028">res/movies/spinner-028.png</file>
- <file alias="spinner-029">res/movies/spinner-029.png</file>
- <file alias="spinner-030">res/movies/spinner-030.png</file>
- <file alias="spinner-031">res/movies/spinner-031.png</file>
- <file alias="spinner-032">res/movies/spinner-032.png</file>
- <file alias="spinner-033">res/movies/spinner-033.png</file>
- <file alias="spinner-034">res/movies/spinner-034.png</file>
- <file alias="spinner-035">res/movies/spinner-035.png</file>
+ <qresource prefix="/animation">
+ <file alias="spinner-000">res/animation/spinner-000.png</file>
+ <file alias="spinner-001">res/animation/spinner-001.png</file>
+ <file alias="spinner-002">res/animation/spinner-002.png</file>
+ <file alias="spinner-003">res/animation/spinner-003.png</file>
+ <file alias="spinner-004">res/animation/spinner-004.png</file>
+ <file alias="spinner-005">res/animation/spinner-005.png</file>
+ <file alias="spinner-006">res/animation/spinner-006.png</file>
+ <file alias="spinner-007">res/animation/spinner-007.png</file>
+ <file alias="spinner-008">res/animation/spinner-008.png</file>
+ <file alias="spinner-009">res/animation/spinner-009.png</file>
+ <file alias="spinner-010">res/animation/spinner-010.png</file>
+ <file alias="spinner-011">res/animation/spinner-011.png</file>
+ <file alias="spinner-012">res/animation/spinner-012.png</file>
+ <file alias="spinner-013">res/animation/spinner-013.png</file>
+ <file alias="spinner-014">res/animation/spinner-014.png</file>
+ <file alias="spinner-015">res/animation/spinner-015.png</file>
+ <file alias="spinner-016">res/animation/spinner-016.png</file>
+ <file alias="spinner-017">res/animation/spinner-017.png</file>
+ <file alias="spinner-018">res/animation/spinner-018.png</file>
+ <file alias="spinner-019">res/animation/spinner-019.png</file>
+ <file alias="spinner-020">res/animation/spinner-020.png</file>
+ <file alias="spinner-021">res/animation/spinner-021.png</file>
+ <file alias="spinner-022">res/animation/spinner-022.png</file>
+ <file alias="spinner-023">res/animation/spinner-023.png</file>
+ <file alias="spinner-024">res/animation/spinner-024.png</file>
+ <file alias="spinner-025">res/animation/spinner-025.png</file>
+ <file alias="spinner-026">res/animation/spinner-026.png</file>
+ <file alias="spinner-027">res/animation/spinner-027.png</file>
+ <file alias="spinner-028">res/animation/spinner-028.png</file>
+ <file alias="spinner-029">res/animation/spinner-029.png</file>
+ <file alias="spinner-030">res/animation/spinner-030.png</file>
+ <file alias="spinner-031">res/animation/spinner-031.png</file>
+ <file alias="spinner-032">res/animation/spinner-032.png</file>
+ <file alias="spinner-033">res/animation/spinner-033.png</file>
+ <file alias="spinner-034">res/animation/spinner-034.png</file>
+ <file alias="spinner-035">res/animation/spinner-035.png</file>
</qresource>
</RCC>
diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp
index 4c57f1e352..a953c991bc 100644
--- a/src/qt/bitcoinamountfield.cpp
+++ b/src/qt/bitcoinamountfield.cpp
@@ -56,7 +56,7 @@ public:
if (valid) {
val = qBound(m_min_amount, val, m_max_amount);
- input = BitcoinUnits::format(currentUnit, val, false, BitcoinUnits::separatorAlways);
+ input = BitcoinUnits::format(currentUnit, val, false, BitcoinUnits::SeparatorStyle::ALWAYS);
lineEdit()->setText(input);
}
}
@@ -68,7 +68,7 @@ public:
void setValue(const CAmount& value)
{
- lineEdit()->setText(BitcoinUnits::format(currentUnit, value, false, BitcoinUnits::separatorAlways));
+ lineEdit()->setText(BitcoinUnits::format(currentUnit, value, false, BitcoinUnits::SeparatorStyle::ALWAYS));
Q_EMIT valueChanged();
}
@@ -102,7 +102,7 @@ public:
CAmount val = value(&valid);
currentUnit = unit;
- lineEdit()->setPlaceholderText(BitcoinUnits::format(currentUnit, m_min_amount, false, BitcoinUnits::separatorAlways));
+ lineEdit()->setPlaceholderText(BitcoinUnits::format(currentUnit, m_min_amount, false, BitcoinUnits::SeparatorStyle::ALWAYS));
if(valid)
setValue(val);
else
@@ -122,7 +122,7 @@ public:
const QFontMetrics fm(fontMetrics());
int h = lineEdit()->minimumSizeHint().height();
- int w = GUIUtil::TextWidth(fm, BitcoinUnits::format(BitcoinUnits::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::separatorAlways));
+ int w = GUIUtil::TextWidth(fm, BitcoinUnits::format(BitcoinUnits::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::SeparatorStyle::ALWAYS));
w += 2; // cursor blinking space
QStyleOptionSpinBox opt;
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 6192013e5f..8935ff19bf 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -34,7 +34,7 @@
#include <chainparams.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
-#include <ui_interface.h>
+#include <node/ui_interface.h>
#include <util/system.h>
#include <util/translation.h>
#include <validation.h>
@@ -95,7 +95,7 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
updateWindowTitle();
rpcConsole = new RPCConsole(node, _platformStyle, nullptr);
- helpMessageDialog = new HelpMessageDialog(node, this, false);
+ helpMessageDialog = new HelpMessageDialog(this, false);
#ifdef ENABLE_WALLET
if(enableWallet)
{
@@ -112,6 +112,8 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
Q_EMIT consoleShown(rpcConsole);
}
+ modalOverlay = new ModalOverlay(enableWallet, this->centralWidget());
+
// Accept D&D of URIs
setAcceptDrops(true);
@@ -201,7 +203,6 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
openOptionsDialogWithTab(OptionsDialog::TAB_NETWORK);
});
- modalOverlay = new ModalOverlay(enableWallet, this->centralWidget());
connect(labelBlocksIcon, &GUIUtil::ClickableLabel::clicked, this, &BitcoinGUI::showModalOverlay);
connect(progressBar, &GUIUtil::ClickableProgressBar::clicked, this, &BitcoinGUI::showModalOverlay);
#ifdef ENABLE_WALLET
@@ -238,6 +239,7 @@ BitcoinGUI::~BitcoinGUI()
void BitcoinGUI::createActions()
{
QActionGroup *tabGroup = new QActionGroup(this);
+ connect(modalOverlay, &ModalOverlay::triggered, tabGroup, &QActionGroup::setEnabled);
overviewAction = new QAction(platformStyle->SingleColorIcon(":/icons/overview"), tr("&Overview"), this);
overviewAction->setStatusTip(tr("Show general overview of wallet"));
@@ -321,8 +323,10 @@ void BitcoinGUI::createActions()
signMessageAction->setStatusTip(tr("Sign messages with your Bitcoin addresses to prove you own them"));
verifyMessageAction = new QAction(tr("&Verify message..."), this);
verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified Bitcoin addresses"));
- m_load_psbt_action = new QAction(tr("Load PSBT..."), this);
+ m_load_psbt_action = new QAction(tr("&Load PSBT from file..."), this);
m_load_psbt_action->setStatusTip(tr("Load Partially Signed Bitcoin Transaction"));
+ m_load_psbt_clipboard_action = new QAction(tr("Load PSBT from clipboard..."), this);
+ m_load_psbt_clipboard_action->setStatusTip(tr("Load Partially Signed Bitcoin Transaction from clipboard"));
openRPCConsoleAction = new QAction(tr("Node window"), this);
openRPCConsoleAction->setStatusTip(tr("Open node debugging and diagnostic console"));
@@ -350,6 +354,9 @@ void BitcoinGUI::createActions()
m_create_wallet_action->setEnabled(false);
m_create_wallet_action->setStatusTip(tr("Create a new wallet"));
+ m_close_all_wallets_action = new QAction(tr("Close All Wallets..."), this);
+ m_close_all_wallets_action->setStatusTip(tr("Close all wallets"));
+
showHelpMessageAction = new QAction(tr("&Command-line options"), this);
showHelpMessageAction->setMenuRole(QAction::NoRole);
showHelpMessageAction->setStatusTip(tr("Show the %1 help message to get a list with possible Bitcoin command-line options").arg(PACKAGE_NAME));
@@ -378,6 +385,7 @@ void BitcoinGUI::createActions()
connect(signMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(signMessageAction, &QAction::triggered, [this]{ gotoSignMessageTab(); });
connect(m_load_psbt_action, &QAction::triggered, [this]{ gotoLoadPSBT(); });
+ connect(m_load_psbt_clipboard_action, &QAction::triggered, [this]{ gotoLoadPSBT(true); });
connect(verifyMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(verifyMessageAction, &QAction::triggered, [this]{ gotoVerifyMessageTab(); });
connect(usedSendingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedSendingAddresses);
@@ -421,7 +429,9 @@ void BitcoinGUI::createActions()
connect(activity, &CreateWalletActivity::finished, activity, &QObject::deleteLater);
activity->create();
});
-
+ connect(m_close_all_wallets_action, &QAction::triggered, [this] {
+ m_wallet_controller->closeAllWallets(this);
+ });
connect(m_mask_values_action, &QAction::toggled, this, &BitcoinGUI::setPrivacy);
}
#endif // ENABLE_WALLET
@@ -447,12 +457,14 @@ void BitcoinGUI::createMenuBar()
file->addAction(m_create_wallet_action);
file->addAction(m_open_wallet_action);
file->addAction(m_close_wallet_action);
+ file->addAction(m_close_all_wallets_action);
file->addSeparator();
file->addAction(openAction);
file->addAction(backupWalletAction);
file->addAction(signMessageAction);
file->addAction(verifyMessageAction);
file->addAction(m_load_psbt_action);
+ file->addAction(m_load_psbt_clipboard_action);
file->addSeparator();
}
file->addAction(quitAction);
@@ -562,7 +574,7 @@ void BitcoinGUI::createToolBars()
}
}
-void BitcoinGUI::setClientModel(ClientModel *_clientModel)
+void BitcoinGUI::setClientModel(ClientModel *_clientModel, interfaces::BlockAndHeaderTipInfo* tip_info)
{
this->clientModel = _clientModel;
if(_clientModel)
@@ -576,8 +588,8 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel)
connect(_clientModel, &ClientModel::numConnectionsChanged, this, &BitcoinGUI::setNumConnections);
connect(_clientModel, &ClientModel::networkActiveChanged, this, &BitcoinGUI::setNetworkActive);
- modalOverlay->setKnownBestHeight(_clientModel->getHeaderTipHeight(), QDateTime::fromTime_t(_clientModel->getHeaderTipTime()));
- setNumBlocks(m_node.getNumBlocks(), QDateTime::fromTime_t(m_node.getLastBlockTime()), m_node.getVerificationProgress(), false, SynchronizationState::INIT_DOWNLOAD);
+ modalOverlay->setKnownBestHeight(tip_info->header_height, QDateTime::fromTime_t(tip_info->header_time));
+ setNumBlocks(tip_info->block_height, QDateTime::fromTime_t(tip_info->block_time), tip_info->verification_progress, false, SynchronizationState::INIT_DOWNLOAD);
connect(_clientModel, &ClientModel::numBlocksChanged, this, &BitcoinGUI::setNumBlocks);
// Receive and report messages from client model
@@ -588,7 +600,7 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel)
// Show progress dialog
connect(_clientModel, &ClientModel::showProgress, this, &BitcoinGUI::showProgress);
- rpcConsole->setClientModel(_clientModel);
+ rpcConsole->setClientModel(_clientModel, tip_info->block_height, tip_info->block_time, tip_info->verification_progress);
updateProxyIcon();
@@ -673,6 +685,7 @@ void BitcoinGUI::removeWallet(WalletModel* walletModel)
m_wallet_selector->removeItem(index);
if (m_wallet_selector->count() == 0) {
setWalletActionsEnabled(false);
+ overviewAction->setChecked(true);
} else if (m_wallet_selector->count() == 1) {
m_wallet_selector_label_action->setVisible(false);
m_wallet_selector_action->setVisible(false);
@@ -727,6 +740,7 @@ void BitcoinGUI::setWalletActionsEnabled(bool enabled)
usedReceivingAddressesAction->setEnabled(enabled);
openAction->setEnabled(enabled);
m_close_wallet_action->setEnabled(enabled);
+ m_close_all_wallets_action->setEnabled(enabled);
}
void BitcoinGUI::createTrayIcon()
@@ -807,7 +821,7 @@ void BitcoinGUI::aboutClicked()
if(!clientModel)
return;
- HelpMessageDialog dlg(m_node, this, true);
+ HelpMessageDialog dlg(this, true);
dlg.exec();
}
@@ -871,9 +885,9 @@ void BitcoinGUI::gotoVerifyMessageTab(QString addr)
{
if (walletFrame) walletFrame->gotoVerifyMessageTab(addr);
}
-void BitcoinGUI::gotoLoadPSBT()
+void BitcoinGUI::gotoLoadPSBT(bool from_clipboard)
{
- if (walletFrame) walletFrame->gotoLoadPSBT();
+ if (walletFrame) walletFrame->gotoLoadPSBT(from_clipboard);
}
#endif // ENABLE_WALLET
@@ -1026,7 +1040,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
if(count != prevBlocks)
{
labelBlocksIcon->setPixmap(platformStyle->SingleColorIcon(QString(
- ":/movies/spinner-%1").arg(spinnerFrame, 3, 10, QChar('0')))
+ ":/animation/spinner-%1").arg(spinnerFrame, 3, 10, QChar('0')))
.pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES;
}
@@ -1062,9 +1076,6 @@ void BitcoinGUI::message(const QString& title, QString message, unsigned int sty
int nMBoxIcon = QMessageBox::Information;
int nNotifyIcon = Notificator::Information;
- bool prefix = !(style & CClientUIInterface::MSG_NOPREFIX);
- style &= ~CClientUIInterface::MSG_NOPREFIX;
-
QString msgType;
if (!title.isEmpty()) {
msgType = title;
@@ -1072,11 +1083,11 @@ void BitcoinGUI::message(const QString& title, QString message, unsigned int sty
switch (style) {
case CClientUIInterface::MSG_ERROR:
msgType = tr("Error");
- if (prefix) message = tr("Error: %1").arg(message);
+ message = tr("Error: %1").arg(message);
break;
case CClientUIInterface::MSG_WARNING:
msgType = tr("Warning");
- if (prefix) message = tr("Warning: %1").arg(message);
+ message = tr("Warning: %1").arg(message);
break;
case CClientUIInterface::MSG_INFORMATION:
msgType = tr("Information");
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index c0198dd168..4c55f28693 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -43,6 +43,7 @@ enum class SynchronizationState;
namespace interfaces {
class Handler;
class Node;
+struct BlockAndHeaderTipInfo;
}
QT_BEGIN_NAMESPACE
@@ -75,7 +76,7 @@ public:
/** Set the client model.
The client model represents the part of the core that communicates with the P2P network, and is wallet-agnostic.
*/
- void setClientModel(ClientModel *clientModel);
+ void setClientModel(ClientModel *clientModel = nullptr, interfaces::BlockAndHeaderTipInfo* tip_info = nullptr);
#ifdef ENABLE_WALLET
void setWalletController(WalletController* wallet_controller);
#endif
@@ -139,6 +140,7 @@ private:
QAction* signMessageAction = nullptr;
QAction* verifyMessageAction = nullptr;
QAction* m_load_psbt_action = nullptr;
+ QAction* m_load_psbt_clipboard_action = nullptr;
QAction* aboutAction = nullptr;
QAction* receiveCoinsAction = nullptr;
QAction* receiveCoinsMenuAction = nullptr;
@@ -155,6 +157,7 @@ private:
QAction* m_open_wallet_action{nullptr};
QMenu* m_open_wallet_menu{nullptr};
QAction* m_close_wallet_action{nullptr};
+ QAction* m_close_all_wallets_action{nullptr};
QAction* m_wallet_selector_label_action = nullptr;
QAction* m_wallet_selector_action = nullptr;
QAction* m_mask_values_action{nullptr};
@@ -277,8 +280,8 @@ public Q_SLOTS:
void gotoSignMessageTab(QString addr = "");
/** Show Sign/Verify Message dialog and switch to verify message tab */
void gotoVerifyMessageTab(QString addr = "");
- /** Show load Partially Signed Bitcoin Transaction dialog */
- void gotoLoadPSBT();
+ /** Load Partially Signed Bitcoin Transaction from file or clipboard */
+ void gotoLoadPSBT(bool from_clipboard = false);
/** Show open dialog */
void openClicked();
diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp
index 318a6dcbfd..fd55c547fc 100644
--- a/src/qt/bitcoinunits.cpp
+++ b/src/qt/bitcoinunits.cpp
@@ -114,7 +114,7 @@ QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, Separator
// confused with the decimal marker.
QChar thin_sp(THIN_SP_CP);
int q_size = quotient_str.size();
- if (separators == separatorAlways || (separators == separatorStandard && q_size > 4))
+ if (separators == SeparatorStyle::ALWAYS || (separators == SeparatorStyle::STANDARD && q_size > 4))
for (int i = 3; i < q_size; i += 3)
quotient_str.insert(q_size - i, thin_sp);
diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h
index dac5484393..e22ba0a938 100644
--- a/src/qt/bitcoinunits.h
+++ b/src/qt/bitcoinunits.h
@@ -46,11 +46,11 @@ public:
SAT
};
- enum SeparatorStyle
+ enum class SeparatorStyle
{
- separatorNever,
- separatorStandard,
- separatorAlways
+ NEVER,
+ STANDARD,
+ ALWAYS
};
//! @name Static API
@@ -72,11 +72,11 @@ public:
//! Number of decimals left
static int decimals(int unit);
//! Format as string
- static QString format(int unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = separatorStandard, bool justify = false);
+ static QString format(int unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = SeparatorStyle::STANDARD, bool justify = false);
//! Format as string (with unit)
- static QString formatWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=separatorStandard);
+ static QString formatWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD);
//! Format as HTML string (with unit)
- static QString formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=separatorStandard);
+ static QString formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD);
//! Format as string (with unit) of fixed length to preserve privacy, if it is set.
static QString formatWithPrivacy(int unit, const CAmount& amount, SeparatorStyle separators, bool privacy);
//! Parse string to coin amount
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index f15921c5bc..7822d4c5f3 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -116,9 +116,23 @@ int ClientModel::getNumBlocks() const
uint256 ClientModel::getBestBlockHash()
{
+ uint256 tip{WITH_LOCK(m_cached_tip_mutex, return m_cached_tip_blocks)};
+
+ if (!tip.IsNull()) {
+ return tip;
+ }
+
+ // Lock order must be: first `cs_main`, then `m_cached_tip_mutex`.
+ // The following will lock `cs_main` (and release it), so we must not
+ // own `m_cached_tip_mutex` here.
+ tip = m_node.getBestBlockHash();
+
LOCK(m_cached_tip_mutex);
+ // We checked that `m_cached_tip_blocks` is not null above, but then we
+ // released the mutex `m_cached_tip_mutex`, so it could have changed in the
+ // meantime. Thus, check again.
if (m_cached_tip_blocks.IsNull()) {
- m_cached_tip_blocks = m_node.getBestBlockHash();
+ m_cached_tip_blocks = tip;
}
return m_cached_tip_blocks;
}
@@ -152,7 +166,7 @@ enum BlockSource ClientModel::getBlockSource() const
QString ClientModel::getStatusBarWarnings() const
{
- return QString::fromStdString(m_node.getWarnings());
+ return QString::fromStdString(m_node.getWarnings().translated);
}
OptionsModel *ClientModel::getOptionsModel()
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index db77c17df0..7c72858501 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -400,7 +400,6 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel *
// nPayAmount
CAmount nPayAmount = 0;
bool fDust = false;
- CMutableTransaction txDummy;
for (const CAmount &amount : CoinControlDialog::payAmounts)
{
nPayAmount += amount;
@@ -409,7 +408,6 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel *
{
// Assumes a p2pkh script size
CTxOut txout(amount, CScript() << std::vector<unsigned char>(24, 0));
- txDummy.vout.push_back(txout);
fDust |= IsDust(txout, model->node().getDustRelayFee());
}
}
@@ -458,7 +456,7 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel *
{
CPubKey pubkey;
PKHash *pkhash = boost::get<PKHash>(&address);
- if (pkhash && model->wallet().getPubKey(out.txout.scriptPubKey, CKeyID(*pkhash), pubkey))
+ if (pkhash && model->wallet().getPubKey(out.txout.scriptPubKey, ToKeyID(*pkhash), pubkey))
{
nBytesInputs += (pubkey.IsCompressed() ? 148 : 180);
}
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index 8b70800838..d210faec03 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -290,7 +290,7 @@
<item row="11" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
- <string>Current number of blocks</string>
+ <string>Current block height</string>
</property>
</widget>
</item>
@@ -1078,22 +1078,16 @@
<height>426</height>
</rect>
</property>
- <property name="minimumSize">
- <size>
- <width>300</width>
- <height>0</height>
- </size>
- </property>
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,1">
<item row="0" column="0">
<widget class="QLabel" name="label_30">
<property name="text">
- <string>Whitelisted</string>
+ <string>Permissions</string>
</property>
</widget>
</item>
<item row="0" column="1">
- <widget class="QLabel" name="peerWhitelisted">
+ <widget class="QLabel" name="peerPermissions">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
@@ -1270,36 +1264,13 @@
</widget>
</item>
<item row="8" column="0">
- <widget class="QLabel" name="label_24">
- <property name="text">
- <string>Ban Score</string>
- </property>
- </widget>
- </item>
- <item row="8" column="1">
- <widget class="QLabel" name="peerBanScore">
- <property name="cursor">
- <cursorShape>IBeamCursor</cursorShape>
- </property>
- <property name="text">
- <string>N/A</string>
- </property>
- <property name="textFormat">
- <enum>Qt::PlainText</enum>
- </property>
- <property name="textInteractionFlags">
- <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
- </property>
- </widget>
- </item>
- <item row="9" column="0">
<widget class="QLabel" name="label_22">
<property name="text">
<string>Connection Time</string>
</property>
</widget>
</item>
- <item row="9" column="1">
+ <item row="8" column="1">
<widget class="QLabel" name="peerConnTime">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1315,14 +1286,14 @@
</property>
</widget>
</item>
- <item row="10" column="0">
+ <item row="9" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Last Send</string>
</property>
</widget>
</item>
- <item row="10" column="1">
+ <item row="9" column="1">
<widget class="QLabel" name="peerLastSend">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1338,14 +1309,14 @@
</property>
</widget>
</item>
- <item row="11" column="0">
+ <item row="10" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Last Receive</string>
</property>
</widget>
</item>
- <item row="11" column="1">
+ <item row="10" column="1">
<widget class="QLabel" name="peerLastRecv">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1361,14 +1332,14 @@
</property>
</widget>
</item>
- <item row="12" column="0">
+ <item row="11" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Sent</string>
</property>
</widget>
</item>
- <item row="12" column="1">
+ <item row="11" column="1">
<widget class="QLabel" name="peerBytesSent">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1384,14 +1355,14 @@
</property>
</widget>
</item>
- <item row="13" column="0">
+ <item row="12" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Received</string>
</property>
</widget>
</item>
- <item row="13" column="1">
+ <item row="12" column="1">
<widget class="QLabel" name="peerBytesRecv">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1407,14 +1378,14 @@
</property>
</widget>
</item>
- <item row="14" column="0">
+ <item row="13" column="0">
<widget class="QLabel" name="label_26">
<property name="text">
<string>Ping Time</string>
</property>
</widget>
</item>
- <item row="14" column="1">
+ <item row="13" column="1">
<widget class="QLabel" name="peerPingTime">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1430,7 +1401,7 @@
</property>
</widget>
</item>
- <item row="15" column="0">
+ <item row="14" column="0">
<widget class="QLabel" name="peerPingWaitLabel">
<property name="toolTip">
<string>The duration of a currently outstanding ping.</string>
@@ -1440,7 +1411,7 @@
</property>
</widget>
</item>
- <item row="15" column="1">
+ <item row="14" column="1">
<widget class="QLabel" name="peerPingWait">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1456,14 +1427,14 @@
</property>
</widget>
</item>
- <item row="16" column="0">
+ <item row="15" column="0">
<widget class="QLabel" name="peerMinPingLabel">
<property name="text">
<string>Min Ping</string>
</property>
</widget>
</item>
- <item row="16" column="1">
+ <item row="15" column="1">
<widget class="QLabel" name="peerMinPing">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1479,14 +1450,14 @@
</property>
</widget>
</item>
- <item row="17" column="0">
+ <item row="16" column="0">
<widget class="QLabel" name="label_timeoffset">
<property name="text">
<string>Time Offset</string>
</property>
</widget>
</item>
- <item row="17" column="1">
+ <item row="16" column="1">
<widget class="QLabel" name="timeoffset">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1502,7 +1473,7 @@
</property>
</widget>
</item>
- <item row="18" column="0">
+ <item row="17" column="0">
<widget class="QLabel" name="peerMappedASLabel">
<property name="toolTip">
<string>The mapped Autonomous System used for diversifying peer selection.</string>
@@ -1512,7 +1483,7 @@
</property>
</widget>
</item>
- <item row="18" column="1">
+ <item row="17" column="1">
<widget class="QLabel" name="peerMappedAS">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
@@ -1528,7 +1499,7 @@
</property>
</widget>
</item>
- <item row="19" column="0">
+ <item row="18" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui
index fea759dee0..0016fb9739 100644
--- a/src/qt/forms/optionsdialog.ui
+++ b/src/qt/forms/optionsdialog.ui
@@ -459,10 +459,10 @@
<item>
<widget class="QCheckBox" name="connectSocksTor">
<property name="toolTip">
- <string>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</string>
+ <string>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor onion services.</string>
</property>
<property name="text">
- <string>Use separate SOCKS&amp;5 proxy to reach peers via Tor hidden services:</string>
+ <string>Use separate SOCKS&amp;5 proxy to reach peers via Tor onion services:</string>
</property>
</widget>
</item>
diff --git a/src/qt/forms/psbtoperationsdialog.ui b/src/qt/forms/psbtoperationsdialog.ui
new file mode 100644
index 0000000000..c2e2f5035b
--- /dev/null
+++ b/src/qt/forms/psbtoperationsdialog.ui
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PSBTOperationsDialog</class>
+ <widget class="QDialog" name="PSBTOperationsDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>585</width>
+ <height>327</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>12</number>
+ </property>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <property name="bottomMargin">
+ <number>12</number>
+ </property>
+ <item>
+ <layout class="QVBoxLayout" name="mainDialogLayout">
+ <property name="spacing">
+ <number>5</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="statusBar">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTextEdit" name="transactionDescription">
+ <property name="undoRedoEnabled">
+ <bool>false</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="buttonRowLayout">
+ <property name="spacing">
+ <number>5</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="signTransactionButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <weight>50</weight>
+ <bold>false</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Sign Tx</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ <property name="default">
+ <bool>false</bool>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="broadcastTransactionButton">
+ <property name="text">
+ <string>Broadcast Tx</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="copyToClipboardButton">
+ <property name="text">
+ <string>Copy to Clipboard</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="saveButton">
+ <property name="text">
+ <string>Save...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="closeButton">
+ <property name="text">
+ <string>Close</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui
index 7dbee6d689..06d39426c9 100644
--- a/src/qt/forms/receivecoinsdialog.ui
+++ b/src/qt/forms/receivecoinsdialog.ui
@@ -114,6 +114,12 @@
<iconset resource="../bitcoin.qrc">
<normaloff>:/icons/receiving_addresses</normaloff>:/icons/receiving_addresses</iconset>
</property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
</widget>
</item>
<item>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index ce44d4f3a5..bab17562a6 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -21,11 +21,6 @@
#include <util/system.h>
#ifdef WIN32
-#ifdef _WIN32_IE
-#undef _WIN32_IE
-#endif
-#define _WIN32_IE 0x0501
-#define WIN32_LEAN_AND_MEAN 1
#ifndef NOMINMAX
#define NOMINMAX
#endif
@@ -94,7 +89,7 @@ static std::string DummyAddress(const CChainParams &params)
std::vector<unsigned char> sourcedata = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
sourcedata.insert(sourcedata.end(), dummydata, dummydata + sizeof(dummydata));
for(int i=0; i<256; ++i) { // Try every trailing byte
- std::string s = EncodeBase58(sourcedata.data(), sourcedata.data() + sourcedata.size());
+ std::string s = EncodeBase58(sourcedata);
if (!IsValidDestinationString(s)) {
return s;
}
@@ -188,7 +183,7 @@ QString formatBitcoinURI(const SendCoinsRecipient &info)
if (info.amount)
{
- ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, info.amount, false, BitcoinUnits::separatorNever));
+ ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, info.amount, false, BitcoinUnits::SeparatorStyle::NEVER));
paramCount++;
}
@@ -450,6 +445,28 @@ bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt)
return QObject::eventFilter(obj, evt);
}
+LabelOutOfFocusEventFilter::LabelOutOfFocusEventFilter(QObject* parent)
+ : QObject(parent)
+{
+}
+
+bool LabelOutOfFocusEventFilter::eventFilter(QObject* watched, QEvent* event)
+{
+ if (event->type() == QEvent::FocusOut) {
+ auto focus_out = static_cast<QFocusEvent*>(event);
+ if (focus_out->reason() != Qt::PopupFocusReason) {
+ auto label = qobject_cast<QLabel*>(watched);
+ if (label) {
+ auto flags = label->textInteractionFlags();
+ label->setTextInteractionFlags(Qt::NoTextInteraction);
+ label->setTextInteractionFlags(flags);
+ }
+ }
+ }
+
+ return QObject::eventFilter(watched, event);
+}
+
void TableViewLastColumnResizingFixer::connectViewHeadersSignals()
{
connect(tableView->horizontalHeader(), &QHeaderView::sectionResized, this, &TableViewLastColumnResizingFixer::on_sectionResized);
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 8741d90102..2bd94b5eb3 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -162,6 +162,21 @@ namespace GUIUtil
};
/**
+ * Qt event filter that intercepts QEvent::FocusOut events for QLabel objects, and
+ * resets their `textInteractionFlags' property to get rid of the visible cursor.
+ *
+ * This is a temporary fix of QTBUG-59514.
+ */
+ class LabelOutOfFocusEventFilter : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ explicit LabelOutOfFocusEventFilter(QObject* parent);
+ bool eventFilter(QObject* watched, QEvent* event) override;
+ };
+
+ /**
* Makes a QTableView last column feel as if it was being resized from its left border.
* Also makes sure the column widths are never larger than the table's viewport.
* In Qt, all columns are resizable from the right, but it's not intuitive resizing the last column from the right.
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index ad21dfc3ef..235722d091 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -6,6 +6,7 @@
#include <config/bitcoin-config.h>
#endif
+#include <chainparams.h>
#include <fs.h>
#include <qt/intro.h>
#include <qt/forms/ui_intro.h>
@@ -181,7 +182,7 @@ void Intro::setDataDirectory(const QString &dataDir)
}
}
-bool Intro::showIfNeeded(interfaces::Node& node, bool& did_show_intro, bool& prune)
+bool Intro::showIfNeeded(bool& did_show_intro, bool& prune)
{
did_show_intro = false;
@@ -199,13 +200,13 @@ bool Intro::showIfNeeded(interfaces::Node& node, bool& did_show_intro, bool& pru
{
/* Use selectParams here to guarantee Params() can be used by node interface */
try {
- node.selectParams(gArgs.GetChainName());
+ SelectParams(gArgs.GetChainName());
} catch (const std::exception&) {
return false;
}
/* If current default data directory does not exist, let the user choose one */
- Intro intro(0, node.getAssumedBlockchainSize(), node.getAssumedChainStateSize());
+ Intro intro(0, Params().AssumedBlockchainSize(), Params().AssumedChainStateSize());
intro.setDataDirectory(dataDir);
intro.setWindowIcon(QIcon(":icons/bitcoin"));
did_show_intro = true;
@@ -242,7 +243,7 @@ bool Intro::showIfNeeded(interfaces::Node& node, bool& did_show_intro, bool& pru
* (to be consistent with bitcoind behavior)
*/
if(dataDir != GUIUtil::getDefaultDataDirectory()) {
- node.softSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
+ gArgs.SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
}
return true;
}
diff --git a/src/qt/intro.h b/src/qt/intro.h
index 732393246e..51f42de7ac 100644
--- a/src/qt/intro.h
+++ b/src/qt/intro.h
@@ -47,7 +47,7 @@ public:
* @note do NOT call global GetDataDir() before calling this function, this
* will cause the wrong path to be cached.
*/
- static bool showIfNeeded(interfaces::Node& node, bool& did_show_intro, bool& prune);
+ static bool showIfNeeded(bool& did_show_intro, bool& prune);
Q_SIGNALS:
void requestCheck();
diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp
index 0ba1beaf3e..8070aa627c 100644
--- a/src/qt/modaloverlay.cpp
+++ b/src/qt/modaloverlay.cpp
@@ -171,6 +171,8 @@ void ModalOverlay::showHide(bool hide, bool userRequested)
if ( (layerIsVisible && !hide) || (!layerIsVisible && hide) || (!hide && userClosed && !userRequested))
return;
+ Q_EMIT triggered(hide);
+
if (!isVisible() && !hide)
setVisible(true);
diff --git a/src/qt/modaloverlay.h b/src/qt/modaloverlay.h
index 1d84046d3d..7b07777641 100644
--- a/src/qt/modaloverlay.h
+++ b/src/qt/modaloverlay.h
@@ -25,16 +25,20 @@ public:
explicit ModalOverlay(bool enable_wallet, QWidget *parent);
~ModalOverlay();
-public Q_SLOTS:
void tipUpdate(int count, const QDateTime& blockDate, double nVerificationProgress);
void setKnownBestHeight(int count, const QDateTime& blockDate);
- void toggleVisibility();
// will show or hide the modal layer
void showHide(bool hide = false, bool userRequested = false);
- void closeClicked();
bool isLayerVisible() const { return layerIsVisible; }
+public Q_SLOTS:
+ void toggleVisibility();
+ void closeClicked();
+
+Q_SIGNALS:
+ void triggered(bool hidden);
+
protected:
bool eventFilter(QObject * obj, QEvent * ev) override;
bool event(QEvent* ev) override;
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 58a7591c95..7e089b4f95 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -27,8 +27,8 @@ const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1";
static const QString GetDefaultProxyAddress();
-OptionsModel::OptionsModel(interfaces::Node& node, QObject *parent, bool resetSettings) :
- QAbstractListModel(parent), m_node(node)
+OptionsModel::OptionsModel(QObject *parent, bool resetSettings) :
+ QAbstractListModel(parent)
{
Init(resetSettings);
}
@@ -97,12 +97,12 @@ void OptionsModel::Init(bool resetSettings)
if (!settings.contains("nDatabaseCache"))
settings.setValue("nDatabaseCache", (qint64)nDefaultDbCache);
- if (!m_node.softSetArg("-dbcache", settings.value("nDatabaseCache").toString().toStdString()))
+ if (!gArgs.SoftSetArg("-dbcache", settings.value("nDatabaseCache").toString().toStdString()))
addOverriddenOption("-dbcache");
if (!settings.contains("nThreadsScriptVerif"))
settings.setValue("nThreadsScriptVerif", DEFAULT_SCRIPTCHECK_THREADS);
- if (!m_node.softSetArg("-par", settings.value("nThreadsScriptVerif").toString().toStdString()))
+ if (!gArgs.SoftSetArg("-par", settings.value("nThreadsScriptVerif").toString().toStdString()))
addOverriddenOption("-par");
if (!settings.contains("strDataDir"))
@@ -112,19 +112,19 @@ void OptionsModel::Init(bool resetSettings)
#ifdef ENABLE_WALLET
if (!settings.contains("bSpendZeroConfChange"))
settings.setValue("bSpendZeroConfChange", true);
- if (!m_node.softSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool()))
+ if (!gArgs.SoftSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool()))
addOverriddenOption("-spendzeroconfchange");
#endif
// Network
if (!settings.contains("fUseUPnP"))
settings.setValue("fUseUPnP", DEFAULT_UPNP);
- if (!m_node.softSetBoolArg("-upnp", settings.value("fUseUPnP").toBool()))
+ if (!gArgs.SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool()))
addOverriddenOption("-upnp");
if (!settings.contains("fListen"))
settings.setValue("fListen", DEFAULT_LISTEN);
- if (!m_node.softSetBoolArg("-listen", settings.value("fListen").toBool()))
+ if (!gArgs.SoftSetBoolArg("-listen", settings.value("fListen").toBool()))
addOverriddenOption("-listen");
if (!settings.contains("fUseProxy"))
@@ -132,7 +132,7 @@ void OptionsModel::Init(bool resetSettings)
if (!settings.contains("addrProxy"))
settings.setValue("addrProxy", GetDefaultProxyAddress());
// Only try to set -proxy, if user has enabled fUseProxy
- if (settings.value("fUseProxy").toBool() && !m_node.softSetArg("-proxy", settings.value("addrProxy").toString().toStdString()))
+ if ((settings.value("fUseProxy").toBool() && !gArgs.SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString())))
addOverriddenOption("-proxy");
else if(!settings.value("fUseProxy").toBool() && !gArgs.GetArg("-proxy", "").empty())
addOverriddenOption("-proxy");
@@ -142,7 +142,7 @@ void OptionsModel::Init(bool resetSettings)
if (!settings.contains("addrSeparateProxyTor"))
settings.setValue("addrSeparateProxyTor", GetDefaultProxyAddress());
// Only try to set -onion, if user has enabled fUseSeparateProxyTor
- if (settings.value("fUseSeparateProxyTor").toBool() && !m_node.softSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString()))
+ if ((settings.value("fUseSeparateProxyTor").toBool() && !gArgs.SoftSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString())))
addOverriddenOption("-onion");
else if(!settings.value("fUseSeparateProxyTor").toBool() && !gArgs.GetArg("-onion", "").empty())
addOverriddenOption("-onion");
@@ -150,7 +150,7 @@ void OptionsModel::Init(bool resetSettings)
// Display
if (!settings.contains("language"))
settings.setValue("language", "");
- if (!m_node.softSetArg("-lang", settings.value("language").toString().toStdString()))
+ if (!gArgs.SoftSetArg("-lang", settings.value("language").toString().toStdString()))
addOverriddenOption("-lang");
language = settings.value("language").toString();
@@ -244,10 +244,10 @@ void OptionsModel::SetPruneEnabled(bool prune, bool force)
const int64_t prune_target_mib = PruneGBtoMiB(settings.value("nPruneSize").toInt());
std::string prune_val = prune ? ToString(prune_target_mib) : "0";
if (force) {
- m_node.forceSetArg("-prune", prune_val);
+ gArgs.ForceSetArg("-prune", prune_val);
return;
}
- if (!m_node.softSetArg("-prune", prune_val)) {
+ if (!gArgs.SoftSetArg("-prune", prune_val)) {
addOverriddenOption("-prune");
}
}
@@ -353,7 +353,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
break;
case MapPortUPnP: // core option - can be changed on-the-fly
settings.setValue("fUseUPnP", value.toBool());
- m_node.mapPort(value.toBool());
+ node().mapPort(value.toBool());
break;
case MinimizeOnClose:
fMinimizeOnClose = value.toBool();
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 6ca5ac9d75..3d9e7bbb80 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -6,16 +6,19 @@
#define BITCOIN_QT_OPTIONSMODEL_H
#include <amount.h>
+#include <cstdint>
#include <qt/guiconstants.h>
#include <QAbstractListModel>
+#include <assert.h>
+
namespace interfaces {
class Node;
}
extern const char *DEFAULT_GUI_PROXY_HOST;
-static constexpr unsigned short DEFAULT_GUI_PROXY_PORT = 9050;
+static constexpr uint16_t DEFAULT_GUI_PROXY_PORT = 9050;
/**
* Convert configured prune target MiB to displayed GB. Round up to avoid underestimating max disk usage.
@@ -38,7 +41,7 @@ class OptionsModel : public QAbstractListModel
Q_OBJECT
public:
- explicit OptionsModel(interfaces::Node& node, QObject *parent = nullptr, bool resetSettings = false);
+ explicit OptionsModel(QObject *parent = nullptr, bool resetSettings = false);
enum OptionID {
StartAtStartup, // bool
@@ -91,10 +94,11 @@ public:
void setRestartRequired(bool fRequired);
bool isRestartRequired() const;
- interfaces::Node& node() const { return m_node; }
+ interfaces::Node& node() const { assert(m_node); return *m_node; }
+ void setNode(interfaces::Node& node) { assert(!m_node); m_node = &node; }
private:
- interfaces::Node& m_node;
+ interfaces::Node* m_node = nullptr;
/* Qt-only settings */
bool fHideTrayIcon;
bool fMinimizeToTray;
diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp
index 0af70f2735..b536567c8b 100644
--- a/src/qt/overviewpage.cpp
+++ b/src/qt/overviewpage.cpp
@@ -88,7 +88,7 @@ public:
foreground = option.palette.color(QPalette::Text);
}
painter->setPen(foreground);
- QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true, BitcoinUnits::separatorAlways);
+ QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true, BitcoinUnits::SeparatorStyle::ALWAYS);
if(!confirmed)
{
amountText = QString("[") + amountText + QString("]");
@@ -180,25 +180,25 @@ void OverviewPage::setBalance(const interfaces::WalletBalances& balances)
m_balances = balances;
if (walletModel->wallet().isLegacy()) {
if (walletModel->wallet().privateKeysDisabled()) {
- ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance, BitcoinUnits::separatorAlways, m_privacy));
- ui->labelUnconfirmed->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_watch_only_balance, BitcoinUnits::separatorAlways, m_privacy));
- ui->labelImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_watch_only_balance, BitcoinUnits::separatorAlways, m_privacy));
- ui->labelTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, BitcoinUnits::separatorAlways, m_privacy));
+ ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelUnconfirmed->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
} else {
- ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance, BitcoinUnits::separatorAlways, m_privacy));
- ui->labelUnconfirmed->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_balance, BitcoinUnits::separatorAlways, m_privacy));
- ui->labelImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_balance, BitcoinUnits::separatorAlways, m_privacy));
- ui->labelTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, BitcoinUnits::separatorAlways, m_privacy));
- ui->labelWatchAvailable->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance, BitcoinUnits::separatorAlways, m_privacy));
- ui->labelWatchPending->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_watch_only_balance, BitcoinUnits::separatorAlways, m_privacy));
- ui->labelWatchImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_watch_only_balance, BitcoinUnits::separatorAlways, m_privacy));
- ui->labelWatchTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, BitcoinUnits::separatorAlways, m_privacy));
+ ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelUnconfirmed->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelWatchAvailable->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelWatchPending->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelWatchImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelWatchTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
}
} else {
- ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance, BitcoinUnits::separatorAlways, m_privacy));
- ui->labelUnconfirmed->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_balance, BitcoinUnits::separatorAlways, m_privacy));
- ui->labelImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_balance, BitcoinUnits::separatorAlways, m_privacy));
- ui->labelTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, BitcoinUnits::separatorAlways, m_privacy));
+ ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelUnconfirmed->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
+ ui->labelTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy));
}
// only show immature (newly mined) balance if it's non-zero, so as not to complicate things
// for the non-mining users
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index beca78a021..8679ced685 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -14,9 +14,9 @@
#include <chainparams.h>
#include <interfaces/node.h>
-#include <policy/policy.h>
#include <key_io.h>
-#include <ui_interface.h>
+#include <node/ui_interface.h>
+#include <policy/policy.h>
#include <util/system.h>
#include <wallet/wallet.h>
@@ -74,7 +74,7 @@ static QSet<QString> savedPaymentRequests;
// Warning: ipcSendCommandLine() is called early in init,
// so don't use "Q_EMIT message()", but "QMessageBox::"!
//
-void PaymentServer::ipcParseCommandLine(interfaces::Node& node, int argc, char* argv[])
+void PaymentServer::ipcParseCommandLine(int argc, char* argv[])
{
for (int i = 1; i < argc; i++)
{
@@ -97,11 +97,11 @@ void PaymentServer::ipcParseCommandLine(interfaces::Node& node, int argc, char*
auto tempChainParams = CreateChainParams(CBaseChainParams::MAIN);
if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
- node.selectParams(CBaseChainParams::MAIN);
+ SelectParams(CBaseChainParams::MAIN);
} else {
tempChainParams = CreateChainParams(CBaseChainParams::TESTNET);
if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) {
- node.selectParams(CBaseChainParams::TESTNET);
+ SelectParams(CBaseChainParams::TESTNET);
}
}
}
diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h
index 154f4a7ea6..eaf2bafe59 100644
--- a/src/qt/paymentserver.h
+++ b/src/qt/paymentserver.h
@@ -61,7 +61,7 @@ class PaymentServer : public QObject
public:
// Parse URIs on command line
// Returns false on error
- static void ipcParseCommandLine(interfaces::Node& node, int argc, char *argv[]);
+ static void ipcParseCommandLine(int argc, char *argv[]);
// Returns true if there were URIs on the command line
// which were successfully sent to an already-running
diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp
new file mode 100644
index 0000000000..58167d4bb4
--- /dev/null
+++ b/src/qt/psbtoperationsdialog.cpp
@@ -0,0 +1,268 @@
+// Copyright (c) 2011-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <qt/psbtoperationsdialog.h>
+
+#include <core_io.h>
+#include <interfaces/node.h>
+#include <key_io.h>
+#include <node/psbt.h>
+#include <policy/policy.h>
+#include <qt/bitcoinunits.h>
+#include <qt/forms/ui_psbtoperationsdialog.h>
+#include <qt/guiutil.h>
+#include <qt/optionsmodel.h>
+#include <util/strencodings.h>
+
+#include <iostream>
+
+
+PSBTOperationsDialog::PSBTOperationsDialog(
+ QWidget* parent, WalletModel* wallet_model, ClientModel* client_model) : QDialog(parent),
+ m_ui(new Ui::PSBTOperationsDialog),
+ m_wallet_model(wallet_model),
+ m_client_model(client_model)
+{
+ m_ui->setupUi(this);
+ setWindowTitle("PSBT Operations");
+
+ connect(m_ui->signTransactionButton, &QPushButton::clicked, this, &PSBTOperationsDialog::signTransaction);
+ connect(m_ui->broadcastTransactionButton, &QPushButton::clicked, this, &PSBTOperationsDialog::broadcastTransaction);
+ connect(m_ui->copyToClipboardButton, &QPushButton::clicked, this, &PSBTOperationsDialog::copyToClipboard);
+ connect(m_ui->saveButton, &QPushButton::clicked, this, &PSBTOperationsDialog::saveTransaction);
+
+ connect(m_ui->closeButton, &QPushButton::clicked, this, &PSBTOperationsDialog::close);
+
+ m_ui->signTransactionButton->setEnabled(false);
+ m_ui->broadcastTransactionButton->setEnabled(false);
+}
+
+PSBTOperationsDialog::~PSBTOperationsDialog()
+{
+ delete m_ui;
+}
+
+void PSBTOperationsDialog::openWithPSBT(PartiallySignedTransaction psbtx)
+{
+ m_transaction_data = psbtx;
+
+ bool complete;
+ size_t n_could_sign;
+ FinalizePSBT(psbtx); // Make sure all existing signatures are fully combined before checking for completeness.
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, m_transaction_data, complete, &n_could_sign);
+ if (err != TransactionError::OK) {
+ showStatus(tr("Failed to load transaction: %1")
+ .arg(QString::fromStdString(TransactionErrorString(err).translated)), StatusLevel::ERR);
+ return;
+ }
+
+ m_ui->broadcastTransactionButton->setEnabled(complete);
+ m_ui->signTransactionButton->setEnabled(!complete && !m_wallet_model->wallet().privateKeysDisabled() && n_could_sign > 0);
+
+ updateTransactionDisplay();
+}
+
+void PSBTOperationsDialog::signTransaction()
+{
+ bool complete;
+ size_t n_signed;
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, m_transaction_data, complete, &n_signed);
+
+ if (err != TransactionError::OK) {
+ showStatus(tr("Failed to sign transaction: %1")
+ .arg(QString::fromStdString(TransactionErrorString(err).translated)), StatusLevel::ERR);
+ return;
+ }
+
+ updateTransactionDisplay();
+
+ if (!complete && n_signed < 1) {
+ showStatus(tr("Could not sign any more inputs."), StatusLevel::WARN);
+ } else if (!complete) {
+ showStatus(tr("Signed %1 inputs, but more signatures are still required.").arg(n_signed),
+ StatusLevel::INFO);
+ } else {
+ showStatus(tr("Signed transaction successfully. Transaction is ready to broadcast."),
+ StatusLevel::INFO);
+ m_ui->broadcastTransactionButton->setEnabled(true);
+ }
+}
+
+void PSBTOperationsDialog::broadcastTransaction()
+{
+ CMutableTransaction mtx;
+ if (!FinalizeAndExtractPSBT(m_transaction_data, mtx)) {
+ // This is never expected to fail unless we were given a malformed PSBT
+ // (e.g. with an invalid signature.)
+ showStatus(tr("Unknown error processing transaction."), StatusLevel::ERR);
+ return;
+ }
+
+ CTransactionRef tx = MakeTransactionRef(mtx);
+ std::string err_string;
+ TransactionError error = BroadcastTransaction(
+ *m_client_model->node().context(), tx, err_string, DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK(), /* relay */ true, /* await_callback */ false);
+
+ if (error == TransactionError::OK) {
+ showStatus(tr("Transaction broadcast successfully! Transaction ID: %1")
+ .arg(QString::fromStdString(tx->GetHash().GetHex())), StatusLevel::INFO);
+ } else {
+ showStatus(tr("Transaction broadcast failed: %1")
+ .arg(QString::fromStdString(TransactionErrorString(error).translated)), StatusLevel::ERR);
+ }
+}
+
+void PSBTOperationsDialog::copyToClipboard() {
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << m_transaction_data;
+ GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
+ showStatus(tr("PSBT copied to clipboard."), StatusLevel::INFO);
+}
+
+void PSBTOperationsDialog::saveTransaction() {
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << m_transaction_data;
+
+ QString selected_filter;
+ QString filename_suggestion = "";
+ bool first = true;
+ for (const CTxOut& out : m_transaction_data.tx->vout) {
+ if (!first) {
+ filename_suggestion.append("-");
+ }
+ CTxDestination address;
+ ExtractDestination(out.scriptPubKey, address);
+ QString amount = BitcoinUnits::format(m_wallet_model->getOptionsModel()->getDisplayUnit(), out.nValue);
+ QString address_str = QString::fromStdString(EncodeDestination(address));
+ filename_suggestion.append(address_str + "-" + amount);
+ first = false;
+ }
+ filename_suggestion.append(".psbt");
+ QString filename = GUIUtil::getSaveFileName(this,
+ tr("Save Transaction Data"), filename_suggestion,
+ tr("Partially Signed Transaction (Binary) (*.psbt)"), &selected_filter);
+ if (filename.isEmpty()) {
+ return;
+ }
+ std::ofstream out(filename.toLocal8Bit().data());
+ out << ssTx.str();
+ out.close();
+ showStatus(tr("PSBT saved to disk."), StatusLevel::INFO);
+}
+
+void PSBTOperationsDialog::updateTransactionDisplay() {
+ m_ui->transactionDescription->setText(QString::fromStdString(renderTransaction(m_transaction_data)));
+ showTransactionStatus(m_transaction_data);
+}
+
+std::string PSBTOperationsDialog::renderTransaction(const PartiallySignedTransaction &psbtx)
+{
+ QString tx_description = "";
+ CAmount totalAmount = 0;
+ for (const CTxOut& out : psbtx.tx->vout) {
+ CTxDestination address;
+ ExtractDestination(out.scriptPubKey, address);
+ totalAmount += out.nValue;
+ tx_description.append(tr(" * Sends %1 to %2")
+ .arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, out.nValue))
+ .arg(QString::fromStdString(EncodeDestination(address))));
+ tx_description.append("<br>");
+ }
+
+ PSBTAnalysis analysis = AnalyzePSBT(psbtx);
+ tx_description.append(" * ");
+ if (!*analysis.fee) {
+ // This happens if the transaction is missing input UTXO information.
+ tx_description.append(tr("Unable to calculate transaction fee or total transaction amount."));
+ } else {
+ tx_description.append(tr("Pays transaction fee: "));
+ tx_description.append(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, *analysis.fee));
+
+ // add total amount in all subdivision units
+ tx_description.append("<hr />");
+ QStringList alternativeUnits;
+ for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits())
+ {
+ if(u != m_client_model->getOptionsModel()->getDisplayUnit()) {
+ alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
+ }
+ }
+ tx_description.append(QString("<b>%1</b>: <b>%2</b>").arg(tr("Total Amount"))
+ .arg(BitcoinUnits::formatHtmlWithUnit(m_client_model->getOptionsModel()->getDisplayUnit(), totalAmount)));
+ tx_description.append(QString("<br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>")
+ .arg(alternativeUnits.join(" " + tr("or") + " ")));
+ }
+
+ size_t num_unsigned = CountPSBTUnsignedInputs(psbtx);
+ if (num_unsigned > 0) {
+ tx_description.append("<br><br>");
+ tx_description.append(tr("Transaction has %1 unsigned inputs.").arg(QString::number(num_unsigned)));
+ }
+
+ return tx_description.toStdString();
+}
+
+void PSBTOperationsDialog::showStatus(const QString &msg, StatusLevel level) {
+ m_ui->statusBar->setText(msg);
+ switch (level) {
+ case StatusLevel::INFO: {
+ m_ui->statusBar->setStyleSheet("QLabel { background-color : lightgreen }");
+ break;
+ }
+ case StatusLevel::WARN: {
+ m_ui->statusBar->setStyleSheet("QLabel { background-color : orange }");
+ break;
+ }
+ case StatusLevel::ERR: {
+ m_ui->statusBar->setStyleSheet("QLabel { background-color : red }");
+ break;
+ }
+ }
+ m_ui->statusBar->show();
+}
+
+size_t PSBTOperationsDialog::couldSignInputs(const PartiallySignedTransaction &psbtx) {
+ size_t n_signed;
+ bool complete;
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, false /* bip32derivs */, m_transaction_data, complete, &n_signed);
+
+ if (err != TransactionError::OK) {
+ return 0;
+ }
+ return n_signed;
+}
+
+void PSBTOperationsDialog::showTransactionStatus(const PartiallySignedTransaction &psbtx) {
+ PSBTAnalysis analysis = AnalyzePSBT(psbtx);
+ size_t n_could_sign = couldSignInputs(psbtx);
+
+ switch (analysis.next) {
+ case PSBTRole::UPDATER: {
+ showStatus(tr("Transaction is missing some information about inputs."), StatusLevel::WARN);
+ break;
+ }
+ case PSBTRole::SIGNER: {
+ QString need_sig_text = tr("Transaction still needs signature(s).");
+ StatusLevel level = StatusLevel::INFO;
+ if (m_wallet_model->wallet().privateKeysDisabled()) {
+ need_sig_text += " " + tr("(But this wallet cannot sign transactions.)");
+ level = StatusLevel::WARN;
+ } else if (n_could_sign < 1) {
+ need_sig_text += " " + tr("(But this wallet does not have the right keys.)"); // XXX wording
+ level = StatusLevel::WARN;
+ }
+ showStatus(need_sig_text, level);
+ break;
+ }
+ case PSBTRole::FINALIZER:
+ case PSBTRole::EXTRACTOR: {
+ showStatus(tr("Transaction is fully signed and ready for broadcast."), StatusLevel::INFO);
+ break;
+ }
+ default: {
+ showStatus(tr("Transaction status is unknown."), StatusLevel::ERR);
+ break;
+ }
+ }
+}
diff --git a/src/qt/psbtoperationsdialog.h b/src/qt/psbtoperationsdialog.h
new file mode 100644
index 0000000000..f37bdbe39a
--- /dev/null
+++ b/src/qt/psbtoperationsdialog.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2011-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_QT_PSBTOPERATIONSDIALOG_H
+#define BITCOIN_QT_PSBTOPERATIONSDIALOG_H
+
+#include <QDialog>
+
+#include <psbt.h>
+#include <qt/clientmodel.h>
+#include <qt/walletmodel.h>
+
+namespace Ui {
+class PSBTOperationsDialog;
+}
+
+/** Dialog showing transaction details. */
+class PSBTOperationsDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit PSBTOperationsDialog(QWidget* parent, WalletModel* walletModel, ClientModel* clientModel);
+ ~PSBTOperationsDialog();
+
+ void openWithPSBT(PartiallySignedTransaction psbtx);
+
+public Q_SLOTS:
+ void signTransaction();
+ void broadcastTransaction();
+ void copyToClipboard();
+ void saveTransaction();
+
+private:
+ Ui::PSBTOperationsDialog* m_ui;
+ PartiallySignedTransaction m_transaction_data;
+ WalletModel* m_wallet_model;
+ ClientModel* m_client_model;
+
+ enum class StatusLevel {
+ INFO,
+ WARN,
+ ERR
+ };
+
+ size_t couldSignInputs(const PartiallySignedTransaction &psbtx);
+ void updateTransactionDisplay();
+ std::string renderTransaction(const PartiallySignedTransaction &psbtx);
+ void showStatus(const QString &msg, StatusLevel level);
+ void showTransactionStatus(const PartiallySignedTransaction &psbtx);
+};
+
+#endif // BITCOIN_QT_PSBTOPERATIONSDIALOG_H
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index 5debded4ea..d374d610ee 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -242,22 +242,6 @@ void ReceiveCoinsDialog::resizeEvent(QResizeEvent *event)
columnResizingFixer->stretchColumnWidth(RecentRequestsTableModel::Message);
}
-void ReceiveCoinsDialog::keyPressEvent(QKeyEvent *event)
-{
- if (event->key() == Qt::Key_Return)
- {
- // press return -> submit form
- if (ui->reqLabel->hasFocus() || ui->reqAmount->hasFocus() || ui->reqMessage->hasFocus())
- {
- event->ignore();
- on_receiveButton_clicked();
- return;
- }
- }
-
- this->QDialog::keyPressEvent(event);
-}
-
QModelIndex ReceiveCoinsDialog::selectedRow()
{
if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel())
diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h
index 2f48cd58f0..27455e2906 100644
--- a/src/qt/receivecoinsdialog.h
+++ b/src/qt/receivecoinsdialog.h
@@ -49,9 +49,6 @@ public Q_SLOTS:
void reject() override;
void accept() override;
-protected:
- virtual void keyPressEvent(QKeyEvent *event) override;
-
private:
Ui::ReceiveCoinsDialog *ui;
GUIUtil::TableViewLastColumnResizingFixer *columnResizingFixer;
diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp
index 7419297a96..3e20368a36 100644
--- a/src/qt/recentrequeststablemodel.cpp
+++ b/src/qt/recentrequeststablemodel.cpp
@@ -82,7 +82,7 @@ QVariant RecentRequestsTableModel::data(const QModelIndex &index, int role) cons
if (rec->recipient.amount == 0 && role == Qt::DisplayRole)
return tr("(no amount requested)");
else if (role == Qt::EditRole)
- return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), rec->recipient.amount, false, BitcoinUnits::separatorNever);
+ return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), rec->recipient.amount, false, BitcoinUnits::SeparatorStyle::NEVER);
else
return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), rec->recipient.amount);
}
diff --git a/src/qt/res/movies/makespinner.sh b/src/qt/res/animation/makespinner.sh
index 4fa8dadf86..4fa8dadf86 100755
--- a/src/qt/res/movies/makespinner.sh
+++ b/src/qt/res/animation/makespinner.sh
diff --git a/src/qt/res/movies/spinner-000.png b/src/qt/res/animation/spinner-000.png
index 0dc48d0d8c..0dc48d0d8c 100644
--- a/src/qt/res/movies/spinner-000.png
+++ b/src/qt/res/animation/spinner-000.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-001.png b/src/qt/res/animation/spinner-001.png
index d167f20541..d167f20541 100644
--- a/src/qt/res/movies/spinner-001.png
+++ b/src/qt/res/animation/spinner-001.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-002.png b/src/qt/res/animation/spinner-002.png
index 4a1f1f8e56..4a1f1f8e56 100644
--- a/src/qt/res/movies/spinner-002.png
+++ b/src/qt/res/animation/spinner-002.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-003.png b/src/qt/res/animation/spinner-003.png
index fb1c2cd4ad..fb1c2cd4ad 100644
--- a/src/qt/res/movies/spinner-003.png
+++ b/src/qt/res/animation/spinner-003.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-004.png b/src/qt/res/animation/spinner-004.png
index 4df2132344..4df2132344 100644
--- a/src/qt/res/movies/spinner-004.png
+++ b/src/qt/res/animation/spinner-004.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-005.png b/src/qt/res/animation/spinner-005.png
index 5d6f41e0dc..5d6f41e0dc 100644
--- a/src/qt/res/movies/spinner-005.png
+++ b/src/qt/res/animation/spinner-005.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-006.png b/src/qt/res/animation/spinner-006.png
index c1f7d18899..c1f7d18899 100644
--- a/src/qt/res/movies/spinner-006.png
+++ b/src/qt/res/animation/spinner-006.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-007.png b/src/qt/res/animation/spinner-007.png
index 1e794b2626..1e794b2626 100644
--- a/src/qt/res/movies/spinner-007.png
+++ b/src/qt/res/animation/spinner-007.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-008.png b/src/qt/res/animation/spinner-008.png
index df12ea8719..df12ea8719 100644
--- a/src/qt/res/movies/spinner-008.png
+++ b/src/qt/res/animation/spinner-008.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-009.png b/src/qt/res/animation/spinner-009.png
index 18fc3a7d16..18fc3a7d16 100644
--- a/src/qt/res/movies/spinner-009.png
+++ b/src/qt/res/animation/spinner-009.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-010.png b/src/qt/res/animation/spinner-010.png
index a79c845fe8..a79c845fe8 100644
--- a/src/qt/res/movies/spinner-010.png
+++ b/src/qt/res/animation/spinner-010.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-011.png b/src/qt/res/animation/spinner-011.png
index 57baf66895..57baf66895 100644
--- a/src/qt/res/movies/spinner-011.png
+++ b/src/qt/res/animation/spinner-011.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-012.png b/src/qt/res/animation/spinner-012.png
index 9deae7853a..9deae7853a 100644
--- a/src/qt/res/movies/spinner-012.png
+++ b/src/qt/res/animation/spinner-012.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-013.png b/src/qt/res/animation/spinner-013.png
index 0659d48dec..0659d48dec 100644
--- a/src/qt/res/movies/spinner-013.png
+++ b/src/qt/res/animation/spinner-013.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-014.png b/src/qt/res/animation/spinner-014.png
index bc1ef51bde..bc1ef51bde 100644
--- a/src/qt/res/movies/spinner-014.png
+++ b/src/qt/res/animation/spinner-014.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-015.png b/src/qt/res/animation/spinner-015.png
index 24b57b62c2..24b57b62c2 100644
--- a/src/qt/res/movies/spinner-015.png
+++ b/src/qt/res/animation/spinner-015.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-016.png b/src/qt/res/animation/spinner-016.png
index d622872651..d622872651 100644
--- a/src/qt/res/movies/spinner-016.png
+++ b/src/qt/res/animation/spinner-016.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-017.png b/src/qt/res/animation/spinner-017.png
index f48f688db2..f48f688db2 100644
--- a/src/qt/res/movies/spinner-017.png
+++ b/src/qt/res/animation/spinner-017.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-018.png b/src/qt/res/animation/spinner-018.png
index a2c8f38b1d..a2c8f38b1d 100644
--- a/src/qt/res/movies/spinner-018.png
+++ b/src/qt/res/animation/spinner-018.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-019.png b/src/qt/res/animation/spinner-019.png
index 9d7cc35d82..9d7cc35d82 100644
--- a/src/qt/res/movies/spinner-019.png
+++ b/src/qt/res/animation/spinner-019.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-020.png b/src/qt/res/animation/spinner-020.png
index 1a07acc454..1a07acc454 100644
--- a/src/qt/res/movies/spinner-020.png
+++ b/src/qt/res/animation/spinner-020.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-021.png b/src/qt/res/animation/spinner-021.png
index 9cea8f2543..9cea8f2543 100644
--- a/src/qt/res/movies/spinner-021.png
+++ b/src/qt/res/animation/spinner-021.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-022.png b/src/qt/res/animation/spinner-022.png
index 60250f6dea..60250f6dea 100644
--- a/src/qt/res/movies/spinner-022.png
+++ b/src/qt/res/animation/spinner-022.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-023.png b/src/qt/res/animation/spinner-023.png
index fc290a0cf2..fc290a0cf2 100644
--- a/src/qt/res/movies/spinner-023.png
+++ b/src/qt/res/animation/spinner-023.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-024.png b/src/qt/res/animation/spinner-024.png
index c5dcf1eae9..c5dcf1eae9 100644
--- a/src/qt/res/movies/spinner-024.png
+++ b/src/qt/res/animation/spinner-024.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-025.png b/src/qt/res/animation/spinner-025.png
index 7f3577a4de..7f3577a4de 100644
--- a/src/qt/res/movies/spinner-025.png
+++ b/src/qt/res/animation/spinner-025.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-026.png b/src/qt/res/animation/spinner-026.png
index 1663ddf44c..1663ddf44c 100644
--- a/src/qt/res/movies/spinner-026.png
+++ b/src/qt/res/animation/spinner-026.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-027.png b/src/qt/res/animation/spinner-027.png
index d0e6da4503..d0e6da4503 100644
--- a/src/qt/res/movies/spinner-027.png
+++ b/src/qt/res/animation/spinner-027.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-028.png b/src/qt/res/animation/spinner-028.png
index 2a7aba50e2..2a7aba50e2 100644
--- a/src/qt/res/movies/spinner-028.png
+++ b/src/qt/res/animation/spinner-028.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-029.png b/src/qt/res/animation/spinner-029.png
index c8ca15c1e1..c8ca15c1e1 100644
--- a/src/qt/res/movies/spinner-029.png
+++ b/src/qt/res/animation/spinner-029.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-030.png b/src/qt/res/animation/spinner-030.png
index c847c99a93..c847c99a93 100644
--- a/src/qt/res/movies/spinner-030.png
+++ b/src/qt/res/animation/spinner-030.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-031.png b/src/qt/res/animation/spinner-031.png
index 403443144e..403443144e 100644
--- a/src/qt/res/movies/spinner-031.png
+++ b/src/qt/res/animation/spinner-031.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-032.png b/src/qt/res/animation/spinner-032.png
index f9db080567..f9db080567 100644
--- a/src/qt/res/movies/spinner-032.png
+++ b/src/qt/res/animation/spinner-032.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-033.png b/src/qt/res/animation/spinner-033.png
index 43f57719e7..43f57719e7 100644
--- a/src/qt/res/movies/spinner-033.png
+++ b/src/qt/res/animation/spinner-033.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-034.png b/src/qt/res/animation/spinner-034.png
index c26656ff17..c26656ff17 100644
--- a/src/qt/res/movies/spinner-034.png
+++ b/src/qt/res/animation/spinner-034.png
Binary files differ
diff --git a/src/qt/res/movies/spinner-035.png b/src/qt/res/animation/spinner-035.png
index e471f950a3..e471f950a3 100644
--- a/src/qt/res/movies/spinner-035.png
+++ b/src/qt/res/animation/spinner-035.png
Binary files differ
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 0f89d4e6fe..a14fae6460 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -466,6 +466,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
// Install event filter for up and down arrow
ui->lineEdit->installEventFilter(this);
+ ui->lineEdit->setMaxLength(16 * 1024 * 1024);
ui->messagesWidget->installEventFilter(this);
connect(ui->clearButton, &QPushButton::clicked, this, &RPCConsole::clear);
@@ -555,7 +556,7 @@ bool RPCConsole::eventFilter(QObject* obj, QEvent *event)
return QWidget::eventFilter(obj, event);
}
-void RPCConsole::setClientModel(ClientModel *model)
+void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_t bestblock_date, double verification_progress)
{
clientModel = model;
@@ -575,13 +576,13 @@ void RPCConsole::setClientModel(ClientModel *model)
setNumConnections(model->getNumConnections());
connect(model, &ClientModel::numConnectionsChanged, this, &RPCConsole::setNumConnections);
- interfaces::Node& node = clientModel->node();
- setNumBlocks(node.getNumBlocks(), QDateTime::fromTime_t(node.getLastBlockTime()), node.getVerificationProgress(), false);
+ setNumBlocks(bestblock_height, QDateTime::fromTime_t(bestblock_date), verification_progress, false);
connect(model, &ClientModel::numBlocksChanged, this, &RPCConsole::setNumBlocks);
updateNetworkState();
connect(model, &ClientModel::networkActiveChanged, this, &RPCConsole::setNetworkActive);
+ interfaces::Node& node = clientModel->node();
updateTrafficStats(node.getTotalBytesRecv(), node.getTotalBytesSent());
connect(model, &ClientModel::bytesChanged, this, &RPCConsole::updateTrafficStats);
@@ -1118,15 +1119,20 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats)
ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer));
ui->peerDirection->setText(stats->nodeStats.fInbound ? tr("Inbound") : tr("Outbound"));
ui->peerHeight->setText(QString::number(stats->nodeStats.nStartingHeight));
- ui->peerWhitelisted->setText(stats->nodeStats.m_legacyWhitelisted ? tr("Yes") : tr("No"));
+ if (stats->nodeStats.m_permissionFlags == PF_NONE) {
+ ui->peerPermissions->setText(tr("N/A"));
+ } else {
+ QStringList permissions;
+ for (const auto& permission : NetPermissions::ToStrings(stats->nodeStats.m_permissionFlags)) {
+ permissions.append(QString::fromStdString(permission));
+ }
+ ui->peerPermissions->setText(permissions.join(" & "));
+ }
ui->peerMappedAS->setText(stats->nodeStats.m_mapped_as != 0 ? QString::number(stats->nodeStats.m_mapped_as) : tr("N/A"));
// This check fails for example if the lock was busy and
// nodeStateStats couldn't be fetched.
if (stats->fNodeStateStatsAvailable) {
- // Ban score is init to 0
- ui->peerBanScore->setText(QString("%1").arg(stats->nodeStateStats.nMisbehavior));
-
// Sync height is init to -1
if (stats->nodeStateStats.nSyncHeight > -1)
ui->peerSyncHeight->setText(QString("%1").arg(stats->nodeStateStats.nSyncHeight));
@@ -1217,7 +1223,7 @@ void RPCConsole::banSelectedNode(int bantime)
// Find possible nodes, ban it and clear the selected node
const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow);
if (stats) {
- m_node.ban(stats->nodeStats.addr, BanReasonManuallyAdded, bantime);
+ m_node.ban(stats->nodeStats.addr, bantime);
m_node.disconnectByAddress(stats->nodeStats.addr);
}
}
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index de8e37cca2..280c5bd71a 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -46,7 +46,7 @@ public:
return RPCParseCommandLine(&node, strResult, strCommand, true, pstrFilteredOut, wallet_model);
}
- void setClientModel(ClientModel *model);
+ void setClientModel(ClientModel *model = nullptr, int bestblock_height = 0, int64_t bestblock_date = 0, double verification_progress = 0.0);
void addWallet(WalletModel * const walletModel);
void removeWallet(WalletModel* const walletModel);
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 9e23fe78d8..97fb88d71c 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -21,9 +21,9 @@
#include <chainparams.h>
#include <interfaces/node.h>
#include <key_io.h>
+#include <node/ui_interface.h>
#include <policy/fees.h>
#include <txmempool.h>
-#include <ui_interface.h>
#include <wallet/coincontrol.h>
#include <wallet/fees.h>
#include <wallet/wallet.h>
@@ -392,7 +392,7 @@ void SendCoinsDialog::on_sendButton_clicked()
CMutableTransaction mtx = CMutableTransaction{*(m_current_transaction->getWtx())};
PartiallySignedTransaction psbtx(mtx);
bool complete = false;
- const TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete);
+ const TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
assert(!complete);
assert(err == TransactionError::OK);
// Serialize the PSBT
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index ced6a299d5..bd63d6e7fb 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -14,7 +14,6 @@
#include <interfaces/wallet.h>
#include <qt/guiutil.h>
#include <qt/networkstyle.h>
-#include <ui_interface.h>
#include <util/system.h>
#include <util/translation.h>
@@ -25,8 +24,8 @@
#include <QScreen>
-SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle) :
- QWidget(nullptr, f), curAlignment(0), m_node(node)
+SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle) :
+ QWidget(nullptr, f), curAlignment(0)
{
// set reference point, paddings
int paddingRight = 50;
@@ -125,7 +124,6 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
setFixedSize(r.size());
move(QGuiApplication::primaryScreen()->geometry().center() - r.center());
- subscribeToCoreSignals();
installEventFilter(this);
GUIUtil::handleCloseWindowShortcut(this);
@@ -133,14 +131,28 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
SplashScreen::~SplashScreen()
{
- unsubscribeFromCoreSignals();
+ if (m_node) unsubscribeFromCoreSignals();
+}
+
+void SplashScreen::setNode(interfaces::Node& node)
+{
+ assert(!m_node);
+ m_node = &node;
+ subscribeToCoreSignals();
+ if (m_shutdown) m_node->startShutdown();
+}
+
+void SplashScreen::shutdown()
+{
+ m_shutdown = true;
+ if (m_node) m_node->startShutdown();
}
bool SplashScreen::eventFilter(QObject * obj, QEvent * ev) {
if (ev->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
if (keyEvent->key() == Qt::Key_Q) {
- m_node.startShutdown();
+ shutdown();
}
}
return QObject::eventFilter(obj, ev);
@@ -184,10 +196,10 @@ void SplashScreen::ConnectWallet(std::unique_ptr<interfaces::Wallet> wallet)
void SplashScreen::subscribeToCoreSignals()
{
// Connect signals to client
- m_handler_init_message = m_node.handleInitMessage(std::bind(InitMessage, this, std::placeholders::_1));
- m_handler_show_progress = m_node.handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
+ m_handler_init_message = m_node->handleInitMessage(std::bind(InitMessage, this, std::placeholders::_1));
+ m_handler_show_progress = m_node->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
#ifdef ENABLE_WALLET
- m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) { ConnectWallet(std::move(wallet)); });
+ m_handler_load_wallet = m_node->handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) { ConnectWallet(std::move(wallet)); });
#endif
}
@@ -222,6 +234,6 @@ void SplashScreen::paintEvent(QPaintEvent *event)
void SplashScreen::closeEvent(QCloseEvent *event)
{
- m_node.startShutdown(); // allows an "emergency" shutdown during startup
+ shutdown(); // allows an "emergency" shutdown during startup
event->ignore();
}
diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h
index 3158524117..2213b02c55 100644
--- a/src/qt/splashscreen.h
+++ b/src/qt/splashscreen.h
@@ -28,8 +28,9 @@ class SplashScreen : public QWidget
Q_OBJECT
public:
- explicit SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle);
+ explicit SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle);
~SplashScreen();
+ void setNode(interfaces::Node& node);
protected:
void paintEvent(QPaintEvent *event) override;
@@ -50,6 +51,8 @@ private:
void subscribeToCoreSignals();
/** Disconnect core signals to splash screen */
void unsubscribeFromCoreSignals();
+ /** Initiate shutdown */
+ void shutdown();
/** Connect wallet signals to splash screen */
void ConnectWallet(std::unique_ptr<interfaces::Wallet> wallet);
@@ -58,7 +61,8 @@ private:
QColor curColor;
int curAlignment;
- interfaces::Node& m_node;
+ interfaces::Node* m_node = nullptr;
+ bool m_shutdown = false;
std::unique_ptr<interfaces::Handler> m_handler_init_message;
std::unique_ptr<interfaces::Handler> m_handler_show_progress;
std::unique_ptr<interfaces::Handler> m_handler_load_wallet;
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index 476128520c..84f981dff3 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -18,6 +18,7 @@
#include <key.h>
#include <key_io.h>
#include <wallet/wallet.h>
+#include <walletinitinterface.h>
#include <QApplication>
#include <QTimer>
@@ -59,7 +60,8 @@ void EditAddressAndSubmit(
void TestAddAddressesToSendBook(interfaces::Node& node)
{
TestChain100Setup test;
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), WalletLocation(), WalletDatabase::CreateMock());
+ node.setContext(&test.m_node);
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), WalletLocation(), CreateMockWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan();
bool firstRun;
wallet->LoadWallet(firstRun);
@@ -106,7 +108,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
// Initialize relevant QT models.
std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
- OptionsModel optionsModel(node);
+ OptionsModel optionsModel;
ClientModel clientModel(node, &optionsModel);
AddWallet(wallet);
WalletModel walletModel(interfaces::MakeWallet(wallet), clientModel, platformStyle.get());
diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp
index f88d57c716..0b5c341548 100644
--- a/src/qt/test/apptests.cpp
+++ b/src/qt/test/apptests.cpp
@@ -62,10 +62,12 @@ void AppTests::appTests()
}
#endif
- BasicTestingSetup test{CBaseChainParams::REGTEST}; // Create a temp data directory to backup the gui settings to
- ECC_Stop(); // Already started by the common test setup, so stop it to avoid interference
- LogInstance().DisconnectTestLogger();
+ fs::create_directories([] {
+ BasicTestingSetup test{CBaseChainParams::REGTEST}; // Create a temp data directory to backup the gui settings to
+ return GetDataDir() / "blocks";
+ }());
+ qRegisterMetaType<interfaces::BlockAndHeaderTipInfo>("interfaces::BlockAndHeaderTipInfo");
m_app.parameterSetup();
m_app.createOptionsModel(true /* reset settings */);
QScopedPointer<const NetworkStyle> style(NetworkStyle::instantiate(Params().NetworkIDString()));
@@ -80,8 +82,9 @@ void AppTests::appTests()
m_app.exec();
// Reset global state to avoid interfering with later tests.
+ LogInstance().DisconnectTestLogger();
AbortShutdown();
- UnloadBlockIndex();
+ UnloadBlockIndex(/* mempool */ nullptr);
WITH_LOCK(::cs_main, g_chainman.Reset());
}
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index aefdcd2716..86356b43c8 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -40,7 +40,7 @@ Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin);
const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
// This is all you need to run all the tests
-int main(int argc, char *argv[])
+int main(int argc, char* argv[])
{
// Initialize persistent globals with the testing setup state for sanity.
// E.g. -datadir in gArgs is set to a temp directory dummy value (instead
@@ -52,7 +52,8 @@ int main(int argc, char *argv[])
BasicTestingSetup dummy{CBaseChainParams::REGTEST};
}
- std::unique_ptr<interfaces::Node> node = interfaces::MakeNode();
+ NodeContext node_context;
+ std::unique_ptr<interfaces::Node> node = interfaces::MakeNode(&node_context);
bool fInvalid = false;
@@ -67,9 +68,11 @@ int main(int argc, char *argv[])
// Don't remove this, it's needed to access
// QApplication:: and QCoreApplication:: in the tests
- BitcoinApplication app(*node);
+ BitcoinApplication app;
+ app.setNode(*node);
app.setApplicationName("Bitcoin-Qt-test");
+ app.node().context()->args = &gArgs; // Make gArgs available in the NodeContext
AppTests app_tests(app);
if (QTest::qExec(&app_tests) != 0) {
fInvalid = true;
@@ -78,7 +81,7 @@ int main(int argc, char *argv[])
if (QTest::qExec(&test1) != 0) {
fInvalid = true;
}
- RPCNestedTests test3(*node);
+ RPCNestedTests test3(app.node());
if (QTest::qExec(&test3) != 0) {
fInvalid = true;
}
@@ -87,11 +90,11 @@ int main(int argc, char *argv[])
fInvalid = true;
}
#ifdef ENABLE_WALLET
- WalletTests test5(*node);
+ WalletTests test5(app.node());
if (QTest::qExec(&test5) != 0) {
fInvalid = true;
}
- AddressBookTests test6(*node);
+ AddressBookTests test6(app.node());
if (QTest::qExec(&test6) != 0) {
fInvalid = true;
}
diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp
index 8da0250e57..adcfe0d25c 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -138,9 +138,8 @@ void TestGUI(interfaces::Node& node)
for (int i = 0; i < 5; ++i) {
test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
}
- node.context()->connman = std::move(test.m_node.connman);
- node.context()->mempool = std::move(test.m_node.mempool);
- std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), WalletLocation(), WalletDatabase::CreateMock());
+ node.setContext(&test.m_node);
+ std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), WalletLocation(), CreateMockWalletDatabase());
bool firstRun;
wallet->LoadWallet(firstRun);
{
@@ -164,7 +163,7 @@ void TestGUI(interfaces::Node& node)
std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
SendCoinsDialog sendCoinsDialog(platformStyle.get());
TransactionView transactionView(platformStyle.get());
- OptionsModel optionsModel(node);
+ OptionsModel optionsModel;
ClientModel clientModel(node, &optionsModel);
AddWallet(wallet);
WalletModel walletModel(interfaces::MakeWallet(wallet), clientModel, platformStyle.get());
@@ -178,7 +177,7 @@ void TestGUI(interfaces::Node& node)
QString balanceText = balanceLabel->text();
int unit = walletModel.getOptionsModel()->getDisplayUnit();
CAmount balance = walletModel.wallet().getBalance();
- QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways);
+ QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::SeparatorStyle::ALWAYS);
QCOMPARE(balanceText, balanceComparison);
}
@@ -204,7 +203,7 @@ void TestGUI(interfaces::Node& node)
QString balanceText = balanceLabel->text().trimmed();
int unit = walletModel.getOptionsModel()->getDisplayUnit();
CAmount balance = walletModel.wallet().getBalance();
- QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways);
+ QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::SeparatorStyle::ALWAYS);
QCOMPARE(balanceText, balanceComparison);
// Check Request Payment button
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index 01dff8069c..632a18de5c 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -47,7 +47,6 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface
if(mine)
{
TransactionRecord sub(hash, nTime);
- CTxDestination address;
sub.idx = i; // vout index
sub.credit = txout.nValue;
sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY;
@@ -235,6 +234,7 @@ void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, cons
bool TransactionRecord::statusUpdateNeeded(const uint256& block_hash) const
{
+ assert(!block_hash.IsNull());
return status.m_cur_block_hash != block_hash || status.needsUpdate;
}
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 04eb1ae706..c560dc58e7 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -178,21 +178,16 @@ public:
TransactionRecord* index(interfaces::Wallet& wallet, const uint256& cur_block_hash, const int idx)
{
- if(idx >= 0 && idx < cachedWallet.size())
- {
+ if (idx >= 0 && idx < cachedWallet.size()) {
TransactionRecord *rec = &cachedWallet[idx];
- // Get required locks upfront. This avoids the GUI from getting
- // stuck if the core is holding the locks for a longer time - for
- // example, during a wallet rescan.
- //
// If a status update is needed (blocks came in since last check),
- // update the status of this transaction from the wallet. Otherwise,
- // simply re-use the cached status.
+ // try to update the status of this transaction from the wallet.
+ // Otherwise, simply re-use the cached status.
interfaces::WalletTxStatus wtx;
int numBlocks;
int64_t block_time;
- if (rec->statusUpdateNeeded(cur_block_hash) && wallet.tryGetTxStatus(rec->hash, wtx, numBlocks, block_time)) {
+ if (!cur_block_hash.IsNull() && rec->statusUpdateNeeded(cur_block_hash) && wallet.tryGetTxStatus(rec->hash, wtx, numBlocks, block_time)) {
rec->updateStatus(wtx, cur_block_hash, numBlocks, block_time);
}
return rec;
@@ -524,7 +519,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
case ToAddress:
return formatTxToAddress(rec, false);
case Amount:
- return formatTxAmount(rec, true, BitcoinUnits::separatorAlways);
+ return formatTxAmount(rec, true, BitcoinUnits::SeparatorStyle::ALWAYS);
}
break;
case Qt::EditRole:
@@ -614,14 +609,14 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
details.append(QString::fromStdString(rec->address));
details.append(" ");
}
- details.append(formatTxAmount(rec, false, BitcoinUnits::separatorNever));
+ details.append(formatTxAmount(rec, false, BitcoinUnits::SeparatorStyle::NEVER));
return details;
}
case ConfirmedRole:
return rec->status.status == TransactionStatus::Status::Confirming || rec->status.status == TransactionStatus::Status::Confirmed;
case FormattedAmountRole:
// Used for copy/export, so don't include separators
- return formatTxAmount(rec, false, BitcoinUnits::separatorNever);
+ return formatTxAmount(rec, false, BitcoinUnits::SeparatorStyle::NEVER);
case StatusRole:
return rec->status.status;
}
@@ -664,7 +659,7 @@ QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientat
QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(parent);
- TransactionRecord* data = priv->index(walletModel->wallet(), walletModel->clientModel().getBestBlockHash(), row);
+ TransactionRecord* data = priv->index(walletModel->wallet(), walletModel->getLastBlockProcessed(), row);
if(data)
{
return createIndex(row, column, data);
diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h
index f06f0ea15f..4b699d4d7d 100644
--- a/src/qt/transactiontablemodel.h
+++ b/src/qt/transactiontablemodel.h
@@ -101,7 +101,7 @@ private:
QString formatTxDate(const TransactionRecord *wtx) const;
QString formatTxType(const TransactionRecord *wtx) const;
QString formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const;
- QString formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true, BitcoinUnits::SeparatorStyle separators=BitcoinUnits::separatorStandard) const;
+ QString formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true, BitcoinUnits::SeparatorStyle separators=BitcoinUnits::SeparatorStyle::STANDARD) const;
QString formatTooltip(const TransactionRecord *rec) const;
QVariant txStatusDecoration(const TransactionRecord *wtx) const;
QVariant txWatchonlyDecoration(const TransactionRecord *wtx) const;
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 3df81807f0..54ecfc38ec 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -17,7 +17,7 @@
#include <qt/transactiontablemodel.h>
#include <qt/walletmodel.h>
-#include <ui_interface.h>
+#include <node/ui_interface.h>
#include <QApplication>
#include <QComboBox>
diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp
index 01922cf996..b7f85446f4 100644
--- a/src/qt/utilitydialog.cpp
+++ b/src/qt/utilitydialog.cpp
@@ -28,7 +28,7 @@
#include <QVBoxLayout>
/** "Help message" or "About" dialog box */
-HelpMessageDialog::HelpMessageDialog(interfaces::Node& node, QWidget *parent, bool about) :
+HelpMessageDialog::HelpMessageDialog(QWidget *parent, bool about) :
QDialog(parent),
ui(new Ui::HelpMessageDialog)
{
diff --git a/src/qt/utilitydialog.h b/src/qt/utilitydialog.h
index 425b468f40..d2a5d5f67f 100644
--- a/src/qt/utilitydialog.h
+++ b/src/qt/utilitydialog.h
@@ -12,10 +12,6 @@ QT_BEGIN_NAMESPACE
class QMainWindow;
QT_END_NAMESPACE
-namespace interfaces {
- class Node;
-}
-
namespace Ui {
class HelpMessageDialog;
}
@@ -26,7 +22,7 @@ class HelpMessageDialog : public QDialog
Q_OBJECT
public:
- explicit HelpMessageDialog(interfaces::Node& node, QWidget *parent, bool about);
+ explicit HelpMessageDialog(QWidget *parent, bool about);
~HelpMessageDialog();
void printToConsole();
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index 20f2ef5b5f..3aed98e0e8 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -92,6 +92,23 @@ void WalletController::closeWallet(WalletModel* wallet_model, QWidget* parent)
removeAndDeleteWallet(wallet_model);
}
+void WalletController::closeAllWallets(QWidget* parent)
+{
+ QMessageBox::StandardButton button = QMessageBox::question(parent, tr("Close all wallets"),
+ tr("Are you sure you wish to close all wallets?"),
+ QMessageBox::Yes|QMessageBox::Cancel,
+ QMessageBox::Yes);
+ if (button != QMessageBox::Yes) return;
+
+ QMutexLocker locker(&m_mutex);
+ for (WalletModel* wallet_model : m_wallets) {
+ wallet_model->wallet().remove();
+ Q_EMIT walletRemoved(wallet_model);
+ delete wallet_model;
+ }
+ m_wallets.clear();
+}
+
WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wallet> wallet)
{
QMutexLocker locker(&m_mutex);
@@ -245,7 +262,7 @@ void CreateWalletActivity::finish()
{
destroyProgressDialog();
- if (!m_error_message.original.empty()) {
+ if (!m_error_message.empty()) {
QMessageBox::critical(m_parent_widget, tr("Create wallet failed"), QString::fromStdString(m_error_message.translated));
} else if (!m_warning_message.empty()) {
QMessageBox::warning(m_parent_widget, tr("Create wallet warning"), QString::fromStdString(Join(m_warning_message, Untranslated("\n")).translated));
@@ -286,7 +303,7 @@ void OpenWalletActivity::finish()
{
destroyProgressDialog();
- if (!m_error_message.original.empty()) {
+ if (!m_error_message.empty()) {
QMessageBox::critical(m_parent_widget, tr("Open wallet failed"), QString::fromStdString(m_error_message.translated));
} else if (!m_warning_message.empty()) {
QMessageBox::warning(m_parent_widget, tr("Open wallet warning"), QString::fromStdString(Join(m_warning_message, Untranslated("\n")).translated));
diff --git a/src/qt/walletcontroller.h b/src/qt/walletcontroller.h
index 24dd83adf7..f7e366878d 100644
--- a/src/qt/walletcontroller.h
+++ b/src/qt/walletcontroller.h
@@ -62,6 +62,7 @@ public:
std::map<std::string, bool> listWalletDir() const;
void closeWallet(WalletModel* wallet_model, QWidget* parent = nullptr);
+ void closeAllWallets(QWidget* parent = nullptr);
Q_SIGNALS:
void walletAdded(WalletModel* wallet_model);
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index 5e68ee4f93..ec56f2755f 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -165,11 +165,11 @@ void WalletFrame::gotoVerifyMessageTab(QString addr)
walletView->gotoVerifyMessageTab(addr);
}
-void WalletFrame::gotoLoadPSBT()
+void WalletFrame::gotoLoadPSBT(bool from_clipboard)
{
WalletView *walletView = currentWalletView();
if (walletView) {
- walletView->gotoLoadPSBT();
+ walletView->gotoLoadPSBT(from_clipboard);
}
}
diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h
index d90ade5005..2b5f263468 100644
--- a/src/qt/walletframe.h
+++ b/src/qt/walletframe.h
@@ -79,7 +79,7 @@ public Q_SLOTS:
void gotoVerifyMessageTab(QString addr = "");
/** Load Partially Signed Bitcoin Transaction */
- void gotoLoadPSBT();
+ void gotoLoadPSBT(bool from_clipboard = false);
/** Encrypt the wallet */
void encryptWallet(bool status);
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 671b5e1ce6..e374dd191c 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -21,8 +21,8 @@
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <key_io.h>
+#include <node/ui_interface.h>
#include <psbt.h>
-#include <ui_interface.h>
#include <util/system.h> // for GetBoolArg
#include <util/translation.h>
#include <wallet/coincontrol.h>
@@ -87,7 +87,7 @@ void WalletModel::pollBalanceChanged()
{
// Avoid recomputing wallet balances unless a TransactionChanged or
// BlockTip notification was received.
- if (!fForceCheckBalanceChanged && m_cached_last_update_tip == m_client_model->getBestBlockHash()) return;
+ if (!fForceCheckBalanceChanged && m_cached_last_update_tip == getLastBlockProcessed()) return;
// Try to get balances and return early if locks can't be acquired. This
// avoids the GUI from getting stuck on periodical polls if the core is
@@ -536,7 +536,7 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
if (create_psbt) {
PartiallySignedTransaction psbtx(mtx);
bool complete = false;
- const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete);
+ const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
if (err != TransactionError::OK || complete) {
QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Can't draft transaction."));
return false;
@@ -588,3 +588,8 @@ void WalletModel::refresh(bool pk_hash_only)
{
addressTableModel = new AddressTableModel(this, pk_hash_only);
}
+
+uint256 WalletModel::getLastBlockProcessed() const
+{
+ return m_client_model ? m_client_model->getBestBlockHash() : uint256{};
+}
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 38e8a14556..fd52db2da3 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -155,6 +155,9 @@ public:
AddressTableModel* getAddressTableModel() const { return addressTableModel; }
void refresh(bool pk_hash_only = false);
+
+ uint256 getLastBlockProcessed() const;
+
private:
std::unique_ptr<interfaces::Wallet> m_wallet;
std::unique_ptr<interfaces::Handler> m_handler_unload;
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 861d1c5f4a..2fc883a5f5 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -4,13 +4,11 @@
#include <qt/walletview.h>
-#include <node/psbt.h>
-#include <node/transaction.h>
-#include <policy/policy.h>
#include <qt/addressbookpage.h>
#include <qt/askpassphrasedialog.h>
#include <qt/clientmodel.h>
#include <qt/guiutil.h>
+#include <qt/psbtoperationsdialog.h>
#include <qt/optionsmodel.h>
#include <qt/overviewpage.h>
#include <qt/platformstyle.h>
@@ -22,11 +20,14 @@
#include <qt/walletmodel.h>
#include <interfaces/node.h>
-#include <ui_interface.h>
+#include <node/ui_interface.h>
+#include <psbt.h>
#include <util/strencodings.h>
#include <QAction>
#include <QActionGroup>
+#include <QApplication>
+#include <QClipboard>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QProgressDialog>
@@ -204,78 +205,42 @@ void WalletView::gotoVerifyMessageTab(QString addr)
signVerifyMessageDialog->setAddress_VM(addr);
}
-void WalletView::gotoLoadPSBT()
+void WalletView::gotoLoadPSBT(bool from_clipboard)
{
- QString filename = GUIUtil::getOpenFileName(this,
- tr("Load Transaction Data"), QString(),
- tr("Partially Signed Transaction (*.psbt)"), nullptr);
- if (filename.isEmpty()) return;
- if (GetFileSize(filename.toLocal8Bit().data(), MAX_FILE_SIZE_PSBT) == MAX_FILE_SIZE_PSBT) {
- Q_EMIT message(tr("Error"), tr("PSBT file must be smaller than 100 MiB"), CClientUIInterface::MSG_ERROR);
- return;
+ std::string data;
+
+ if (from_clipboard) {
+ std::string raw = QApplication::clipboard()->text().toStdString();
+ bool invalid;
+ data = DecodeBase64(raw, &invalid);
+ if (invalid) {
+ Q_EMIT message(tr("Error"), tr("Unable to decode PSBT from clipboard (invalid base64)"), CClientUIInterface::MSG_ERROR);
+ return;
+ }
+ } else {
+ QString filename = GUIUtil::getOpenFileName(this,
+ tr("Load Transaction Data"), QString(),
+ tr("Partially Signed Transaction (*.psbt)"), nullptr);
+ if (filename.isEmpty()) return;
+ if (GetFileSize(filename.toLocal8Bit().data(), MAX_FILE_SIZE_PSBT) == MAX_FILE_SIZE_PSBT) {
+ Q_EMIT message(tr("Error"), tr("PSBT file must be smaller than 100 MiB"), CClientUIInterface::MSG_ERROR);
+ return;
+ }
+ std::ifstream in(filename.toLocal8Bit().data(), std::ios::binary);
+ data = std::string(std::istreambuf_iterator<char>{in}, {});
}
- std::ifstream in(filename.toLocal8Bit().data(), std::ios::binary);
- std::string data(std::istreambuf_iterator<char>{in}, {});
std::string error;
PartiallySignedTransaction psbtx;
if (!DecodeRawPSBT(psbtx, data, error)) {
- Q_EMIT message(tr("Error"), tr("Unable to decode PSBT file") + "\n" + QString::fromStdString(error), CClientUIInterface::MSG_ERROR);
+ Q_EMIT message(tr("Error"), tr("Unable to decode PSBT") + "\n" + QString::fromStdString(error), CClientUIInterface::MSG_ERROR);
return;
}
- CMutableTransaction mtx;
- bool complete = false;
- PSBTAnalysis analysis = AnalyzePSBT(psbtx);
- QMessageBox msgBox;
- msgBox.setText("PSBT");
- switch (analysis.next) {
- case PSBTRole::CREATOR:
- case PSBTRole::UPDATER:
- msgBox.setInformativeText("PSBT is incomplete. Copy to clipboard for manual inspection?");
- break;
- case PSBTRole::SIGNER:
- msgBox.setInformativeText("Transaction needs more signatures. Copy to clipboard?");
- break;
- case PSBTRole::FINALIZER:
- case PSBTRole::EXTRACTOR:
- complete = FinalizeAndExtractPSBT(psbtx, mtx);
- if (complete) {
- msgBox.setInformativeText(tr("Would you like to send this transaction?"));
- } else {
- // The analyzer missed something, e.g. if there are final_scriptSig/final_scriptWitness
- // but with invalid signatures.
- msgBox.setInformativeText(tr("There was an unexpected problem processing the PSBT. Copy to clipboard for manual inspection?"));
- }
- }
-
- msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
- switch (msgBox.exec()) {
- case QMessageBox::Yes: {
- if (complete) {
- std::string err_string;
- CTransactionRef tx = MakeTransactionRef(mtx);
-
- TransactionError result = BroadcastTransaction(*clientModel->node().context(), tx, err_string, DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK(), /* relay */ true, /* wait_callback */ false);
- if (result == TransactionError::OK) {
- Q_EMIT message(tr("Success"), tr("Broadcasted transaction successfully."), CClientUIInterface::MSG_INFORMATION | CClientUIInterface::MODAL);
- } else {
- Q_EMIT message(tr("Error"), QString::fromStdString(err_string), CClientUIInterface::MSG_ERROR);
- }
- } else {
- // Serialize the PSBT
- CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
- ssTx << psbtx;
- GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
- Q_EMIT message(tr("PSBT copied"), "Copied to clipboard", CClientUIInterface::MSG_INFORMATION);
- return;
- }
- }
- case QMessageBox::Cancel:
- break;
- default:
- assert(false);
- }
+ PSBTOperationsDialog* dlg = new PSBTOperationsDialog(this, walletModel, clientModel);
+ dlg->openWithPSBT(psbtx);
+ dlg->setAttribute(Qt::WA_DeleteOnClose);
+ dlg->exec();
}
bool WalletView::handlePaymentRequest(const SendCoinsRecipient& recipient)
diff --git a/src/qt/walletview.h b/src/qt/walletview.h
index fd09456baa..f186554758 100644
--- a/src/qt/walletview.h
+++ b/src/qt/walletview.h
@@ -84,7 +84,7 @@ public Q_SLOTS:
/** Show Sign/Verify Message dialog and switch to verify message tab */
void gotoVerifyMessageTab(QString addr = "");
/** Load Partially Signed Bitcoin Transaction */
- void gotoLoadPSBT();
+ void gotoLoadPSBT(bool from_clipboard = false);
/** Show incoming transaction notification for new transactions.