diff options
Diffstat (limited to 'src/qt')
-rw-r--r-- | src/qt/bitcoin.cpp | 47 | ||||
-rw-r--r-- | src/qt/callback.h | 30 | ||||
-rw-r--r-- | src/qt/coincontroldialog.cpp | 20 | ||||
-rw-r--r-- | src/qt/forms/debugwindow.ui | 2 | ||||
-rw-r--r-- | src/qt/forms/optionsdialog.ui | 102 | ||||
-rw-r--r-- | src/qt/forms/sendcoinsdialog.ui | 38 | ||||
-rw-r--r-- | src/qt/guiutil.cpp | 69 | ||||
-rw-r--r-- | src/qt/guiutil.h | 10 | ||||
-rw-r--r-- | src/qt/intro.cpp | 5 | ||||
-rw-r--r-- | src/qt/optionsdialog.cpp | 12 | ||||
-rw-r--r-- | src/qt/optionsdialog.h | 1 | ||||
-rw-r--r-- | src/qt/peertablemodel.cpp | 2 | ||||
-rw-r--r-- | src/qt/rpcconsole.cpp | 11 | ||||
-rw-r--r-- | src/qt/sendcoinsdialog.cpp | 17 | ||||
-rw-r--r-- | src/qt/test/paymentservertests.cpp | 2 | ||||
-rw-r--r-- | src/qt/test/rpcnestedtests.cpp | 9 | ||||
-rw-r--r-- | src/qt/test/test_main.cpp | 48 | ||||
-rw-r--r-- | src/qt/test/wallettests.cpp | 118 | ||||
-rw-r--r-- | src/qt/test/wallettests.h | 15 | ||||
-rw-r--r-- | src/qt/transactiondesc.cpp | 2 | ||||
-rw-r--r-- | src/qt/transactionrecord.cpp | 10 | ||||
-rw-r--r-- | src/qt/walletmodel.cpp | 11 | ||||
-rw-r--r-- | src/qt/walletmodel.h | 2 |
23 files changed, 441 insertions, 142 deletions
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index c31dea2067..23ec3ab434 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -10,6 +10,7 @@ #include "chainparams.h" #include "clientmodel.h" +#include "fs.h" #include "guiconstants.h" #include "guiutil.h" #include "intro.h" @@ -38,7 +39,6 @@ #include <stdint.h> -#include <boost/filesystem/operations.hpp> #include <boost/thread.hpp> #include <QApplication> @@ -152,15 +152,21 @@ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTrans #if QT_VERSION < 0x050000 void DebugMessageHandler(QtMsgType type, const char *msg) { - const char *category = (type == QtDebugMsg) ? "qt" : NULL; - LogPrint(category, "GUI: %s\n", msg); + if (type == QtDebugMsg) { + LogPrint(BCLog::QT, "GUI: %s\n", msg); + } else { + LogPrintf("GUI: %s\n", msg); + } } #else void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &msg) { Q_UNUSED(context); - const char *category = (type == QtDebugMsg) ? "qt" : NULL; - LogPrint(category, "GUI: %s\n", msg.toStdString()); + if (type == QtDebugMsg) { + LogPrint(BCLog::QT, "GUI: %s\n", msg.toStdString()); + } else { + LogPrintf("GUI: %s\n", msg.toStdString()); + } } #endif @@ -178,8 +184,8 @@ public Q_SLOTS: void shutdown(); Q_SIGNALS: - void initializeResult(int retval); - void shutdownResult(int retval); + void initializeResult(bool success); + void shutdownResult(); void runawayException(const QString &message); private: @@ -223,8 +229,8 @@ public: WId getMainWinId() const; public Q_SLOTS: - void initializeResult(int retval); - void shutdownResult(int retval); + void initializeResult(bool success); + void shutdownResult(); /// Handle runaway exceptions. Shows a message box with the problem and quits the program. void handleRunawayException(const QString &message); @@ -284,7 +290,7 @@ void BitcoinCore::initialize() Q_EMIT initializeResult(false); return; } - int rv = AppInitMain(threadGroup, scheduler); + bool rv = AppInitMain(threadGroup, scheduler); Q_EMIT initializeResult(rv); } catch (const std::exception& e) { handleRunawayException(&e); @@ -302,7 +308,7 @@ void BitcoinCore::shutdown() threadGroup.join_all(); Shutdown(); qDebug() << __func__ << ": Shutdown finished"; - Q_EMIT shutdownResult(1); + Q_EMIT shutdownResult(); } catch (const std::exception& e) { handleRunawayException(&e); } catch (...) { @@ -398,8 +404,8 @@ void BitcoinApplication::startThread() executor->moveToThread(coreThread); /* communication to and from thread */ - connect(executor, SIGNAL(initializeResult(int)), this, SLOT(initializeResult(int))); - connect(executor, SIGNAL(shutdownResult(int)), this, SLOT(shutdownResult(int))); + connect(executor, SIGNAL(initializeResult(bool)), this, SLOT(initializeResult(bool))); + connect(executor, SIGNAL(shutdownResult()), this, SLOT(shutdownResult())); connect(executor, SIGNAL(runawayException(QString)), this, SLOT(handleRunawayException(QString))); connect(this, SIGNAL(requestedInitialize()), executor, SLOT(initialize())); connect(this, SIGNAL(requestedShutdown()), executor, SLOT(shutdown())); @@ -450,12 +456,12 @@ void BitcoinApplication::requestShutdown() Q_EMIT requestedShutdown(); } -void BitcoinApplication::initializeResult(int retval) +void BitcoinApplication::initializeResult(bool success) { - qDebug() << __func__ << ": Initialization result: " << retval; - // Set exit result: 0 if successful, 1 if failure - returnValue = retval ? 0 : 1; - if(retval) + qDebug() << __func__ << ": Initialization result: " << success; + // Set exit result. + returnValue = success ? EXIT_SUCCESS : EXIT_FAILURE; + if(success) { // Log this only after AppInitMain finishes, as then logging setup is guaranteed complete qWarning() << "Platform customization:" << platformStyle->getName(); @@ -507,9 +513,8 @@ void BitcoinApplication::initializeResult(int retval) } } -void BitcoinApplication::shutdownResult(int retval) +void BitcoinApplication::shutdownResult() { - qDebug() << __func__ << ": Shutdown result: " << retval; quit(); // Exit main loop after shutdown finished } @@ -603,7 +608,7 @@ int main(int argc, char *argv[]) /// 6. Determine availability of data directory and parse bitcoin.conf /// - Do not call GetDataDir(true) before this step finishes - if (!boost::filesystem::is_directory(GetDataDir(false))) + if (!fs::is_directory(GetDataDir(false))) { QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(GetArg("-datadir", "")))); diff --git a/src/qt/callback.h b/src/qt/callback.h new file mode 100644 index 0000000000..a8b593a652 --- /dev/null +++ b/src/qt/callback.h @@ -0,0 +1,30 @@ +#ifndef BITCOIN_QT_CALLBACK_H +#define BITCOIN_QT_CALLBACK_H + +#include <QObject> + +class Callback : public QObject +{ + Q_OBJECT +public Q_SLOTS: + virtual void call() = 0; +}; + +template <typename F> +class FunctionCallback : public Callback +{ + F f; + +public: + FunctionCallback(F f_) : f(std::move(f_)) {} + ~FunctionCallback() override {} + void call() override { f(this); } +}; + +template <typename F> +FunctionCallback<F>* makeCallback(F f) +{ + return new FunctionCallback<F>(std::move(f)); +} + +#endif // BITCOIN_QT_CALLBACK_H diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index d4fd8bd372..1d19c65753 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -444,11 +444,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) CAmount nChange = 0; unsigned int nBytes = 0; unsigned int nBytesInputs = 0; - double dPriority = 0; - double dPriorityInputs = 0; unsigned int nQuantity = 0; - int nQuantityUncompressed = 0; - bool fAllowFree = false; bool fWitness = false; std::vector<COutPoint> vCoinControl; @@ -473,9 +469,6 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) // Amount nAmount += out.tx->tx->vout[out.i].nValue; - // Priority - dPriorityInputs += (double)out.tx->tx->vout[out.i].nValue * (out.nDepth+1); - // Bytes CTxDestination address; int witnessversion = 0; @@ -492,8 +485,6 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) if (keyid && model->getPubKey(*keyid, pubkey)) { nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); - if (!pubkey.IsCompressed()) - nQuantityUncompressed++; } else nBytesInputs += 148; // in all error cases, simply assume 148 here @@ -525,17 +516,6 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) if (nPayFee > 0 && coinControl->nMinimumTotalFee > nPayFee) nPayFee = coinControl->nMinimumTotalFee; - - // Allow free? (require at least hard-coded threshold and default to that if no estimate) - double mempoolEstimatePriority = mempool.estimateSmartPriority(nTxConfirmTarget); - dPriority = dPriorityInputs / (nBytes - nBytesInputs + (nQuantityUncompressed * 29)); // 29 = 180 - 151 (uncompressed public keys are over the limit. max 151 bytes of the input are ignored for priority) - double dPriorityNeeded = std::max(mempoolEstimatePriority, AllowFreeThreshold()); - fAllowFree = (dPriority >= dPriorityNeeded); - - if (fSendFreeTransactions) - if (fAllowFree && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) - nPayFee = 0; - if (nPayAmount > 0) { nChange = nAmount - nPayAmount; diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index 8be4a955b3..093e644bdc 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -645,7 +645,7 @@ <item> <widget class="QPushButton" name="btnClearTrafficGraph"> <property name="text"> - <string>&Clear</string> + <string>&Reset</string> </property> <property name="autoDefault"> <bool>false</bool> diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 0b29201872..0f1b3f4a73 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -692,17 +692,34 @@ <item> <layout class="QHBoxLayout" name="horizontalLayout_Buttons"> <item> - <widget class="QPushButton" name="resetButton"> - <property name="toolTip"> - <string>Reset all client options to default.</string> - </property> - <property name="text"> - <string>&Reset Options</string> - </property> - <property name="autoDefault"> - <bool>false</bool> - </property> - </widget> + <layout class="QVBoxLayout" name="verticalLayout_Buttons"> + <item> + <widget class="QPushButton" name="openBitcoinConfButton"> + <property name="toolTip"> + <string>Open the %1 configuration file from the working directory.</string> + </property> + <property name="text"> + <string>Open Configuration File</string> + </property> + <property name="autoDefault"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="resetButton"> + <property name="toolTip"> + <string>Reset all client options to default.</string> + </property> + <property name="text"> + <string>&Reset Options</string> + </property> + <property name="autoDefault"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> </item> <item> <spacer name="horizontalSpacer_1"> @@ -756,27 +773,48 @@ </spacer> </item> <item> - <widget class="QPushButton" name="okButton"> - <property name="text"> - <string>&OK</string> - </property> - <property name="autoDefault"> - <bool>false</bool> - </property> - <property name="default"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="cancelButton"> - <property name="text"> - <string>&Cancel</string> - </property> - <property name="autoDefault"> - <bool>false</bool> - </property> - </widget> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QPushButton" name="okButton"> + <property name="text"> + <string>&OK</string> + </property> + <property name="autoDefault"> + <bool>false</bool> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cancelButton"> + <property name="text"> + <string>&Cancel</string> + </property> + <property name="autoDefault"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> </item> </layout> </item> diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index ca8ecffafe..52256ca5c4 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -760,10 +760,32 @@ </layout> </item> <item> + <widget class="QLabel" name="fallbackFeeWarningLabel"> + <property name="toolTip"> + <string>Using the fallbackfee can result in sending a transaction that will take several hours or days (or never) to confirm. Consider choosing your fee manually or wait until your have validated the complete chain.</string> + </property> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Warning: Fee estimation is currently not possible.</string> + </property> + <property name="wordWrap"> + <bool>false</bool> + </property> + </widget> + </item> + <item> <spacer name="horizontalSpacer_4"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> + <property name="sizeType"> + <enum>QSizePolicy::MinimumExpanding</enum> + </property> <property name="sizeHint" stdset="0"> <size> <width>40</width> @@ -1158,6 +1180,16 @@ </item> </layout> </item> + <item> + <widget class="QCheckBox" name="optInRBF"> + <property name="text"> + <string>Request Replace-By-Fee</string> + </property> + <property name="toolTip"> + <string>Indicates that the sender may wish to replace this transaction with a new one paying higher fees (prior to being confirmed).</string> + </property> + </widget> + </item> </layout> </widget> </item> @@ -1168,8 +1200,8 @@ </property> <property name="sizeHint" stdset="0"> <size> - <width>800</width> - <height>1</height> + <width>40</width> + <height>5</height> </size> </property> </spacer> @@ -1203,7 +1235,7 @@ <bool>false</bool> </property> <property name="default"> - <bool>true</bool> + <bool>false</bool> </property> </widget> </item> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index fd3dcac424..3f3f9b9ccb 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -9,6 +9,7 @@ #include "qvalidatedlineedit.h" #include "walletmodel.h" +#include "fs.h" #include "primitives/transaction.h" #include "init.h" #include "policy/policy.h" @@ -35,9 +36,6 @@ #include "shlwapi.h" #endif -#include <boost/filesystem.hpp> -#include <boost/filesystem/fstream.hpp> -#include <boost/filesystem/detail/utf8_codecvt_facet.hpp> #include <boost/scoped_array.hpp> #include <QAbstractItemView> @@ -65,7 +63,7 @@ #include <QFontDatabase> #endif -static boost::filesystem::detail::utf8_codecvt_facet utf8; +static fs::detail::utf8_codecvt_facet utf8; #if defined(Q_OS_MAC) extern double NSAppKitVersionNumber; @@ -410,13 +408,29 @@ bool isObscured(QWidget *w) void openDebugLogfile() { - boost::filesystem::path pathDebug = GetDataDir() / "debug.log"; + fs::path pathDebug = GetDataDir() / "debug.log"; /* Open debug.log with the associated application */ - if (boost::filesystem::exists(pathDebug)) + if (fs::exists(pathDebug)) QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathDebug))); } +bool openBitcoinConf() +{ + boost::filesystem::path pathConfig = GetConfigFile(BITCOIN_CONF_FILENAME); + + /* Create the file */ + boost::filesystem::ofstream configFile(pathConfig, std::ios_base::app); + + if (!configFile.good()) + return false; + + configFile.close(); + + /* Open bitcoin.conf with the associated application */ + return QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathConfig))); +} + void SubstituteFonts(const QString& language) { #if defined(Q_OS_MAC) @@ -443,7 +457,7 @@ void SubstituteFonts(const QString& language) /* 10.10 or later system */ if (language == "zh_CN" || language == "zh_TW" || language == "zh_HK") // traditional or simplified Chinese QFont::insertSubstitution(".Helvetica Neue DeskInterface", "Heiti SC"); - else if (language == "ja") // Japanesee + else if (language == "ja") // Japanese QFont::insertSubstitution(".Helvetica Neue DeskInterface", "Songti SC"); else QFont::insertSubstitution(".Helvetica Neue DeskInterface", "Lucida Grande"); @@ -597,7 +611,7 @@ TableViewLastColumnResizingFixer::TableViewLastColumnResizingFixer(QTableView* t } #ifdef WIN32 -boost::filesystem::path static StartupShortcutPath() +fs::path static StartupShortcutPath() { std::string chain = ChainNameFromCommandLine(); if (chain == CBaseChainParams::MAIN) @@ -610,13 +624,13 @@ boost::filesystem::path static StartupShortcutPath() bool GetStartOnSystemStartup() { // check for Bitcoin*.lnk - return boost::filesystem::exists(StartupShortcutPath()); + return fs::exists(StartupShortcutPath()); } bool SetStartOnSystemStartup(bool fAutoStart) { // If the shortcut exists already, remove it for updating - boost::filesystem::remove(StartupShortcutPath()); + fs::remove(StartupShortcutPath()); if (fAutoStart) { @@ -686,10 +700,8 @@ bool SetStartOnSystemStartup(bool fAutoStart) // Follow the Desktop Application Autostart Spec: // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html -boost::filesystem::path static GetAutostartDir() +fs::path static GetAutostartDir() { - namespace fs = boost::filesystem; - char* pszConfigHome = getenv("XDG_CONFIG_HOME"); if (pszConfigHome) return fs::path(pszConfigHome) / "autostart"; char* pszHome = getenv("HOME"); @@ -697,7 +709,7 @@ boost::filesystem::path static GetAutostartDir() return fs::path(); } -boost::filesystem::path static GetAutostartFilePath() +fs::path static GetAutostartFilePath() { std::string chain = ChainNameFromCommandLine(); if (chain == CBaseChainParams::MAIN) @@ -707,7 +719,7 @@ boost::filesystem::path static GetAutostartFilePath() bool GetStartOnSystemStartup() { - boost::filesystem::ifstream optionFile(GetAutostartFilePath()); + fs::ifstream optionFile(GetAutostartFilePath()); if (!optionFile.good()) return false; // Scan through file for "Hidden=true": @@ -727,7 +739,7 @@ bool GetStartOnSystemStartup() bool SetStartOnSystemStartup(bool fAutoStart) { if (!fAutoStart) - boost::filesystem::remove(GetAutostartFilePath()); + fs::remove(GetAutostartFilePath()); else { char pszExePath[MAX_PATH+1]; @@ -735,9 +747,9 @@ bool SetStartOnSystemStartup(bool fAutoStart) if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1) return false; - boost::filesystem::create_directories(GetAutostartDir()); + fs::create_directories(GetAutostartDir()); - boost::filesystem::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc); + fs::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc); if (!optionFile.good()) return false; std::string chain = ChainNameFromCommandLine(); @@ -843,14 +855,17 @@ void restoreWindowGeometry(const QString& strSetting, const QSize& defaultSize, QPoint pos = settings.value(strSetting + "Pos").toPoint(); QSize size = settings.value(strSetting + "Size", defaultSize).toSize(); - if (!pos.x() && !pos.y()) { - QRect screen = QApplication::desktop()->screenGeometry(); - pos.setX((screen.width() - size.width()) / 2); - pos.setY((screen.height() - size.height()) / 2); - } - parent->resize(size); parent->move(pos); + + if ((!pos.x() && !pos.y()) || (QApplication::desktop()->screenNumber(parent) == -1)) + { + QRect screen = QApplication::desktop()->screenGeometry(); + QPoint defaultPos((screen.width() - defaultSize.width()) / 2, + (screen.height() - defaultSize.height()) / 2); + parent->resize(defaultSize); + parent->move(defaultPos); + } } void setClipboard(const QString& str) @@ -859,12 +874,12 @@ void setClipboard(const QString& str) QApplication::clipboard()->setText(str, QClipboard::Selection); } -boost::filesystem::path qstringToBoostPath(const QString &path) +fs::path qstringToBoostPath(const QString &path) { - return boost::filesystem::path(path.toStdString(), utf8); + return fs::path(path.toStdString(), utf8); } -QString boostPathToQString(const boost::filesystem::path &path) +QString boostPathToQString(const fs::path &path) { return QString::fromStdString(path.string(utf8)); } diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 913aa5e24b..d6aa8c4ea6 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -6,6 +6,7 @@ #define BITCOIN_QT_GUIUTIL_H #include "amount.h" +#include "fs.h" #include <QEvent> #include <QHeaderView> @@ -16,8 +17,6 @@ #include <QTableView> #include <QLabel> -#include <boost/filesystem.hpp> - class QValidatedLineEdit; class SendCoinsRecipient; @@ -114,6 +113,9 @@ namespace GUIUtil // Open debug.log void openDebugLogfile(); + // Open the config file + bool openBitcoinConf(); + // Replace invalid default fonts with known good ones void SubstituteFonts(const QString& language); @@ -183,10 +185,10 @@ namespace GUIUtil void restoreWindowGeometry(const QString& strSetting, const QSize &defaultSizeIn, QWidget *parent); /* Convert QString to OS specific boost path through UTF-8 */ - boost::filesystem::path qstringToBoostPath(const QString &path); + fs::path qstringToBoostPath(const QString &path); /* Convert OS specific boost path to QString through UTF-8 */ - QString boostPathToQString(const boost::filesystem::path &path); + QString boostPathToQString(const fs::path &path); /* Convert seconds into a QString with days, hours, mins, secs */ QString formatDurationStr(int secs); diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 4939648ff0..2460a59109 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -6,6 +6,7 @@ #include "config/bitcoin-config.h" #endif +#include "fs.h" #include "intro.h" #include "ui_intro.h" @@ -13,8 +14,6 @@ #include "util.h" -#include <boost/filesystem.hpp> - #include <QFileDialog> #include <QSettings> #include <QMessageBox> @@ -70,7 +69,6 @@ FreespaceChecker::FreespaceChecker(Intro *_intro) void FreespaceChecker::check() { - namespace fs = boost::filesystem; QString dataDirStr = intro->getPathToCheck(); fs::path dataDir = GUIUtil::qstringToBoostPath(dataDirStr); uint64_t freeBytesAvailable = 0; @@ -190,7 +188,6 @@ QString Intro::getDefaultDataDirectory() bool Intro::pickDataDirectory() { - namespace fs = boost::filesystem; QSettings settings; /* If data directory provided on command line, no need to look at settings or show a picking dialog */ diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 7ff00b1e9e..efb25aaf18 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -232,6 +232,18 @@ void OptionsDialog::on_resetButton_clicked() } } +void OptionsDialog::on_openBitcoinConfButton_clicked() +{ + /* explain the purpose of the config file */ + QMessageBox::information(this, tr("Configuration options"), + tr("The configuration file is used to specify advanced user options which override GUI settings. " + "Additionally, any command-line options will override this configuration file.")); + + /* show an error if there was some problem opening the file */ + if (!GUIUtil::openBitcoinConf()) + QMessageBox::critical(this, tr("Error"), tr("The configuration file could not be opened.")); +} + void OptionsDialog::on_okButton_clicked() { mapper->submit(); diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h index d98c1dc193..f9f5823c05 100644 --- a/src/qt/optionsdialog.h +++ b/src/qt/optionsdialog.h @@ -47,6 +47,7 @@ private Q_SLOTS: /* set OK button state (enabled / disabled) */ void setOkButtonState(bool fState); void on_resetButton_clicked(); + void on_openBitcoinConfButton_clicked(); void on_okButton_clicked(); void on_cancelButton_clicked(); diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index 974a71ddca..fff072fd4c 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -166,7 +166,7 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const switch(index.column()) { case NetNodeId: - return rec->nodeStats.nodeid; + return (qint64)rec->nodeStats.nodeid; case Address: return QString::fromStdString(rec->nodeStats.addrName); case Subversion: diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 60406c2059..bb8aa23de8 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -175,6 +175,10 @@ bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string & nDepthInsideSensitive = 1; filter_begin_pos = chpos; } + // Make sure stack is not empty before adding something + if (stack.empty()) { + stack.push_back(std::vector<std::string>()); + } stack.back().push_back(strArg); }; @@ -626,9 +630,12 @@ void RPCConsole::setClientModel(ClientModel *model) for (size_t i = 0; i < commandList.size(); ++i) { wordList << commandList[i].c_str(); + wordList << ("help " + commandList[i]).c_str(); } + wordList.sort(); autoCompleter = new QCompleter(wordList, this); + autoCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); ui->lineEdit->setCompleter(autoCompleter); autoCompleter->popup()->installEventFilter(this); // Start thread to execute RPC commands. @@ -1111,7 +1118,7 @@ void RPCConsole::disconnectSelectedNode() for(int i = 0; i < nodes.count(); i++) { // Get currently selected peer address - NodeId id = nodes.at(i).data().toInt(); + NodeId id = nodes.at(i).data().toLongLong(); // Find the node, disconnect it and clear the selected node if(g_connman->DisconnectNode(id)) clearSelectedNode(); @@ -1128,7 +1135,7 @@ void RPCConsole::banSelectedNode(int bantime) for(int i = 0; i < nodes.count(); i++) { // Get currently selected peer address - NodeId id = nodes.at(i).data().toInt(); + NodeId id = nodes.at(i).data().toLongLong(); // Get currently selected peer address int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(id); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 1c0ed663c1..ed7eab03f3 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -23,6 +23,7 @@ #include "txmempool.h" #include "wallet/wallet.h" +#include <QFontMetrics> #include <QMessageBox> #include <QScrollBar> #include <QSettings> @@ -113,6 +114,7 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p ui->groupCustomFee->button((int)std::max(0, std::min(1, settings.value("nCustomFeeRadio").toInt())))->setChecked(true); ui->customFee->setValue(settings.value("nTransactionFee").toLongLong()); ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool()); + ui->optInRBF->setCheckState(model->getDefaultWalletRbf() ? Qt::Checked : Qt::Unchecked); minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool()); } @@ -246,6 +248,8 @@ void SendCoinsDialog::on_sendButton_clicked() else ctrl.nConfirmTarget = 0; + ctrl.signalRbf = ui->optInRBF->isChecked(); + prepareStatus = model->prepareTransaction(currentTransaction, &ctrl); // process prepareStatus and on error generate message shown to user @@ -325,6 +329,13 @@ void SendCoinsDialog::on_sendButton_clicked() questionString.append(QString("<span style='font-size:10pt;font-weight:normal;'><br />(=%2)</span>") .arg(alternativeUnits.join(" " + tr("or") + "<br />"))); + if (ui->optInRBF->isChecked()) + { + questionString.append("<hr /><span>"); + questionString.append(tr("This transaction signals replaceability (optin-RBF).")); + questionString.append("</span>"); + } + SendConfirmationDialog confirmationDialog(tr("Confirm send coins"), questionString.arg(formatted.join("<br />")), SEND_CONFIRM_DELAY, this); confirmationDialog.exec(); @@ -656,6 +667,11 @@ void SendCoinsDialog::updateSmartFeeLabel() std::max(CWallet::fallbackFee.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB"); ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...) ui->labelFeeEstimation->setText(""); + ui->fallbackFeeWarningLabel->setVisible(true); + int lightness = ui->fallbackFeeWarningLabel->palette().color(QPalette::WindowText).lightness(); + QColor warning_colour(255 - (lightness / 5), 176 - (lightness / 3), 48 - (lightness / 14)); + ui->fallbackFeeWarningLabel->setStyleSheet("QLabel { color: " + warning_colour.name() + "; }"); + ui->fallbackFeeWarningLabel->setIndent(QFontMetrics(ui->fallbackFeeWarningLabel->font()).width("x")); } else { @@ -663,6 +679,7 @@ void SendCoinsDialog::updateSmartFeeLabel() std::max(feeRate.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB"); ui->labelSmartFee2->hide(); ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", estimateFoundAtBlocks)); + ui->fallbackFeeWarningLabel->setVisible(false); } updateFeeMinimizedLabel(); diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp index 84ccfea730..08a76c7d49 100644 --- a/src/qt/test/paymentservertests.cpp +++ b/src/qt/test/paymentservertests.cpp @@ -142,7 +142,7 @@ void PaymentServerTests::paymentServerTests() byteArray = QByteArray((const char*)&data[0], data.size()); r.paymentRequest.parse(byteArray); // Ensure the request is initialized, because network "main" is default, even for - // uninizialized payment requests and that will fail our test here. + // uninitialized payment requests and that will fail our test here. QVERIFY(r.paymentRequest.IsInitialized()); QCOMPARE(PaymentServer::verifyNetwork(r.paymentRequest.getDetails()), false); diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index bd496f149c..dada689731 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -6,6 +6,7 @@ #include "chainparams.h" #include "consensus/validation.h" +#include "fs.h" #include "validation.h" #include "rpc/register.h" #include "rpc/server.h" @@ -17,8 +18,6 @@ #include <QDir> #include <QtGlobal> -#include <boost/filesystem.hpp> - static UniValue rpcNestedTest_rpc(const JSONRPCRequest& request) { if (request.fHelp) { @@ -148,9 +147,13 @@ void RPCNestedTests::rpcNestedTests() QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tollerate empty arguments when using , #endif + UnloadBlockIndex(); delete pcoinsTip; + pcoinsTip = nullptr; delete pcoinsdbview; + pcoinsdbview = nullptr; delete pblocktree; + pblocktree = nullptr; - boost::filesystem::remove_all(boost::filesystem::path(path)); + fs::remove_all(fs::path(path)); } diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index d44d711315..cae18f41a5 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -7,7 +7,6 @@ #endif #include "chainparams.h" -#include "key.h" #include "rpcnestedtests.h" #include "util.h" #include "uritests.h" @@ -15,20 +14,34 @@ #ifdef ENABLE_WALLET #include "paymentservertests.h" +#include "wallettests.h" #endif -#include <QCoreApplication> +#include <QApplication> #include <QObject> #include <QTest> #include <openssl/ssl.h> -#if defined(QT_STATICPLUGIN) && QT_VERSION < 0x050000 +#if defined(QT_STATICPLUGIN) #include <QtPlugin> +#if QT_VERSION < 0x050000 Q_IMPORT_PLUGIN(qcncodecs) Q_IMPORT_PLUGIN(qjpcodecs) Q_IMPORT_PLUGIN(qtwcodecs) Q_IMPORT_PLUGIN(qkrcodecs) +#else +#if defined(QT_QPA_PLATFORM_MINIMAL) +Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin); +#endif +#if defined(QT_QPA_PLATFORM_XCB) +Q_IMPORT_PLUGIN(QXcbIntegrationPlugin); +#elif defined(QT_QPA_PLATFORM_WINDOWS) +Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); +#elif defined(QT_QPA_PLATFORM_COCOA) +Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin); +#endif +#endif #endif extern void noui_connect(); @@ -36,7 +49,6 @@ extern void noui_connect(); // This is all you need to run all the tests int main(int argc, char *argv[]) { - ECC_Start(); SetupEnvironment(); SetupNetworking(); SelectParams(CBaseChainParams::MAIN); @@ -44,28 +56,42 @@ int main(int argc, char *argv[]) bool fInvalid = false; + // Prefer the "minimal" platform for the test instead of the normal default + // platform ("xcb", "windows", or "cocoa") so tests can't unintentially + // interfere with any background GUIs and don't require extra resources. + setenv("QT_QPA_PLATFORM", "minimal", 0); + // Don't remove this, it's needed to access - // QCoreApplication:: in the tests - QCoreApplication app(argc, argv); + // QApplication:: and QCoreApplication:: in the tests + QApplication app(argc, argv); app.setApplicationName("Bitcoin-Qt-test"); SSL_library_init(); URITests test1; - if (QTest::qExec(&test1) != 0) + if (QTest::qExec(&test1) != 0) { fInvalid = true; + } #ifdef ENABLE_WALLET PaymentServerTests test2; - if (QTest::qExec(&test2) != 0) + if (QTest::qExec(&test2) != 0) { fInvalid = true; + } #endif RPCNestedTests test3; - if (QTest::qExec(&test3) != 0) + if (QTest::qExec(&test3) != 0) { fInvalid = true; + } CompatTests test4; - if (QTest::qExec(&test4) != 0) + if (QTest::qExec(&test4) != 0) { fInvalid = true; + } +#ifdef ENABLE_WALLET + WalletTests test5; + if (QTest::qExec(&test5) != 0) { + fInvalid = true; + } +#endif - ECC_Stop(); return fInvalid; } diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp new file mode 100644 index 0000000000..f794b6b382 --- /dev/null +++ b/src/qt/test/wallettests.cpp @@ -0,0 +1,118 @@ +#include "wallettests.h" + +#include "qt/bitcoinamountfield.h" +#include "qt/callback.h" +#include "qt/optionsmodel.h" +#include "qt/platformstyle.h" +#include "qt/qvalidatedlineedit.h" +#include "qt/sendcoinsdialog.h" +#include "qt/sendcoinsentry.h" +#include "qt/transactiontablemodel.h" +#include "qt/walletmodel.h" +#include "test/test_bitcoin.h" +#include "validation.h" +#include "wallet/wallet.h" + +#include <QAbstractButton> +#include <QApplication> +#include <QTimer> +#include <QVBoxLayout> + +namespace +{ +//! Press "Yes" button in modal send confirmation dialog. +void ConfirmSend() +{ + QTimer::singleShot(0, makeCallback([](Callback* callback) { + for (QWidget* widget : QApplication::topLevelWidgets()) { + if (widget->inherits("SendConfirmationDialog")) { + SendConfirmationDialog* dialog = qobject_cast<SendConfirmationDialog*>(widget); + QAbstractButton* button = dialog->button(QMessageBox::Yes); + button->setEnabled(true); + button->click(); + } + } + delete callback; + }), SLOT(call())); +} + +//! Send coins to address and return txid. +uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CBitcoinAddress& address, CAmount amount) +{ + QVBoxLayout* entries = sendCoinsDialog.findChild<QVBoxLayout*>("entries"); + SendCoinsEntry* entry = qobject_cast<SendCoinsEntry*>(entries->itemAt(0)->widget()); + entry->findChild<QValidatedLineEdit*>("payTo")->setText(QString::fromStdString(address.ToString())); + entry->findChild<BitcoinAmountField*>("payAmount")->setValue(amount); + uint256 txid; + boost::signals2::scoped_connection c(wallet.NotifyTransactionChanged.connect([&txid](CWallet*, const uint256& hash, ChangeType status) { + if (status == CT_NEW) txid = hash; + })); + ConfirmSend(); + QMetaObject::invokeMethod(&sendCoinsDialog, "on_sendButton_clicked"); + return txid; +} + +//! Find index of txid in transaction list. +QModelIndex FindTx(const QAbstractItemModel& model, const uint256& txid) +{ + QString hash = QString::fromStdString(txid.ToString()); + int rows = model.rowCount({}); + for (int row = 0; row < rows; ++row) { + QModelIndex index = model.index(row, 0, {}); + if (model.data(index, TransactionTableModel::TxHashRole) == hash) { + return index; + } + } + return {}; +} +} + +//! Simple qt wallet tests. +// +// Test widgets can be debugged interactively calling show() on them and +// manually running the event loop, e.g.: +// +// sendCoinsDialog.show(); +// QEventLoop().exec(); +// +// This also requires overriding the default minimal Qt platform: +// +// src/qt/test/test_bitcoin-qt -platform xcb # Linux +// src/qt/test/test_bitcoin-qt -platform windows # Windows +// src/qt/test/test_bitcoin-qt -platform cocoa # macOS +void WalletTests::walletTests() +{ + // Set up wallet and chain with 101 blocks (1 mature block for spending). + TestChain100Setup test; + test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey())); + bitdb.MakeMock(); + CWallet wallet("wallet_test.dat"); + bool firstRun; + wallet.LoadWallet(firstRun); + { + LOCK(wallet.cs_wallet); + wallet.SetAddressBook(test.coinbaseKey.GetPubKey().GetID(), "", "receive"); + wallet.AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey()); + } + wallet.ScanForWalletTransactions(chainActive.Genesis(), true); + wallet.SetBroadcastTransactions(true); + + // Create widgets for sending coins and listing transactions. + std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other")); + SendCoinsDialog sendCoinsDialog(platformStyle.get()); + OptionsModel optionsModel; + WalletModel walletModel(platformStyle.get(), &wallet, &optionsModel); + sendCoinsDialog.setModel(&walletModel); + + // Send two transactions, and verify they are added to transaction list. + TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel(); + QCOMPARE(transactionTableModel->rowCount({}), 101); + uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 5 * COIN); + uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 10 * COIN); + QCOMPARE(transactionTableModel->rowCount({}), 103); + QVERIFY(FindTx(*transactionTableModel, txid1).isValid()); + QVERIFY(FindTx(*transactionTableModel, txid2).isValid()); + + bitdb.Flush(true); + bitdb.Reset(); +} diff --git a/src/qt/test/wallettests.h b/src/qt/test/wallettests.h new file mode 100644 index 0000000000..342f7916c3 --- /dev/null +++ b/src/qt/test/wallettests.h @@ -0,0 +1,15 @@ +#ifndef BITCOIN_QT_TEST_WALLETTESTS_H +#define BITCOIN_QT_TEST_WALLETTESTS_H + +#include <QObject> +#include <QTest> + +class WalletTests : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void walletTests(); +}; + +#endif // BITCOIN_QT_TEST_WALLETTESTS_H diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 7e16cc9dd4..d81188895b 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -273,7 +273,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco // // Debug view // - if (fDebug) + if (logCategories != BCLog::NONE) { strHTML += "<hr><br>" + tr("Debug information") + "<br><br>"; BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index a9d9b6887e..4bb260aa58 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -18,14 +18,8 @@ */ bool TransactionRecord::showTransaction(const CWalletTx &wtx) { - if (wtx.IsCoinBase()) - { - // Ensures we show generated coins / mined transactions at depth 1 - if (!wtx.IsInMainChain()) - { - return false; - } - } + // There are currently no cases where we hide transactions, but + // we may want to use this in the future for things like RBF. return true; } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 0a5a7c3e9f..ebcac53c25 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -580,7 +580,7 @@ void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vect if (!wallet->mapWallet.count(outpoint.hash)) continue; int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); if (nDepth < 0) continue; - COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true, true); + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true /* spendable */, true /* solvable */, true /* safe */); vOutputs.push_back(out); } } @@ -607,7 +607,7 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) if (!wallet->mapWallet.count(outpoint.hash)) continue; int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); if (nDepth < 0) continue; - COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true, true); + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true /* spendable */, true /* solvable */, true /* safe */); if (outpoint.n < out.tx->tx->vout.size() && wallet->IsMine(out.tx->tx->vout[outpoint.n]) == ISMINE_SPENDABLE) vCoins.push_back(out); } @@ -619,7 +619,7 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) while (wallet->IsChange(cout.tx->tx->vout[cout.i]) && cout.tx->tx->vin.size() > 0 && wallet->IsMine(cout.tx->tx->vin[0])) { if (!wallet->mapWallet.count(cout.tx->tx->vin[0].prevout.hash)) break; - cout = COutput(&wallet->mapWallet[cout.tx->tx->vin[0].prevout.hash], cout.tx->tx->vin[0].prevout.n, 0, true, true); + cout = COutput(&wallet->mapWallet[cout.tx->tx->vin[0].prevout.hash], cout.tx->tx->vin[0].prevout.n, 0 /* depth */, true /* spendable */, true /* solvable */, true /* safe */); } CTxDestination address; @@ -706,3 +706,8 @@ int WalletModel::getDefaultConfirmTarget() const { return nTxConfirmTarget; } + +bool WalletModel::getDefaultWalletRbf() const +{ + return fWalletRbf; +} diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index cd7585635f..78e45dc369 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -213,6 +213,8 @@ public: int getDefaultConfirmTarget() const; + bool getDefaultWalletRbf() const; + private: CWallet *wallet; bool fHaveWatchOnly; |