aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.travis/lint_06_script.sh3
-rw-r--r--Makefile.am5
-rw-r--r--build-aux/m4/bitcoin_qt.m42
-rw-r--r--doc/release-notes-13152.md5
-rw-r--r--doc/release-notes-14023.md8
-rw-r--r--doc/release-notes-14282.md9
-rw-r--r--doc/release-notes-14296.md5
-rw-r--r--doc/release-notes-14454.md6
-rw-r--r--doc/release-notes-14468.md15
-rw-r--r--doc/release-notes-pr13381.md29
-rw-r--r--doc/release-notes.md140
-rw-r--r--share/setup.nsi.in1
-rw-r--r--src/prevector.h16
-rw-r--r--src/qt/bitcoin.cpp2
-rw-r--r--src/qt/bitcoinamountfield.cpp67
-rw-r--r--src/qt/bitcoinamountfield.h9
-rw-r--r--src/qt/bitcoingui.cpp72
-rw-r--r--src/qt/bitcoingui.h3
-rw-r--r--src/qt/forms/sendcoinsdialog.ui41
-rw-r--r--src/qt/guiutil.cpp40
-rw-r--r--src/qt/guiutil.h3
-rw-r--r--src/qt/macdockiconhandler.h21
-rw-r--r--src/qt/macdockiconhandler.mm100
-rw-r--r--src/qt/optionsdialog.cpp14
-rw-r--r--src/qt/sendcoinsdialog.cpp37
-rw-r--r--src/qt/sendcoinsdialog.h2
-rw-r--r--src/qt/splashscreen.cpp4
-rw-r--r--src/qt/test/util.h2
-rw-r--r--src/qt/walletview.cpp8
-rw-r--r--src/rpc/blockchain.cpp21
-rw-r--r--src/rpc/mining.cpp7
-rw-r--r--src/rpc/net.cpp15
-rw-r--r--src/rpc/rawtransaction.cpp97
-rw-r--r--src/rpc/server.cpp5
-rw-r--r--src/rpc/util.cpp92
-rw-r--r--src/rpc/util.h49
-rw-r--r--src/script/sign.h3
-rw-r--r--src/util/system.cpp6
-rw-r--r--src/wallet/rpcdump.cpp31
-rw-r--r--src/wallet/rpcwallet.cpp111
-rw-r--r--src/zmq/zmqrpc.cpp1
-rwxr-xr-xtest/functional/feature_block.py2
-rwxr-xr-xtest/functional/feature_config_args.py4
-rwxr-xr-xtest/functional/feature_pruning.py2
-rwxr-xr-xtest/functional/mining_basic.py3
-rwxr-xr-xtest/functional/p2p_disconnect_ban.py2
-rwxr-xr-xtest/functional/p2p_invalid_block.py9
-rwxr-xr-xtest/functional/rpc_blockchain.py6
-rwxr-xr-xtest/functional/rpc_net.py6
-rwxr-xr-xtest/functional/rpc_psbt.py3
-rwxr-xr-xtest/functional/test_framework/test_node.py17
-rwxr-xr-xtest/functional/test_runner.py3
-rwxr-xr-xtest/functional/wallet_import_with_label.py134
-rwxr-xr-xtest/functional/wallet_importmulti.py13
-rwxr-xr-xtest/functional/wallet_importprunedfunds.py2
55 files changed, 909 insertions, 404 deletions
diff --git a/.travis/lint_06_script.sh b/.travis/lint_06_script.sh
index 6191d82571..701e6d8005 100755
--- a/.travis/lint_06_script.sh
+++ b/.travis/lint_06_script.sh
@@ -19,6 +19,7 @@ test/lint/check-rpc-mappings.py .
test/lint/lint-all.sh
if [ "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_EVENT_TYPE" = "cron" ]; then
+ git log --merges --before="2 days ago" -1 --format='%H' > ./contrib/verify-commits/trusted-sha512-root-commit
while read -r LINE; do travis_retry gpg --keyserver hkp://subset.pool.sks-keyservers.net --recv-keys $LINE; done < contrib/verify-commits/trusted-keys &&
- travis_wait 50 contrib/verify-commits/verify-commits.py;
+ travis_wait 50 contrib/verify-commits/verify-commits.py --clean-merge=2;
fi
diff --git a/Makefile.am b/Makefile.am
index 7eb4ea2f52..9a6e15f0dd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -19,6 +19,7 @@ endif
BITCOIND_BIN=$(top_builddir)/src/$(BITCOIN_DAEMON_NAME)$(EXEEXT)
BITCOIN_QT_BIN=$(top_builddir)/src/qt/$(BITCOIN_GUI_NAME)$(EXEEXT)
BITCOIN_CLI_BIN=$(top_builddir)/src/$(BITCOIN_CLI_NAME)$(EXEEXT)
+BITCOIN_TX_BIN=$(top_builddir)/src/$(BITCOIN_TX_NAME)$(EXEEXT)
BITCOIN_WIN_INSTALLER=$(PACKAGE)-$(PACKAGE_VERSION)-win$(WINDOWS_BITS)-setup$(EXEEXT)
empty :=
@@ -74,6 +75,7 @@ $(BITCOIN_WIN_INSTALLER): all-recursive
STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIND_BIN) $(top_builddir)/release
STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_QT_BIN) $(top_builddir)/release
STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_CLI_BIN) $(top_builddir)/release
+ STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_TX_BIN) $(top_builddir)/release
@test -f $(MAKENSIS) && $(MAKENSIS) -V2 $(top_builddir)/share/setup.nsi || \
echo error: could not build $@
@echo built $@
@@ -167,6 +169,9 @@ $(BITCOIND_BIN): FORCE
$(BITCOIN_CLI_BIN): FORCE
$(MAKE) -C src $(@F)
+$(BITCOIN_TX_BIN): FORCE
+ $(MAKE) -C src $(@F)
+
if USE_LCOV
LCOV_FILTER_PATTERN=-p "/usr/include/" -p "/usr/lib/" -p "src/leveldb/" -p "src/bench/" -p "src/univalue" -p "src/crypto/ctaes" -p "src/secp256k1"
diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4
index 05df8621d2..90a2cd9875 100644
--- a/build-aux/m4/bitcoin_qt.m4
+++ b/build-aux/m4/bitcoin_qt.m4
@@ -276,7 +276,7 @@ AC_DEFUN([_BITCOIN_QT_CHECK_QT5],[
#endif
]],
[[
- #if QT_VERSION < 0x050000 || QT_VERSION_MAJOR < 5
+ #if QT_VERSION < 0x050200 || QT_VERSION_MAJOR < 5
choke
#endif
]])],
diff --git a/doc/release-notes-13152.md b/doc/release-notes-13152.md
deleted file mode 100644
index ace56f4d1b..0000000000
--- a/doc/release-notes-13152.md
+++ /dev/null
@@ -1,5 +0,0 @@
-New RPC methods
-------------
-
-- `getnodeaddresses` returns peer addresses known to this node. It may be used to connect to nodes over TCP without using the DNS seeds.
-- `listwalletdir` returns a list of wallets in the wallet directory which is configured with `-walletdir` parameter.
diff --git a/doc/release-notes-14023.md b/doc/release-notes-14023.md
deleted file mode 100644
index 18ea6f26d0..0000000000
--- a/doc/release-notes-14023.md
+++ /dev/null
@@ -1,8 +0,0 @@
-Account API removed
--------------------
-
-The 'account' API was deprecated in v0.17 and has been fully removed in v0.18.
-The 'label' API was introduced in v0.17 as a replacement for accounts.
-
-See the release notes from v0.17 for a full description of the changes from the
-'account' API to the 'label' API.
diff --git a/doc/release-notes-14282.md b/doc/release-notes-14282.md
deleted file mode 100644
index 900ca04324..0000000000
--- a/doc/release-notes-14282.md
+++ /dev/null
@@ -1,9 +0,0 @@
-Low-level RPC changes
-----------------------
-
-`-usehd` was removed in version 0.16. From that version onwards, all new
-wallets created are hierarchical deterministic wallets. Version 0.18 makes
-specifying `-usehd` invalid config.
-
-`ischange` field of boolean type that shows if an address was used for change
-output was added to `getaddressinfo` method response.
diff --git a/doc/release-notes-14296.md b/doc/release-notes-14296.md
deleted file mode 100644
index f7c52baace..0000000000
--- a/doc/release-notes-14296.md
+++ /dev/null
@@ -1,5 +0,0 @@
-addwitnessaddress RPC method removed
-------------------------------------
-
-The `addwitnessaddress` RPC was added for segwit testing in version 0.13.0. It
-was deprecated in version 0.16.0. This version fully removes the RPC method.
diff --git a/doc/release-notes-14454.md b/doc/release-notes-14454.md
deleted file mode 100644
index dd2c6c7ced..0000000000
--- a/doc/release-notes-14454.md
+++ /dev/null
@@ -1,6 +0,0 @@
-Low-level RPC changes
-----------------------
-
-The `importmulti` RPC has been updated to support P2WSH, P2WPKH, P2SH-P2WPKH,
-P2SH-P2WSH. Each request now accepts an additional `witnessscript` to be used
-for P2WSH or P2SH-P2WSH.
diff --git a/doc/release-notes-14468.md b/doc/release-notes-14468.md
deleted file mode 100644
index fb0243aba8..0000000000
--- a/doc/release-notes-14468.md
+++ /dev/null
@@ -1,15 +0,0 @@
-Wallet `generate` RPC method deprecated
----------------------------------------
-
-The wallet's `generate` RPC method has been deprecated and will be fully
-removed in v0.19.
-
-`generate` is only used for testing. The RPC call reaches across multiple
-subsystems (wallet and mining), so is deprecated to simplify the wallet-node
-interface. Projects that are using `generate` for testing purposes should
-transition to using the `generatetoaddress` call, which does not require or use
-the wallet component. Calling `generatetoaddress` with an address returned by
-`getnewaddress` gives the same functionality as the old `generate` method.
-
-To continue using `generate` in v0.18, restart bitcoind with the
-`-deprecatedrpc=generate` configuration.
diff --git a/doc/release-notes-pr13381.md b/doc/release-notes-pr13381.md
new file mode 100644
index 0000000000..75faad9906
--- /dev/null
+++ b/doc/release-notes-pr13381.md
@@ -0,0 +1,29 @@
+RPC importprivkey: new label behavior
+-------------------------------------
+
+Previously, `importprivkey` automatically added the default empty label
+("") to all addresses associated with the imported private key. Now it
+defaults to using any existing label for those addresses. For example:
+
+- Old behavior: you import a watch-only address with the label "cold
+ wallet". Later, you import the corresponding private key using the
+ default settings. The address's label is changed from "cold wallet"
+ to "".
+
+- New behavior: you import a watch-only address with the label "cold
+ wallet". Later, you import the corresponding private key using the
+ default settings. The address's label remains "cold wallet".
+
+In both the previous and current case, if you directly specify a label
+during the import, that label will override whatever previous label the
+addresses may have had. Also in both cases, if none of the addresses
+previously had a label, they will still receive the default empty label
+(""). Examples:
+
+- You import a watch-only address with the label "temporary". Later you
+ import the corresponding private key with the label "final". The
+ address's label will be changed to "final".
+
+- You use the default settings to import a private key for an address that
+ was not previously in the wallet. Its addresses will receive the default
+ empty label ("").
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 2044a50098..f5c139e3f1 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -53,8 +53,14 @@ the Linux kernel, macOS 10.10+, and Windows 7 and newer (Windows XP is not suppo
Bitcoin Core should also work on most other Unix-like systems but is not
frequently tested on them.
-From 0.17.0 onwards macOS <10.10 is no longer supported. 0.17.0 is built using Qt 5.9.x, which doesn't
-support versions of macOS older than 10.10.
+From 0.17.0 onwards, macOS <10.10 is no longer supported. 0.17.0 is
+built using Qt 5.9.x, which doesn't support versions of macOS older than
+10.10. Additionally, Bitcoin Core does not yet change appearance when
+macOS "dark mode" is activated.
+
+In addition to previously-supported CPU platforms, this release's
+pre-compiled distribution also provides binaries for the RISC-V
+platform.
Notable changes
===============
@@ -69,9 +75,137 @@ nodes. The option will now by default be off for improved privacy and security
as well as reduced upload usage. The option can explicitly be turned on for
local-network debugging purposes.
-Example item
+Documentation
+-------------
+
+- A new short
+ [document](https://github.com/bitcoin/bitcoin/blob/master/doc/JSON-RPC-interface.md)
+ about the JSON-RPC interface describes cases where the results of an
+ RPC might contain inconsistencies between data sourced from different
+ subsystems, such as wallet state and mempool state. A note is added
+ to the [REST interface documentation](https://github.com/bitcoin/bitcoin/blob/master/doc/REST-interface.md)
+ indicating that the same rules apply.
+
+- A new [document](https://github.com/bitcoin/bitcoin/blob/master/doc/bitcoin-conf.md)
+ about the `bitcoin.conf` file describes how to use it to configure
+ Bitcoin Core.
+
+- A new document introduces Bitcoin Core's BIP174
+ [Partially-Signed Bitcoin Transactions (PSBT)](https://github.com/bitcoin/bitcoin/blob/master/doc/psbt.md)
+ interface, which is used to allow multiple programs to collaboratively
+ work to create, sign, and broadcast new transactions. This is useful
+ for offline (cold storage) wallets, multisig wallets, coinjoin
+ implementations, and many other cases where two or more programs need
+ to interact to generate a complete transaction.
+
+- The [output script descriptor](https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md)
+ documentation has been updated with information about new features in
+ this still-developing language for describing the output scripts that
+ a wallet or other program wants to receive notifications for, such as
+ which addresses it wants to know received payments. The language is
+ currently used in the `scantxoutset` RPC and is expected to be adapted
+ to other RPCs and to the underlying wallet structure.
+
+Build system changes
+--------------------
+
+- A new `--disable-bip70` option may be passed to `./configure` to
+ prevent Bitcoin-Qt from being built with support for the BIP70 payment
+ protocol or from linking libssl. As the payment protocol has exposed
+ Bitcoin Core to libssl vulnerabilities in the past, builders who don't
+ need BIP70 support are encouraged to use this option to reduce their
+ exposure to future vulnerabilities.
+
+Deprecated or removed RPCs
+--------------------------
+
+- The `signrawtransaction` RPC is removed after being deprecated and
+ hidden behind a special configuration option in version 0.17.0.
+
+- The 'account' API is removed after being deprecated in v0.17. The
+ 'label' API was introduced in v0.17 as a replacement for accounts.
+ See the [release notes from v0.17](https://github.com/bitcoin/bitcoin/blob/master/doc/release-notes/release-notes-0.17.0.md#label-and-account-apis-for-wallet)
+ for a full description of the changes from the 'account' API to the
+ 'label' API.
+
+- The `addwitnessaddress` RPC is removed after being deprecated in
+ version 0.13.0.
+
+- The wallet's `generate` RPC method is deprecated and will be fully
+ removed in a subsequent major version. This RPC is only used for
+ testing, but its implementation reached across multiple subsystems
+ (wallet and mining), so it is being deprecated to simplify the
+ wallet-node interface. Projects that are using `generate` for testing
+ purposes should transition to using the `generatetoaddress` RPC, which
+ does not require or use the wallet component. Calling
+ `generatetoaddress` with an address returned by the `getnewaddress`
+ RPC gives the same functionality as the old `generate` RPC. To
+ continue using `generate` in this version, restart bitcoind with the
+ `-deprecatedrpc=generate` configuration option.
+
+New RPCs
+--------
+
+- A new `getnodeaddresses` RPC returns peer addresses known to this
+ node. It may be used to find nodes to connect to without using a DNS
+ seeder.
+
+- A new `listwalletdir` RPC returns a list of wallets in the wallet
+ directory (either the default wallet directory or the directory
+ configured by the `-walletdir` parameter).
+
+Updated RPCs
------------
+Note: some low-level RPC changes mainly useful for testing are described
+in the Low-level Changes section below.
+
+- The `getpeerinfo` RPC now returns an additional "minfeefilter" field
+ set to the peer's BIP133 fee filter. You can use this to detect that
+ you have peers that are willing to accept transactions below the
+ default minimum relay fee.
+
+- The mempool RPCs, such as `getrawmempool` with `verbose=true`, now
+ return an additional "bip125-replaceable" value indicating whether the
+ transaction (or its unconfirmed ancestors) opts-in to asking nodes and
+ miners to replace it with a higher-feerate transaction spending any of
+ the same inputs.
+
+- The `settxfee` RPC previously silently ignored attempts to set the fee
+ below the allowed minimums. It now prints a warning. The special
+ value of "0" may still be used to request the minimum value.
+
+- The `getaddressinfo` RPC now provides an `ischange` field indicating
+ whether the wallet used the address in a change output.
+
+- The `importmulti` RPC has been updated to support P2WSH, P2WPKH,
+ P2SH-P2WPKH, and P2SH-P2WSH. Requests for P2WSH and P2SH-P2WSH accept
+ an additional `witnessscript` parameter.
+
+Low-level changes
+=================
+
+RPC
+---
+
+- The `submitblock` RPC previously returned the reason a rejected block
+ was invalid the first time it processed that block but returned a
+ generic "duplicate" rejection message on subsequent occasions it
+ processed the same block. It now always returns the fundamental
+ reason for rejecting an invalid block and only returns "duplicate" for
+ valid blocks it has already accepted.
+
+- A new `submitheader` RPC allows submitting block headers independently
+ from their block. This is likely only useful for testing.
+
+Configuration
+-------------
+
+- The `-usehd` configuration option was removed in version 0.16. From
+ that version onwards, all new wallets created are hierarchical
+ deterministic wallets. This release makes specifying `-usehd` an
+ invalid configuration option.
+
Credits
=======
diff --git a/share/setup.nsi.in b/share/setup.nsi.in
index b58a84e02d..6542370f97 100644
--- a/share/setup.nsi.in
+++ b/share/setup.nsi.in
@@ -80,6 +80,7 @@ Section -Main SEC0000
SetOutPath $INSTDIR\daemon
File @abs_top_srcdir@/release/@BITCOIN_DAEMON_NAME@@EXEEXT@
File @abs_top_srcdir@/release/@BITCOIN_CLI_NAME@@EXEEXT@
+ File @abs_top_srcdir@/release/@BITCOIN_TX_NAME@@EXEEXT@
SetOutPath $INSTDIR\doc
File /r /x Makefile* @abs_top_srcdir@/doc\*.*
SetOutPath $INSTDIR
diff --git a/src/prevector.h b/src/prevector.h
index 6ddb6f321f..aa77573746 100644
--- a/src/prevector.h
+++ b/src/prevector.h
@@ -10,6 +10,7 @@
#include <stdint.h>
#include <string.h>
+#include <algorithm>
#include <cstddef>
#include <iterator>
#include <type_traits>
@@ -198,22 +199,11 @@ private:
const T* item_ptr(difference_type pos) const { return is_direct() ? direct_ptr(pos) : indirect_ptr(pos); }
void fill(T* dst, ptrdiff_t count) {
- if (IS_TRIVIALLY_CONSTRUCTIBLE<T>::value) {
- // The most common use of prevector is where T=unsigned char. For
- // trivially constructible types, we can use memset() to avoid
- // looping.
- ::memset(dst, 0, count * sizeof(T));
- } else {
- for (auto i = 0; i < count; ++i) {
- new(static_cast<void*>(dst + i)) T();
- }
- }
+ std::fill_n(dst, count, T{});
}
void fill(T* dst, ptrdiff_t count, const T& value) {
- for (auto i = 0; i < count; ++i) {
- new(static_cast<void*>(dst + i)) T(value);
- }
+ std::fill_n(dst, count, value);
}
template<typename InputIterator>
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 7508f596e6..de236a016f 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -570,10 +570,8 @@ int main(int argc, char *argv[])
Q_INIT_RESOURCE(bitcoin_locale);
BitcoinApplication app(*node, argc, argv);
-#if QT_VERSION > 0x050100
// Generate high-dpi pixmaps
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
-#endif
#if QT_VERSION >= 0x050600
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp
index b68f3a439b..558fcf50ba 100644
--- a/src/qt/bitcoinamountfield.cpp
+++ b/src/qt/bitcoinamountfield.cpp
@@ -23,9 +23,7 @@ class AmountSpinBox: public QAbstractSpinBox
public:
explicit AmountSpinBox(QWidget *parent):
- QAbstractSpinBox(parent),
- currentUnit(BitcoinUnits::BTC),
- singleStep(100000) // satoshis
+ QAbstractSpinBox(parent)
{
setAlignment(Qt::AlignRight);
@@ -44,10 +42,19 @@ public:
void fixup(QString &input) const
{
- bool valid = false;
- CAmount val = parse(input, &valid);
- if(valid)
- {
+ bool valid;
+ CAmount val;
+
+ if (input.isEmpty() && !m_allow_empty) {
+ valid = true;
+ val = m_min_amount;
+ } else {
+ valid = false;
+ val = parse(input, &valid);
+ }
+
+ if (valid) {
+ val = qBound(m_min_amount, val, m_max_amount);
input = BitcoinUnits::format(currentUnit, val, false, BitcoinUnits::separatorAlways);
lineEdit()->setText(input);
}
@@ -64,12 +71,27 @@ public:
Q_EMIT valueChanged();
}
+ void SetAllowEmpty(bool allow)
+ {
+ m_allow_empty = allow;
+ }
+
+ void SetMinValue(const CAmount& value)
+ {
+ m_min_amount = value;
+ }
+
+ void SetMaxValue(const CAmount& value)
+ {
+ m_max_amount = value;
+ }
+
void stepBy(int steps)
{
bool valid = false;
CAmount val = value(&valid);
val = val + steps * singleStep;
- val = qMin(qMax(val, CAmount(0)), BitcoinUnits::maxMoney());
+ val = qBound(m_min_amount, val, m_max_amount);
setValue(val);
}
@@ -125,9 +147,12 @@ public:
}
private:
- int currentUnit;
- CAmount singleStep;
+ int currentUnit{BitcoinUnits::BTC};
+ CAmount singleStep{CAmount(100000)}; // satoshis
mutable QSize cachedMinimumSizeHint;
+ bool m_allow_empty{true};
+ CAmount m_min_amount{CAmount(0)};
+ CAmount m_max_amount{BitcoinUnits::maxMoney()};
/**
* Parse a string into a number of base monetary units and
@@ -174,11 +199,10 @@ protected:
StepEnabled rv = 0;
bool valid = false;
CAmount val = value(&valid);
- if(valid)
- {
- if(val > 0)
+ if (valid) {
+ if (val > m_min_amount)
rv |= StepDownEnabled;
- if(val < BitcoinUnits::maxMoney())
+ if (val < m_max_amount)
rv |= StepUpEnabled;
}
return rv;
@@ -275,6 +299,21 @@ void BitcoinAmountField::setValue(const CAmount& value)
amount->setValue(value);
}
+void BitcoinAmountField::SetAllowEmpty(bool allow)
+{
+ amount->SetAllowEmpty(allow);
+}
+
+void BitcoinAmountField::SetMinValue(const CAmount& value)
+{
+ amount->SetMinValue(value);
+}
+
+void BitcoinAmountField::SetMaxValue(const CAmount& value)
+{
+ amount->SetMaxValue(value);
+}
+
void BitcoinAmountField::setReadOnly(bool fReadOnly)
{
amount->setReadOnly(fReadOnly);
diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h
index f93579c492..650481e30d 100644
--- a/src/qt/bitcoinamountfield.h
+++ b/src/qt/bitcoinamountfield.h
@@ -31,6 +31,15 @@ public:
CAmount value(bool *value=0) const;
void setValue(const CAmount& value);
+ /** If allow empty is set to false the field will be set to the minimum allowed value if left empty. **/
+ void SetAllowEmpty(bool allow);
+
+ /** Set the minimum value in satoshis **/
+ void SetMinValue(const CAmount& value);
+
+ /** Set the maximum value in satoshis **/
+ void SetMaxValue(const CAmount& value);
+
/** Set single step in satoshis **/
void setSingleStep(const CAmount& step);
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 89b80bd5c9..ef82351551 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -92,12 +92,8 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
windowTitle += tr("Node");
}
windowTitle += " " + networkStyle->getTitleAddText();
-#ifndef Q_OS_MAC
QApplication::setWindowIcon(networkStyle->getTrayAndWindowIcon());
setWindowIcon(networkStyle->getTrayAndWindowIcon());
-#else
- MacDockIconHandler::instance()->setIcon(networkStyle->getAppIcon());
-#endif
setWindowTitle(windowTitle);
rpcConsole = new RPCConsole(node, _platformStyle, 0);
@@ -131,7 +127,9 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
createToolBars();
// Create system tray icon and notification
- createTrayIcon(networkStyle);
+ if (QSystemTrayIcon::isSystemTrayAvailable()) {
+ createTrayIcon(networkStyle);
+ }
// Create status bar
statusBar();
@@ -278,17 +276,17 @@ void BitcoinGUI::createActions()
#ifdef ENABLE_WALLET
// These showNormalIfMinimized are needed because Send Coins and Receive Coins
// can be triggered from the tray menu, and need to show the GUI to be useful.
- connect(overviewAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
+ connect(overviewAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(overviewAction, &QAction::triggered, this, &BitcoinGUI::gotoOverviewPage);
- connect(sendCoinsAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
+ connect(sendCoinsAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(sendCoinsAction, &QAction::triggered, [this]{ gotoSendCoinsPage(); });
- connect(sendCoinsMenuAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
+ connect(sendCoinsMenuAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(sendCoinsMenuAction, &QAction::triggered, [this]{ gotoSendCoinsPage(); });
- connect(receiveCoinsAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
+ connect(receiveCoinsAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(receiveCoinsAction, &QAction::triggered, this, &BitcoinGUI::gotoReceiveCoinsPage);
- connect(receiveCoinsMenuAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
+ connect(receiveCoinsMenuAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(receiveCoinsMenuAction, &QAction::triggered, this, &BitcoinGUI::gotoReceiveCoinsPage);
- connect(historyAction, &QAction::triggered, this, static_cast<void (BitcoinGUI::*)()>(&BitcoinGUI::showNormalIfMinimized));
+ connect(historyAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(historyAction, &QAction::triggered, this, &BitcoinGUI::gotoHistoryPage);
#endif // ENABLE_WALLET
@@ -355,7 +353,9 @@ void BitcoinGUI::createActions()
connect(encryptWalletAction, &QAction::triggered, walletFrame, &WalletFrame::encryptWallet);
connect(backupWalletAction, &QAction::triggered, walletFrame, &WalletFrame::backupWallet);
connect(changePassphraseAction, &QAction::triggered, walletFrame, &WalletFrame::changePassphrase);
+ connect(signMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(signMessageAction, &QAction::triggered, [this]{ gotoSignMessageTab(); });
+ connect(verifyMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(verifyMessageAction, &QAction::triggered, [this]{ gotoVerifyMessageTab(); });
connect(usedSendingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedSendingAddresses);
connect(usedReceivingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedReceivingAddresses);
@@ -590,6 +590,8 @@ void BitcoinGUI::setWalletActionsEnabled(bool enabled)
void BitcoinGUI::createTrayIcon(const NetworkStyle *networkStyle)
{
+ assert(QSystemTrayIcon::isSystemTrayAvailable());
+
#ifndef Q_OS_MAC
trayIcon = new QSystemTrayIcon(this);
QString toolTip = tr("%1 client").arg(tr(PACKAGE_NAME)) + " " + networkStyle->getTitleAddText();
@@ -604,7 +606,7 @@ void BitcoinGUI::createTrayIcon(const NetworkStyle *networkStyle)
void BitcoinGUI::createTrayIconMenu()
{
#ifndef Q_OS_MAC
- // return if trayIcon is unset (only on non-Mac OSes)
+ // return if trayIcon is unset (only on non-macOSes)
if (!trayIcon)
return;
@@ -613,15 +615,17 @@ void BitcoinGUI::createTrayIconMenu()
connect(trayIcon, &QSystemTrayIcon::activated, this, &BitcoinGUI::trayIconActivated);
#else
- // Note: On Mac, the dock icon is used to provide the tray's functionality.
+ // Note: On macOS, the Dock icon is used to provide the tray's functionality.
MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance();
- dockIconHandler->setMainWindow(static_cast<QMainWindow*>(this));
- trayIconMenu = dockIconHandler->dockMenu();
+ connect(dockIconHandler, &MacDockIconHandler::dockIconClicked, this, &BitcoinGUI::macosDockIconActivated);
+
+ trayIconMenu = new QMenu(this);
+ trayIconMenu->setAsDockMenu();
#endif
- // Configuration of the tray icon (or dock icon) icon menu
+ // Configuration of the tray icon (or Dock icon) menu
#ifndef Q_OS_MAC
- // Note: On Mac, the dock icon's menu already has show / hide action.
+ // Note: On macOS, the Dock icon's menu already has Show / Hide action.
trayIconMenu->addAction(toggleHideAction);
trayIconMenu->addSeparator();
#endif
@@ -635,7 +639,7 @@ void BitcoinGUI::createTrayIconMenu()
trayIconMenu->addAction(openRPCConsoleAction);
}
trayIconMenu->addAction(optionsAction);
-#ifndef Q_OS_MAC // This is built-in on Mac
+#ifndef Q_OS_MAC // This is built-in on macOS
trayIconMenu->addSeparator();
trayIconMenu->addAction(quitAction);
#endif
@@ -650,6 +654,12 @@ void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
toggleHidden();
}
}
+#else
+void BitcoinGUI::macosDockIconActivated()
+{
+ show();
+ activateWindow();
+}
#endif
void BitcoinGUI::optionsClicked()
@@ -668,10 +678,7 @@ void BitcoinGUI::aboutClicked()
void BitcoinGUI::showDebugWindow()
{
- rpcConsole->showNormal();
- rpcConsole->show();
- rpcConsole->raise();
- rpcConsole->activateWindow();
+ GUIUtil::bringToFront(rpcConsole);
}
void BitcoinGUI::showDebugWindowActivateConsole()
@@ -1158,24 +1165,11 @@ void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden)
if(!clientModel)
return;
- // activateWindow() (sometimes) helps with keyboard focus on Windows
- if (isHidden())
- {
- show();
- activateWindow();
- }
- else if (isMinimized())
- {
- showNormal();
- activateWindow();
- }
- else if (GUIUtil::isObscured(this))
- {
- raise();
- activateWindow();
- }
- else if(fToggleHidden)
+ if (!isHidden() && !isMinimized() && !GUIUtil::isObscured(this) && fToggleHidden) {
hide();
+ } else {
+ GUIUtil::bringToFront(this);
+ }
}
void BitcoinGUI::toggleHidden()
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 57b70803ae..e8b857c17c 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -268,6 +268,9 @@ public Q_SLOTS:
#ifndef Q_OS_MAC
/** Handle tray icon clicked */
void trayIconActivated(QSystemTrayIcon::ActivationReason reason);
+#else
+ /** Handle macOS Dock icon clicked */
+ void macosDockIconActivated();
#endif
/** Show window if hidden, unminimize when minimized, rise when obscured or show if hidden and fToggleHidden is true */
diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui
index 6b31ddea90..386d559281 100644
--- a/src/qt/forms/sendcoinsdialog.ui
+++ b/src/qt/forms/sendcoinsdialog.ui
@@ -878,28 +878,15 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<item>
<layout class="QHBoxLayout" name="horizontalLayoutFee8">
<item>
- <widget class="QCheckBox" name="checkBoxMinimumFee">
- <property name="toolTip">
- <string>Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks. But be aware that this can end up in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</string>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLabel" name="labelMinFeeWarning">
+ <widget class="QLabel" name="labelCustomFeeWarning">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
- <string>Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks. But be aware that this can end up in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</string>
+ <string>When there is less transaction volume than space in the blocks, miners as well as relaying nodes may enforce a minimum fee. Paying only this minimum fee is just fine, but be aware that this can result in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</string>
</property>
<property name="text">
- <string>(read the tooltip)</string>
- </property>
- <property name="margin">
- <number>5</number>
+ <string>A too low fee might result in a never confirming transaction (read the tooltip)</string>
</property>
</widget>
</item>
@@ -992,9 +979,6 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<property name="text">
<string/>
</property>
- <property name="margin">
- <number>2</number>
- </property>
</widget>
</item>
<item>
@@ -1009,9 +993,6 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<property name="text">
<string>(Smart fee not initialized yet. This usually takes a few blocks...)</string>
</property>
- <property name="margin">
- <number>2</number>
- </property>
</widget>
</item>
<item>
@@ -1038,24 +1019,8 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<property name="text">
<string>Confirmation time target:</string>
</property>
- <property name="margin">
- <number>2</number>
- </property>
</widget>
</item>
- <item>
- <spacer name="verticalSpacer_3">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>1</width>
- <height>1</height>
- </size>
- </property>
- </spacer>
- </item>
</layout>
</item>
<item>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index a68140ccf9..0e9aca21b1 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -47,6 +47,7 @@
#include <QDoubleValidator>
#include <QFileDialog>
#include <QFont>
+#include <QFontDatabase>
#include <QKeyEvent>
#include <QLineEdit>
#include <QSettings>
@@ -55,9 +56,12 @@
#include <QUrlQuery>
#include <QMouseEvent>
+#if defined(Q_OS_MAC)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#if QT_VERSION >= 0x50200
-#include <QFontDatabase>
+#include <objc/objc-runtime.h>
+#include <CoreServices/CoreServices.h>
#endif
namespace GUIUtil {
@@ -74,13 +78,7 @@ QString dateTimeStr(qint64 nTime)
QFont fixedPitchFont()
{
-#if QT_VERSION >= 0x50200
return QFontDatabase::systemFont(QFontDatabase::FixedFont);
-#else
- QFont font("Monospace");
- font.setStyleHint(QFont::Monospace);
- return font;
-#endif
}
// Just some dummy data to generate a convincing random-looking (but consistent) address
@@ -353,6 +351,27 @@ bool isObscured(QWidget *w)
&& checkPoint(QPoint(w->width() / 2, w->height() / 2), w));
}
+void bringToFront(QWidget* w)
+{
+#ifdef Q_OS_MAC
+ // Force application activation on macOS. With Qt 5.4 this is required when
+ // an action in the dock menu is triggered.
+ id app = objc_msgSend((id) objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
+ objc_msgSend(app, sel_registerName("activateIgnoringOtherApps:"), YES);
+#endif
+
+ if (w) {
+ // activateWindow() (sometimes) helps with keyboard focus on Windows
+ if (w->isMinimized()) {
+ w->showNormal();
+ } else {
+ w->show();
+ }
+ w->activateWindow();
+ w->raise();
+ }
+}
+
void openDebugLogfile()
{
fs::path pathDebug = GetDataDir() / "debug.log";
@@ -663,13 +682,8 @@ bool SetStartOnSystemStartup(bool fAutoStart)
#elif defined(Q_OS_MAC)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
// based on: https://github.com/Mozketo/LaunchAtLoginController/blob/master/LaunchAtLoginController.m
-#include <CoreFoundation/CoreFoundation.h>
-#include <CoreServices/CoreServices.h>
-
LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl);
LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl)
{
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 011827e134..f1d0aa48ef 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -115,6 +115,9 @@ namespace GUIUtil
// Determine whether a widget is hidden behind other windows
bool isObscured(QWidget *w);
+ // Activate, show and raise the widget
+ void bringToFront(QWidget* w);
+
// Open debug.log
void openDebugLogfile();
diff --git a/src/qt/macdockiconhandler.h b/src/qt/macdockiconhandler.h
index 1c28593d4a..ff867e21a7 100644
--- a/src/qt/macdockiconhandler.h
+++ b/src/qt/macdockiconhandler.h
@@ -1,44 +1,27 @@
-// Copyright (c) 2011-2015 The Bitcoin Core developers
+// Copyright (c) 2011-2018 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_MACDOCKICONHANDLER_H
#define BITCOIN_QT_MACDOCKICONHANDLER_H
-#include <QMainWindow>
#include <QObject>
-QT_BEGIN_NAMESPACE
-class QIcon;
-class QMenu;
-class QWidget;
-QT_END_NAMESPACE
-
-/** Macintosh-specific dock icon handler.
+/** macOS-specific Dock icon handler.
*/
class MacDockIconHandler : public QObject
{
Q_OBJECT
public:
- ~MacDockIconHandler();
-
- QMenu *dockMenu();
- void setIcon(const QIcon &icon);
- void setMainWindow(QMainWindow *window);
static MacDockIconHandler *instance();
static void cleanup();
- void handleDockIconClickEvent();
Q_SIGNALS:
void dockIconClicked();
private:
MacDockIconHandler();
-
- QWidget *m_dummyWidget;
- QMenu *m_dockMenu;
- QMainWindow *mainWindow;
};
#endif // BITCOIN_QT_MACDOCKICONHANDLER_H
diff --git a/src/qt/macdockiconhandler.mm b/src/qt/macdockiconhandler.mm
index b9ad191da7..102adce6c5 100644
--- a/src/qt/macdockiconhandler.mm
+++ b/src/qt/macdockiconhandler.mm
@@ -1,107 +1,36 @@
-// Copyright (c) 2011-2013 The Bitcoin Core developers
+// Copyright (c) 2011-2018 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 "macdockiconhandler.h"
-#include <QImageWriter>
-#include <QMenu>
-#include <QBuffer>
-#include <QWidget>
-
#undef slots
-#include <Cocoa/Cocoa.h>
#include <objc/objc.h>
#include <objc/message.h>
static MacDockIconHandler *s_instance = nullptr;
-bool dockClickHandler(id self,SEL _cmd,...) {
+bool dockClickHandler(id self, SEL _cmd, ...) {
Q_UNUSED(self)
Q_UNUSED(_cmd)
- s_instance->handleDockIconClickEvent();
+ Q_EMIT s_instance->dockIconClicked();
- // Return NO (false) to suppress the default OS X actions
+ // Return NO (false) to suppress the default macOS actions
return false;
}
void setupDockClickHandler() {
- Class cls = objc_getClass("NSApplication");
- id appInst = objc_msgSend((id)cls, sel_registerName("sharedApplication"));
-
- if (appInst != nullptr) {
- id delegate = objc_msgSend(appInst, sel_registerName("delegate"));
- Class delClass = (Class)objc_msgSend(delegate, sel_registerName("class"));
- SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:");
- if (class_getInstanceMethod(delClass, shouldHandle))
- class_replaceMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B@:");
- else
- class_addMethod(delClass, shouldHandle, (IMP)dockClickHandler,"B@:");
- }
+ id app = objc_msgSend((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
+ id delegate = objc_msgSend(app, sel_registerName("delegate"));
+ Class delClass = (Class)objc_msgSend(delegate, sel_registerName("class"));
+ SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:");
+ class_replaceMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B@:");
}
-
MacDockIconHandler::MacDockIconHandler() : QObject()
{
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
setupDockClickHandler();
- this->m_dummyWidget = new QWidget();
- this->m_dockMenu = new QMenu(this->m_dummyWidget);
- this->setMainWindow(nullptr);
-#if QT_VERSION >= 0x050200
- this->m_dockMenu->setAsDockMenu();
-#endif
- [pool release];
-}
-
-void MacDockIconHandler::setMainWindow(QMainWindow *window) {
- this->mainWindow = window;
-}
-
-MacDockIconHandler::~MacDockIconHandler()
-{
- delete this->m_dummyWidget;
- this->setMainWindow(nullptr);
-}
-
-QMenu *MacDockIconHandler::dockMenu()
-{
- return this->m_dockMenu;
-}
-
-void MacDockIconHandler::setIcon(const QIcon &icon)
-{
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- NSImage *image = nil;
- if (icon.isNull())
- image = [[NSImage imageNamed:@"NSApplicationIcon"] retain];
- else {
- // generate NSImage from QIcon and use this as dock icon.
- QSize size = icon.actualSize(QSize(128, 128));
- QPixmap pixmap = icon.pixmap(size);
-
- // Write image into a R/W buffer from raw pixmap, then save the image.
- QBuffer notificationBuffer;
- if (!pixmap.isNull() && notificationBuffer.open(QIODevice::ReadWrite)) {
- QImageWriter writer(&notificationBuffer, "PNG");
- if (writer.write(pixmap.toImage())) {
- NSData* macImgData = [NSData dataWithBytes:notificationBuffer.buffer().data()
- length:notificationBuffer.buffer().size()];
- image = [[NSImage alloc] initWithData:macImgData];
- }
- }
-
- if(!image) {
- // if testnet image could not be created, load std. app icon
- image = [[NSImage imageNamed:@"NSApplicationIcon"] retain];
- }
- }
-
- [NSApp setApplicationIconImage:image];
- [image release];
- [pool release];
}
MacDockIconHandler *MacDockIconHandler::instance()
@@ -115,14 +44,3 @@ void MacDockIconHandler::cleanup()
{
delete s_instance;
}
-
-void MacDockIconHandler::handleDockIconClickEvent()
-{
- if (this->mainWindow)
- {
- this->mainWindow->activateWindow();
- this->mainWindow->show();
- }
-
- Q_EMIT this->dockIconClicked();
-}
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index b51322394f..c9871f6c66 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -23,6 +23,7 @@
#include <QIntValidator>
#include <QLocale>
#include <QMessageBox>
+#include <QSystemTrayIcon>
#include <QTimer>
OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
@@ -126,6 +127,13 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
connect(ui->proxyIpTor, &QValidatedLineEdit::validationDidChange, this, &OptionsDialog::updateProxyValidationState);
connect(ui->proxyPort, &QLineEdit::textChanged, this, &OptionsDialog::updateProxyValidationState);
connect(ui->proxyPortTor, &QLineEdit::textChanged, this, &OptionsDialog::updateProxyValidationState);
+
+ if (!QSystemTrayIcon::isSystemTrayAvailable()) {
+ ui->hideTrayIcon->setChecked(true);
+ ui->hideTrayIcon->setEnabled(false);
+ ui->minimizeToTray->setChecked(false);
+ ui->minimizeToTray->setEnabled(false);
+ }
}
OptionsDialog::~OptionsDialog()
@@ -211,8 +219,10 @@ void OptionsDialog::setMapper()
/* Window */
#ifndef Q_OS_MAC
- mapper->addMapping(ui->hideTrayIcon, OptionsModel::HideTrayIcon);
- mapper->addMapping(ui->minimizeToTray, OptionsModel::MinimizeToTray);
+ if (QSystemTrayIcon::isSystemTrayAvailable()) {
+ mapper->addMapping(ui->hideTrayIcon, OptionsModel::HideTrayIcon);
+ mapper->addMapping(ui->minimizeToTray, OptionsModel::MinimizeToTray);
+ }
mapper->addMapping(ui->minimizeOnClose, OptionsModel::MinimizeOnClose);
#endif
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 858128f9f9..65db0280b7 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -119,13 +119,11 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p
settings.setValue("nSmartFeeSliderPosition", 0);
if (!settings.contains("nTransactionFee"))
settings.setValue("nTransactionFee", (qint64)DEFAULT_PAY_TX_FEE);
- if (!settings.contains("fPayOnlyMinFee"))
- settings.setValue("fPayOnlyMinFee", false);
ui->groupFee->setId(ui->radioSmartFee, 0);
ui->groupFee->setId(ui->radioCustomFee, 1);
ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true);
+ ui->customFee->SetAllowEmpty(false);
ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
- ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool());
minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
}
@@ -174,14 +172,15 @@ void SendCoinsDialog::setModel(WalletModel *_model)
connect(ui->groupFee, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), this, &SendCoinsDialog::updateFeeSectionControls);
connect(ui->groupFee, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), this, &SendCoinsDialog::coinControlUpdateLabels);
connect(ui->customFee, &BitcoinAmountField::valueChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
- connect(ui->checkBoxMinimumFee, &QCheckBox::stateChanged, this, &SendCoinsDialog::setMinimumFee);
- connect(ui->checkBoxMinimumFee, &QCheckBox::stateChanged, this, &SendCoinsDialog::updateFeeSectionControls);
- connect(ui->checkBoxMinimumFee, &QCheckBox::stateChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
connect(ui->optInRBF, &QCheckBox::stateChanged, this, &SendCoinsDialog::updateSmartFeeLabel);
connect(ui->optInRBF, &QCheckBox::stateChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
- ui->customFee->setSingleStep(model->wallet().getRequiredFee(1000));
+ CAmount requiredFee = model->wallet().getRequiredFee(1000);
+ ui->customFee->SetMinValue(requiredFee);
+ if (ui->customFee->value() < requiredFee) {
+ ui->customFee->setValue(requiredFee);
+ }
+ ui->customFee->setSingleStep(requiredFee);
updateFeeSectionControls();
- updateMinFeeLabel();
updateSmartFeeLabel();
// set default rbf checkbox state
@@ -210,7 +209,6 @@ SendCoinsDialog::~SendCoinsDialog()
settings.setValue("nFeeRadio", ui->groupFee->checkedId());
settings.setValue("nConfTarget", getConfTargetForIndex(ui->confTargetSelector->currentIndex()));
settings.setValue("nTransactionFee", (qint64)ui->customFee->value());
- settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked());
delete ui;
}
@@ -542,7 +540,6 @@ void SendCoinsDialog::updateDisplayUnit()
{
setBalance(model->wallet().getBalances());
ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
- updateMinFeeLabel();
updateSmartFeeLabel();
}
@@ -642,11 +639,6 @@ void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry)
}
}
-void SendCoinsDialog::setMinimumFee()
-{
- ui->customFee->setValue(model->wallet().getRequiredFee(1000));
-}
-
void SendCoinsDialog::updateFeeSectionControls()
{
ui->confTargetSelector ->setEnabled(ui->radioSmartFee->isChecked());
@@ -654,10 +646,9 @@ void SendCoinsDialog::updateFeeSectionControls()
ui->labelSmartFee2 ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelSmartFee3 ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked());
- ui->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked());
- ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
- ui->labelCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
- ui->customFee ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
+ ui->labelCustomFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
+ ui->labelCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked());
+ ui->customFee ->setEnabled(ui->radioCustomFee->isChecked());
}
void SendCoinsDialog::updateFeeMinimizedLabel()
@@ -672,14 +663,6 @@ void SendCoinsDialog::updateFeeMinimizedLabel()
}
}
-void SendCoinsDialog::updateMinFeeLabel()
-{
- if (model && model->getOptionsModel())
- ui->checkBoxMinimumFee->setText(tr("Pay only the required fee of %1").arg(
- BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->wallet().getRequiredFee(1000)) + "/kB")
- );
-}
-
void SendCoinsDialog::updateCoinControlState(CCoinControl& ctrl)
{
if (ui->radioCustomFee->isChecked()) {
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index 7009855f17..e1ebc77d59 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -92,9 +92,7 @@ private Q_SLOTS:
void coinControlClipboardBytes();
void coinControlClipboardLowOutput();
void coinControlClipboardChange();
- void setMinimumFee();
void updateFeeSectionControls();
- void updateMinFeeLabel();
void updateSmartFeeLabel();
Q_SIGNALS:
diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp
index b6235e91f7..df38285d08 100644
--- a/src/qt/splashscreen.cpp
+++ b/src/qt/splashscreen.cpp
@@ -37,9 +37,7 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
float fontFactor = 1.0;
float devicePixelRatio = 1.0;
-#if QT_VERSION > 0x050100
devicePixelRatio = static_cast<QGuiApplication*>(QCoreApplication::instance())->devicePixelRatio();
-#endif
// define text to place
QString titleText = tr(PACKAGE_NAME);
@@ -53,10 +51,8 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
QSize splashSize(480*devicePixelRatio,320*devicePixelRatio);
pixmap = QPixmap(splashSize);
-#if QT_VERSION > 0x050100
// change to HiDPI if it makes sense
pixmap.setDevicePixelRatio(devicePixelRatio);
-#endif
QPainter pixPaint(&pixmap);
pixPaint.setPen(QColor(100,100,100));
diff --git a/src/qt/test/util.h b/src/qt/test/util.h
index 5363c94547..377f07dcba 100644
--- a/src/qt/test/util.h
+++ b/src/qt/test/util.h
@@ -1,6 +1,8 @@
#ifndef BITCOIN_QT_TEST_UTIL_H
#define BITCOIN_QT_TEST_UTIL_H
+#include <QString>
+
/**
* Press "Ok" button in message box dialog.
*
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 053e951921..a619992344 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -292,9 +292,7 @@ void WalletView::usedSendingAddresses()
if(!walletModel)
return;
- usedSendingAddressesPage->show();
- usedSendingAddressesPage->raise();
- usedSendingAddressesPage->activateWindow();
+ GUIUtil::bringToFront(usedSendingAddressesPage);
}
void WalletView::usedReceivingAddresses()
@@ -302,9 +300,7 @@ void WalletView::usedReceivingAddresses()
if(!walletModel)
return;
- usedReceivingAddressesPage->show();
- usedReceivingAddressesPage->raise();
- usedReceivingAddressesPage->activateWindow();
+ GUIUtil::bringToFront(usedReceivingAddressesPage);
}
void WalletView::showProgress(const QString &title, int nProgress)
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 92f6f0fe11..95915b5488 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -12,8 +12,8 @@
#include <checkpoints.h>
#include <coins.h>
#include <consensus/validation.h>
-#include <validation.h>
#include <core_io.h>
+#include <hash.h>
#include <index/txindex.h>
#include <key_io.h>
#include <policy/feerate.h>
@@ -21,14 +21,15 @@
#include <policy/rbf.h>
#include <primitives/transaction.h>
#include <rpc/server.h>
+#include <rpc/util.h>
#include <script/descriptor.h>
#include <streams.h>
#include <sync.h>
#include <txdb.h>
#include <txmempool.h>
-#include <util/system.h>
#include <util/strencodings.h>
-#include <hash.h>
+#include <util/system.h>
+#include <validation.h>
#include <validationinterface.h>
#include <versionbitsinfo.h>
#include <warnings.h>
@@ -286,12 +287,12 @@ static UniValue waitforblockheight(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "waitforblockheight <height> (timeout)\n"
+ "waitforblockheight height ( timeout )\n"
"\nWaits for (at least) block height and returns the height and hash\n"
"of the current tip.\n"
"\nReturns the current block on timeout or exit.\n"
"\nArguments:\n"
- "1. height (required, int) Block height to wait for (int)\n"
+ "1. height (int, required) Block height to wait for.\n"
"2. timeout (int, optional, default=0) Time in milliseconds to wait for a response. 0 indicates no timeout.\n"
"\nResult:\n"
"{ (json object)\n"
@@ -695,11 +696,11 @@ static UniValue getblockheader(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "getblockheader \"hash\" ( verbose )\n"
+ "getblockheader \"blockhash\" ( verbose )\n"
"\nIf verbose is false, returns a string that is serialized, hex-encoded data for blockheader 'hash'.\n"
"If verbose is true, returns an Object with information about blockheader <hash>.\n"
"\nArguments:\n"
- "1. \"hash\" (string, required) The block hash\n"
+ "1. \"blockhash\" (string, required) The block hash\n"
"2. verbose (boolean, optional, default=true) true for a json object, false for the hex-encoded data\n"
"\nResult (for verbose = true):\n"
"{\n"
@@ -925,7 +926,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "pruneblockchain\n"
+ "pruneblockchain height\n"
"\nArguments:\n"
"1. \"height\" (numeric, required) The block height to prune up to. May be set to a discrete height, or a unix timestamp\n"
" to prune blocks whose block time is at least 2 hours older than the provided timestamp.\n"
@@ -1562,7 +1563,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 2)
throw std::runtime_error(
- "getchaintxstats ( nblocks blockhash )\n"
+ "getchaintxstats ( nblocks \"blockhash\" )\n"
"\nCompute statistics about the total number and rate of transactions in the chain.\n"
"\nArguments:\n"
"1. nblocks (numeric, optional) Size of the window in number of blocks (default: one month).\n"
@@ -2039,7 +2040,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "scantxoutset <action> ( <scanobjects> )\n"
+ "scantxoutset \"action\" [scanobjects,...]\n"
"\nEXPERIMENTAL warning: this call may be removed or changed in future releases.\n"
"\nScans the unspent transaction output set for entries that match certain output descriptors.\n"
"Examples of output descriptors are:\n"
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 58b54c66de..4f314ef215 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -18,10 +18,11 @@
#include <rpc/blockchain.h>
#include <rpc/mining.h>
#include <rpc/server.h>
+#include <rpc/util.h>
#include <shutdown.h>
#include <txmempool.h>
-#include <util/system.h>
#include <util/strencodings.h>
+#include <util/system.h>
#include <validation.h>
#include <validationinterface.h>
#include <versionbitsinfo.h>
@@ -231,7 +232,7 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 3)
throw std::runtime_error(
- "prioritisetransaction <txid> <dummy value> <fee delta>\n"
+ "prioritisetransaction \"txid\" dummy fee_delta\n"
"Accepts the transaction into mined blocks at a higher (or lower) priority\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id.\n"
@@ -294,7 +295,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1)
throw std::runtime_error(
- "getblocktemplate ( TemplateRequest )\n"
+ "getblocktemplate ( \"template_request\" )\n"
"\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n"
"It returns data needed to construct a block to work on.\n"
"For full specification, see BIPs 22, 23, 9, and 145:\n"
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 4d893bb838..fc1498a224 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -7,17 +7,18 @@
#include <chainparams.h>
#include <clientversion.h>
#include <core_io.h>
-#include <validation.h>
#include <net.h>
#include <net_processing.h>
#include <netbase.h>
#include <policy/policy.h>
#include <rpc/protocol.h>
+#include <rpc/util.h>
#include <sync.h>
#include <timedata.h>
#include <ui_interface.h>
-#include <util/system.h>
#include <util/strencodings.h>
+#include <util/system.h>
+#include <validation.h>
#include <version.h>
#include <warnings.h>
@@ -200,7 +201,7 @@ static UniValue addnode(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 2 ||
(strCommand != "onetry" && strCommand != "add" && strCommand != "remove"))
throw std::runtime_error(
- "addnode \"node\" \"add|remove|onetry\"\n"
+ "addnode \"node\" \"command\"\n"
"\nAttempts to add or remove a node from the addnode list.\n"
"Or try a connection to a node once.\n"
"Nodes added using addnode (or -connect) are protected from DoS disconnection and are not required to be\n"
@@ -243,13 +244,13 @@ static UniValue disconnectnode(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() == 0 || request.params.size() >= 3)
throw std::runtime_error(
- "disconnectnode \"[address]\" [nodeid]\n"
+ "disconnectnode ( \"address\" nodeid )\n"
"\nImmediately disconnects from the specified peer node.\n"
"\nStrictly one out of 'address' and 'nodeid' can be provided to identify the node.\n"
"\nTo disconnect by nodeid, either set 'address' to the empty string, or call using the named 'nodeid' argument only.\n"
"\nArguments:\n"
"1. \"address\" (string, optional) The IP address/port of the node\n"
- "2. \"nodeid\" (number, optional) The node ID (see getpeerinfo for node IDs)\n"
+ "2. nodeid (number, optional) The node ID (see getpeerinfo for node IDs)\n"
"\nExamples:\n"
+ HelpExampleCli("disconnectnode", "\"192.168.0.6:8333\"")
+ HelpExampleCli("disconnectnode", "\"\" 1")
@@ -499,7 +500,7 @@ static UniValue setban(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 2 ||
(strCommand != "add" && strCommand != "remove"))
throw std::runtime_error(
- "setban \"subnet\" \"add|remove\" (bantime) (absolute)\n"
+ "setban \"subnet\" \"command\" ( bantime absolute )\n"
"\nAttempts to add or remove an IP/Subnet from the banned list.\n"
"\nArguments:\n"
"1. \"subnet\" (string, required) The IP/Subnet (see getpeerinfo for nodes IP) with an optional netmask (default is /32 = single IP)\n"
@@ -610,7 +611,7 @@ static UniValue setnetworkactive(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
- "setnetworkactive true|false\n"
+ "setnetworkactive state\n"
"\nDisable/enable all p2p network activity.\n"
"\nArguments:\n"
"1. \"state\" (boolean, required) true to enable networking, false to disable\n"
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 94a9ea20da..480cb0b9d2 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -10,10 +10,8 @@
#include <core_io.h>
#include <index/txindex.h>
#include <init.h>
-#include <keystore.h>
-#include <validation.h>
-#include <validationinterface.h>
#include <key_io.h>
+#include <keystore.h>
#include <merkleblock.h>
#include <net.h>
#include <policy/policy.h>
@@ -29,6 +27,8 @@
#include <txmempool.h>
#include <uint256.h>
#include <util/strencodings.h>
+#include <validation.h>
+#include <validationinterface.h>
#include <future>
#include <stdint.h>
@@ -206,7 +206,16 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
{
if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2))
throw std::runtime_error(
- "gettxoutproof [\"txid\",...] ( blockhash )\n"
+ RPCHelpMan{"gettxoutproof",
+ {
+ {"txids", RPCArg::Type::ARR,
+ {
+ {"txid", RPCArg::Type::STR_HEX, false},
+ },
+ false},
+ {"blockhash", RPCArg::Type::STR_HEX, true},
+ }}
+ .ToString() +
"\nReturns a hex-encoded proof that \"txid\" was included in a block.\n"
"\nNOTE: By default this function only works sometimes. This is when there is an\n"
"unspent output in the utxo for this transaction. To make it always work,\n"
@@ -673,10 +682,17 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::
static UniValue combinerawtransaction(const JSONRPCRequest& request)
{
-
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "combinerawtransaction [\"hexstring\",...]\n"
+ RPCHelpMan{"combinerawtransaction",
+ {
+ {"txs", RPCArg::Type::ARR,
+ {
+ {"hexstring", RPCArg::Type::STR_HEX, false},
+ },
+ false},
+ }}
+ .ToString() +
"\nCombine multiple partially signed transactions into one transaction.\n"
"The combined transaction may be another partially signed transaction or a \n"
"fully signed transaction."
@@ -899,7 +915,30 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
throw std::runtime_error(
- "signrawtransactionwithkey \"hexstring\" [\"privatekey1\",...] ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] sighashtype )\n"
+ RPCHelpMan{"signrawtransactionwithkey",
+ {
+ {"hexstring", RPCArg::Type::STR, false},
+ {"privkyes", RPCArg::Type::ARR,
+ {
+ {"privatekey", RPCArg::Type::STR_HEX, false},
+ },
+ false},
+ {"prevtxs", RPCArg::Type::ARR,
+ {
+ {"", RPCArg::Type::OBJ,
+ {
+ {"txid", RPCArg::Type::STR_HEX, false},
+ {"vout", RPCArg::Type::NUM, false},
+ {"scriptPubKey", RPCArg::Type::STR_HEX, false},
+ {"redeemScript", RPCArg::Type::STR_HEX, false},
+ {"amount", RPCArg::Type::AMOUNT, false},
+ },
+ true},
+ },
+ true},
+ {"sighashtype", RPCArg::Type::STR, true},
+ }}
+ .ToString() +
"\nSign inputs for raw transaction (serialized, hex-encoded).\n"
"The second argument is an array of base58-encoded private\n"
"keys that will be the only keys used to sign the transaction.\n"
@@ -1454,7 +1493,15 @@ UniValue combinepsbt(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
- "combinepsbt [\"psbt\",...]\n"
+ RPCHelpMan{"combinepsbt",
+ {
+ {"txs", RPCArg::Type::ARR,
+ {
+ {"psbt", RPCArg::Type::STR_HEX, false},
+ },
+ false},
+ }}
+ .ToString() +
"\nCombine multiple partially signed Bitcoin transactions into one transaction.\n"
"Implements the Combiner role.\n"
"\nArguments:\n"
@@ -1571,7 +1618,37 @@ UniValue createpsbt(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
throw std::runtime_error(
- "createpsbt [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable )\n"
+ RPCHelpMan{"createpsbt",
+ {
+ {"inputs", RPCArg::Type::ARR,
+ {
+ {"", RPCArg::Type::OBJ,
+ {
+ {"txid", RPCArg::Type::STR_HEX, false},
+ {"vout", RPCArg::Type::NUM, false},
+ {"sequence", RPCArg::Type::NUM, true},
+ },
+ false},
+ },
+ false},
+ {"outputs", RPCArg::Type::ARR,
+ {
+ {"", RPCArg::Type::OBJ,
+ {
+ {"address", RPCArg::Type::AMOUNT, false},
+ },
+ true},
+ {"", RPCArg::Type::OBJ,
+ {
+ {"data", RPCArg::Type::STR_HEX, false},
+ },
+ true},
+ },
+ false},
+ {"locktime", RPCArg::Type::NUM, true},
+ {"replaceable", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nCreates a transaction in the Partially Signed Transaction format.\n"
"Implements the Creator role.\n"
"\nArguments:\n"
@@ -1672,7 +1749,7 @@ UniValue converttopsbt(const JSONRPCRequest& request)
// Remove all scriptSigs and scriptWitnesses from inputs
for (CTxIn& input : tx.vin) {
- if ((!input.scriptSig.empty() || !input.scriptWitness.IsNull()) && (request.params[1].isNull() || (!request.params[1].isNull() && request.params[1].get_bool()))) {
+ if ((!input.scriptSig.empty() || !input.scriptWitness.IsNull()) && !permitsigdata) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Inputs must not have scriptSigs and scriptWitnesses");
}
input.scriptSig.clear();
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 78abd7b610..f890baba51 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -8,11 +8,12 @@
#include <fs.h>
#include <key_io.h>
#include <random.h>
+#include <rpc/util.h>
#include <shutdown.h>
#include <sync.h>
#include <ui_interface.h>
-#include <util/system.h>
#include <util/strencodings.h>
+#include <util/system.h>
#include <boost/bind.hpp>
#include <boost/signals2/signal.hpp>
@@ -234,7 +235,7 @@ UniValue stop(const JSONRPCRequest& jsonRequest)
static UniValue uptime(const JSONRPCRequest& jsonRequest)
{
- if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
+ if (jsonRequest.fHelp || jsonRequest.params.size() > 0)
throw std::runtime_error(
"uptime\n"
"\nReturns the total uptime of the server.\n"
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index ef2d14b90e..6f2450708a 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -128,3 +128,95 @@ UniValue DescribeAddress(const CTxDestination& dest)
{
return boost::apply_visitor(DescribeAddressVisitor(), dest);
}
+
+std::string RPCHelpMan::ToString() const
+{
+ std::string ret;
+
+ ret += m_name;
+ bool is_optional{false};
+ for (const auto& arg : m_args) {
+ ret += " ";
+ if (arg.m_optional) {
+ if (!is_optional) ret += "( ";
+ is_optional = true;
+ } else {
+ // Currently we still support unnamed arguments, so any argument following an optional argument must also be optional
+ // If support for positional arguments is deprecated in the future, remove this line
+ assert(!is_optional);
+ }
+ ret += arg.ToString();
+ }
+ if (is_optional) ret += " )";
+ ret += "\n";
+
+ return ret;
+}
+
+std::string RPCArg::ToStringObj() const
+{
+ std::string res = "\"" + m_name + "\":";
+ switch (m_type) {
+ case Type::STR:
+ return res + "\"str\"";
+ case Type::STR_HEX:
+ return res + "\"hex\"";
+ case Type::NUM:
+ return res + "n";
+ case Type::AMOUNT:
+ return res + "amount";
+ case Type::BOOL:
+ return res + "bool";
+ case Type::ARR:
+ res += "[";
+ for (const auto& i : m_inner) {
+ res += i.ToString() + ",";
+ }
+ return res + "...]";
+ case Type::OBJ:
+ case Type::OBJ_USER_KEYS:
+ // Currently unused, so avoid writing dead code
+ assert(false);
+
+ // no default case, so the compiler can warn about missing cases
+ }
+ assert(false);
+}
+
+std::string RPCArg::ToString() const
+{
+ switch (m_type) {
+ case Type::STR_HEX:
+ case Type::STR: {
+ return "\"" + m_name + "\"";
+ }
+ case Type::NUM:
+ case Type::AMOUNT:
+ case Type::BOOL: {
+ return m_name;
+ }
+ case Type::OBJ:
+ case Type::OBJ_USER_KEYS: {
+ std::string res;
+ for (size_t i = 0; i < m_inner.size();) {
+ res += m_inner[i].ToStringObj();
+ if (++i < m_inner.size()) res += ",";
+ }
+ if (m_type == Type::OBJ) {
+ return "{" + res + "}";
+ } else {
+ return "{" + res + ",...}";
+ }
+ }
+ case Type::ARR: {
+ std::string res;
+ for (const auto& i : m_inner) {
+ res += i.ToString() + ",";
+ }
+ return "[" + res + "...]";
+ }
+
+ // no default case, so the compiler can warn about missing cases
+ }
+ assert(false);
+}
diff --git a/src/rpc/util.h b/src/rpc/util.h
index e21b5ba22a..55ae2fa363 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -30,4 +30,53 @@ CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey
UniValue DescribeAddress(const CTxDestination& dest);
+struct RPCArg {
+ enum class Type {
+ OBJ,
+ ARR,
+ STR,
+ NUM,
+ BOOL,
+ OBJ_USER_KEYS, //!< Special type where the user must set the keys e.g. to define multiple addresses; as opposed to e.g. an options object where the keys are predefined
+ AMOUNT, //!< Special type representing a floating point amount (can be either NUM or STR)
+ STR_HEX, //!< Special type that is a STR with only hex chars
+ };
+ const std::string m_name; //!< The name of the arg (can be empty for inner args)
+ const Type m_type;
+ const std::vector<RPCArg> m_inner; //!< Only used for arrays or dicts
+ const bool m_optional;
+
+ RPCArg(const std::string& name, const Type& type, const bool optional)
+ : m_name{name}, m_type{type}, m_optional{optional}
+ {
+ assert(type != Type::ARR && type != Type::OBJ);
+ }
+
+ RPCArg(const std::string& name, const Type& type, const std::vector<RPCArg>& inner, const bool optional)
+ : m_name{name}, m_type{type}, m_inner{inner}, m_optional{optional}
+ {
+ assert(type == Type::ARR || type == Type::OBJ);
+ }
+
+ std::string ToString() const;
+
+private:
+ std::string ToStringObj() const;
+};
+
+class RPCHelpMan
+{
+public:
+ RPCHelpMan(const std::string& name, const std::vector<RPCArg>& args)
+ : m_name{name}, m_args{args}
+ {
+ }
+
+ std::string ToString() const;
+
+private:
+ const std::string m_name;
+ const std::vector<RPCArg> m_args;
+};
+
#endif // BITCOIN_RPC_UTIL_H
diff --git a/src/script/sign.h b/src/script/sign.h
index e71f43f96d..a478f49789 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -206,6 +206,9 @@ template<typename Stream>
void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, KeyOriginInfo>& hd_keypaths, uint8_t type)
{
for (auto keypath_pair : hd_keypaths) {
+ if (!keypath_pair.first.IsValid()) {
+ throw std::ios_base::failure("Invalid CPubKey being serialized");
+ }
SerializeToVector(s, type, MakeSpan(keypath_pair.first));
WriteCompactSize(s, (keypath_pair.second.path.size() + 1) * sizeof(uint32_t));
s << keypath_pair.second.fingerprint;
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 4f5dd2d6e9..f6f36c2238 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -826,8 +826,10 @@ static bool GetConfigOptions(std::istream& stream, std::string& error, std::vect
std::string::size_type pos;
int linenr = 1;
while (std::getline(stream, str)) {
+ bool used_hash = false;
if ((pos = str.find('#')) != std::string::npos) {
str = str.substr(0, pos);
+ used_hash = true;
}
const static std::string pattern = " \t\r\n";
str = TrimString(str, pattern);
@@ -840,6 +842,10 @@ static bool GetConfigOptions(std::istream& stream, std::string& error, std::vect
} else if ((pos = str.find('=')) != std::string::npos) {
std::string name = prefix + TrimString(str.substr(0, pos), pattern);
std::string value = TrimString(str.substr(pos + 1), pattern);
+ if (used_hash && name == "rpcpassword") {
+ error = strprintf("parse error on line %i, using # in rpcpassword can be ambiguous and should be avoided", linenr);
+ return false;
+ }
options.emplace_back(name, value);
} else {
error = strprintf("parse error on line %i: %s", linenr, str);
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index e1e4fc51fe..6269bc9d3c 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -3,18 +3,19 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chain.h>
+#include <core_io.h>
#include <interfaces/chain.h>
#include <key_io.h>
+#include <merkleblock.h>
#include <rpc/server.h>
-#include <validation.h>
+#include <rpc/util.h>
#include <script/script.h>
#include <script/standard.h>
#include <sync.h>
#include <util/system.h>
#include <util/time.h>
+#include <validation.h>
#include <wallet/wallet.h>
-#include <merkleblock.h>
-#include <core_io.h>
#include <wallet/rpcwallet.h>
@@ -113,7 +114,7 @@ UniValue importprivkey(const JSONRPCRequest& request)
"Hint: use importmulti to import more than one private key.\n"
"\nArguments:\n"
"1. \"privkey\" (string, required) The private key (see dumpprivkey)\n"
- "2. \"label\" (string, optional, default=\"\") An optional label\n"
+ "2. \"label\" (string, optional, default=current label if address exists, otherwise \"\") An optional label\n"
"3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
"\nNote: This call can take over an hour to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported key exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
@@ -163,9 +164,14 @@ UniValue importprivkey(const JSONRPCRequest& request)
CKeyID vchAddress = pubkey.GetID();
{
pwallet->MarkDirty();
- // We don't know which corresponding address will be used; label them all
+
+ // We don't know which corresponding address will be used;
+ // label all new addresses, and label existing addresses if a
+ // label was passed.
for (const auto& dest : GetAllDestinationsForKey(pubkey)) {
- pwallet->SetAddressBook(dest, strLabel, "receive");
+ if (!request.params[1].isNull() || pwallet->mapAddressBook.count(dest) == 0) {
+ pwallet->SetAddressBook(dest, strLabel, "receive");
+ }
}
// Don't throw error in case a key is already there
@@ -342,7 +348,7 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 2)
throw std::runtime_error(
- "importprunedfunds\n"
+ "importprunedfunds \"rawtransaction\" \"txoutproof\"\n"
"\nImports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included.\n"
"\nArguments:\n"
"1. \"rawtransaction\" (string, required) A raw transaction in hex funding an already-existing address in wallet\n"
@@ -1003,8 +1009,9 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding scriptPubKey script to wallet");
}
- // add to address book or update label
- if (IsValidDestination(scriptpubkey_dest)) {
+ // if not internal add to address book or update label
+ if (!internal) {
+ assert(IsValidDestination(scriptpubkey_dest));
pwallet->SetAddressBook(scriptpubkey_dest, label, "receive");
}
@@ -1079,8 +1086,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
// clang-format off
if (mainRequest.fHelp || mainRequest.params.size() < 1 || mainRequest.params.size() > 2)
throw std::runtime_error(
- "importmulti \"requests\" ( \"options\" )\n\n"
- "Import addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options). Requires a new wallet backup.\n\n"
+ "importmulti \"requests\" ( \"options\" )\n"
+ "\nImport addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options). Requires a new wallet backup.\n\n"
"Arguments:\n"
"1. requests (array, required) Data to be imported\n"
" [ (array of json objects)\n"
@@ -1096,7 +1103,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
" \"witnessscript\": \"<script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH-P2WSH or P2WSH address/scriptPubKey\n"
" \"pubkeys\": [\"<pubKey>\", ... ] , (array, optional) Array of strings giving pubkeys that must occur in the output or redeemscript\n"
" \"keys\": [\"<key>\", ... ] , (array, optional) Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript\n"
- " \"internal\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be treated as not incoming payments\n"
+ " \"internal\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be treated as not incoming payments aka change\n"
" \"watchonly\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be considered watched even when they're not spendable, only allowed if keys are empty\n"
" \"label\": <label> , (string, optional, default: '') Label to assign to the address, only allowed with internal=false\n"
" }\n"
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index bfac990639..17f33db666 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -693,7 +693,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
if (request.fHelp || (request.params.size() > 3 ))
throw std::runtime_error(
- "getbalance ( \"(dummy)\" minconf include_watchonly )\n"
+ "getbalance ( \"dummy\" minconf include_watchonly )\n"
"\nReturns the total available balance.\n"
"The available balance is what the wallet considers currently spendable, and is\n"
"thus affected by options which limit spendability such as -spendzeroconfchange.\n"
@@ -1352,7 +1352,7 @@ UniValue listtransactions(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 4)
throw std::runtime_error(
- "listtransactions (dummy count skip include_watchonly)\n"
+ "listtransactions ( \"dummy\" count skip include_watchonly)\n"
"\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n"
"\nArguments:\n"
"1. \"dummy\" (string, optional) If set, should be \"*\" for backwards compatibility.\n"
@@ -2075,7 +2075,21 @@ static UniValue lockunspent(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error(
- "lockunspent unlock ([{\"txid\":\"txid\",\"vout\":n},...])\n"
+ RPCHelpMan{"lockunspent",
+ {
+ {"unlock", RPCArg::Type::BOOL, false},
+ {"transactions", RPCArg::Type::ARR,
+ {
+ {"", RPCArg::Type::OBJ,
+ {
+ {"txid", RPCArg::Type::STR_HEX, false},
+ {"vout", RPCArg::Type::NUM, false},
+ },
+ true},
+ },
+ true},
+ }}
+ .ToString() +
"\nUpdates list of temporarily unspendable outputs.\n"
"Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n"
"If no transaction outputs are specified when unlocking then all current locked transaction outputs are unlocked.\n"
@@ -2620,7 +2634,26 @@ static UniValue listunspent(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 5)
throw std::runtime_error(
- "listunspent ( minconf maxconf [\"addresses\",...] [include_unsafe] [query_options])\n"
+ RPCHelpMan{"listunspent",
+ {
+ {"minconf", RPCArg::Type::NUM, true},
+ {"maxconf", RPCArg::Type::NUM, true},
+ {"addresses", RPCArg::Type::ARR,
+ {
+ {"address", RPCArg::Type::STR_HEX, true},
+ },
+ true},
+ {"include_unsafe", RPCArg::Type::BOOL, true},
+ {"query_options", RPCArg::Type::OBJ,
+ {
+ {"minimumAmount", RPCArg::Type::AMOUNT, true},
+ {"maximumAmount", RPCArg::Type::AMOUNT, true},
+ {"maximumCount", RPCArg::Type::NUM, true},
+ {"minimumSumAmount", RPCArg::Type::AMOUNT, true},
+ },
+ true},
+ }}
+ .ToString() +
"\nReturns array of unspent transaction outputs\n"
"with between minconf and maxconf (inclusive) confirmations.\n"
"Optionally filter to only include txouts paid to specified addresses.\n"
@@ -2995,7 +3028,25 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error(
- "signrawtransactionwithwallet \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] sighashtype )\n"
+ RPCHelpMan{"signrawtransactionwithwallet",
+ {
+ {"hexstring", RPCArg::Type::STR, false},
+ {"prevtxs", RPCArg::Type::ARR,
+ {
+ {"", RPCArg::Type::OBJ,
+ {
+ {"txid", RPCArg::Type::STR_HEX, false},
+ {"vout", RPCArg::Type::NUM, false},
+ {"scriptPubKey", RPCArg::Type::STR_HEX, false},
+ {"redeemScript", RPCArg::Type::STR_HEX, false},
+ {"amount", RPCArg::Type::AMOUNT, false},
+ },
+ false},
+ },
+ true},
+ {"sighashtype", RPCArg::Type::STR, true},
+ }}
+ .ToString() +
"\nSign inputs for raw transaction (serialized, hex-encoded).\n"
"The second optional argument (may be null) is an array of previous transaction outputs that\n"
"this transaction depends on but may not yet be in the block chain.\n"
@@ -3902,7 +3953,55 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 2 || request.params.size() > 5)
throw std::runtime_error(
- "walletcreatefundedpsbt [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable ) ( options bip32derivs )\n"
+ RPCHelpMan{"walletcreatefundedpsbt",
+ {
+ {"inputs", RPCArg::Type::ARR,
+ {
+ {"", RPCArg::Type::OBJ,
+ {
+ {"txid", RPCArg::Type::STR_HEX, false},
+ {"vout", RPCArg::Type::NUM, false},
+ {"sequence", RPCArg::Type::NUM, false},
+ },
+ false},
+ },
+ false},
+ {"outputs", RPCArg::Type::ARR,
+ {
+ {"", RPCArg::Type::OBJ,
+ {
+ {"address", RPCArg::Type::AMOUNT, true},
+ },
+ true},
+ {"", RPCArg::Type::OBJ,
+ {
+ {"data", RPCArg::Type::STR_HEX, true},
+ },
+ true},
+ },
+ false},
+ {"locktime", RPCArg::Type::NUM, true},
+ {"options", RPCArg::Type::OBJ,
+ {
+ {"changeAddress", RPCArg::Type::STR_HEX, true},
+ {"changePosition", RPCArg::Type::NUM, true},
+ {"change_type", RPCArg::Type::STR, true},
+ {"includeWatching", RPCArg::Type::BOOL, true},
+ {"lockUnspents", RPCArg::Type::BOOL, true},
+ {"feeRate", RPCArg::Type::NUM, true},
+ {"subtractFeeFromOutputs", RPCArg::Type::ARR,
+ {
+ {"int", RPCArg::Type::NUM, true},
+ },
+ true},
+ {"replaceable", RPCArg::Type::BOOL, true},
+ {"conf_target", RPCArg::Type::NUM, true},
+ {"estimate_mode", RPCArg::Type::STR, true},
+ },
+ true},
+ {"bip32derivs", RPCArg::Type::BOOL, true},
+ }}
+ .ToString() +
"\nCreates and funds a transaction in the Partially Signed Transaction format. Inputs will be added if supplied inputs are not enough\n"
"Implements the Creator and Updater roles.\n"
"\nArguments:\n"
diff --git a/src/zmq/zmqrpc.cpp b/src/zmq/zmqrpc.cpp
index 0e0b745375..66b491427d 100644
--- a/src/zmq/zmqrpc.cpp
+++ b/src/zmq/zmqrpc.cpp
@@ -5,6 +5,7 @@
#include <zmq/zmqrpc.h>
#include <rpc/server.h>
+#include <rpc/util.h>
#include <zmq/zmqabstractnotifier.h>
#include <zmq/zmqnotificationinterface.h>
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index 628cefb76d..e386915ada 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -824,7 +824,7 @@ class FullBlockTest(BitcoinTestFramework):
tx.vin.append(CTxIn(COutPoint(b64a.vtx[1].sha256, 0)))
b64a = self.update_block("64a", [tx])
assert_equal(len(b64a.serialize()), MAX_BLOCK_BASE_SIZE + 8)
- self.sync_blocks([b64a], success=False, reject_reason='non-canonical ReadCompactSize():')
+ self.sync_blocks([b64a], success=False, reject_reason='non-canonical ReadCompactSize()')
# bitcoind doesn't disconnect us for sending a bloated block, but if we subsequently
# resend the header message, it won't send us the getdata message again. Just
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index 492772d5e3..88a9aadc7b 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -30,6 +30,10 @@ class ConfArgsTest(BitcoinTestFramework):
self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 1: nono, if you intended to specify a negated option, use nono=1 instead')
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
+ conf.write('server=1\nrpcuser=someuser\nrpcpassword=some#pass')
+ self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 3, using # in rpcpassword can be ambiguous and should be avoided')
+
+ with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
conf.write('') # clear
def run_test(self):
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index c820ca33e2..c162f46d63 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -249,7 +249,7 @@ class PruneTest(BitcoinTestFramework):
return index
def prune(index, expected_ret=None):
- ret = node.pruneblockchain(height(index))
+ ret = node.pruneblockchain(height=height(index))
# Check the return value. When use_timestamp is True, just check
# that the return value is less than or equal to the expected
# value, because when more than one block is generated per second,
diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py
index ff55ea5528..9f01be0646 100755
--- a/test/functional/mining_basic.py
+++ b/test/functional/mining_basic.py
@@ -30,9 +30,10 @@ from test_framework.util import (
def assert_template(node, block, expect, rehash=True):
if rehash:
block.hashMerkleRoot = block.calc_merkle_root()
- rsp = node.getblocktemplate({'data': b2x(block.serialize()), 'mode': 'proposal'})
+ rsp = node.getblocktemplate(template_request={'data': b2x(block.serialize()), 'mode': 'proposal'})
assert_equal(rsp, expect)
+
class MiningTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
diff --git a/test/functional/p2p_disconnect_ban.py b/test/functional/p2p_disconnect_ban.py
index 67f24d6bff..1b11a2a294 100755
--- a/test/functional/p2p_disconnect_ban.py
+++ b/test/functional/p2p_disconnect_ban.py
@@ -22,7 +22,7 @@ class DisconnectBanTest(BitcoinTestFramework):
self.log.info("setban: successfully ban single IP address")
assert_equal(len(self.nodes[1].getpeerinfo()), 2) # node1 should have 2 connections to node0 at this point
- self.nodes[1].setban("127.0.0.1", "add")
+ self.nodes[1].setban(subnet="127.0.0.1", command="add")
wait_until(lambda: len(self.nodes[1].getpeerinfo()) == 0, timeout=10)
assert_equal(len(self.nodes[1].getpeerinfo()), 0) # all nodes must be disconnected at this point
assert_equal(len(self.nodes[1].listbanned()), 1)
diff --git a/test/functional/p2p_invalid_block.py b/test/functional/p2p_invalid_block.py
index 0678b1a651..1e0b876593 100755
--- a/test/functional/p2p_invalid_block.py
+++ b/test/functional/p2p_invalid_block.py
@@ -77,9 +77,9 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
block2.vtx.append(tx2)
assert_equal(block2.hashMerkleRoot, block2.calc_merkle_root())
assert_equal(orig_hash, block2.rehash())
- assert(block2_orig.vtx != block2.vtx)
+ assert block2_orig.vtx != block2.vtx
- node.p2p.send_blocks_and_test([block2], node, success=False, request_block=False, reject_reason='bad-txns-duplicate')
+ node.p2p.send_blocks_and_test([block2], node, success=False, reject_reason='bad-txns-duplicate')
# Check transactions for duplicate inputs
self.log.info("Test duplicate input block.")
@@ -89,7 +89,7 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
block2_orig.hashMerkleRoot = block2_orig.calc_merkle_root()
block2_orig.rehash()
block2_orig.solve()
- node.p2p.send_blocks_and_test([block2_orig], node, success=False, request_block=False, reject_reason='bad-txns-inputs-duplicate')
+ node.p2p.send_blocks_and_test([block2_orig], node, success=False, reject_reason='bad-txns-inputs-duplicate')
self.log.info("Test very broken block.")
@@ -102,7 +102,8 @@ class InvalidBlockRequestTest(BitcoinTestFramework):
block3.rehash()
block3.solve()
- node.p2p.send_blocks_and_test([block3], node, success=False, request_block=False, reject_reason='bad-cb-amount')
+ node.p2p.send_blocks_and_test([block3], node, success=False, reject_reason='bad-cb-amount')
+
if __name__ == '__main__':
InvalidBlockRequestTest().main()
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index 92b690176d..31e60f1cea 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -133,7 +133,7 @@ class BlockchainTest(BitcoinTestFramework):
assert_raises_rpc_error(-8, "Block is not in main chain", self.nodes[0].getchaintxstats, blockhash=blockhash)
self.nodes[0].reconsiderblock(blockhash)
- chaintxstats = self.nodes[0].getchaintxstats(1)
+ chaintxstats = self.nodes[0].getchaintxstats(nblocks=1)
# 200 txs plus genesis tx
assert_equal(chaintxstats['txcount'], 201)
# tx rate should be 1 per 10 minutes, or 1/600
@@ -211,7 +211,7 @@ class BlockchainTest(BitcoinTestFramework):
besthash = node.getbestblockhash()
secondbesthash = node.getblockhash(199)
- header = node.getblockheader(besthash)
+ header = node.getblockheader(blockhash=besthash)
assert_equal(header['hash'], besthash)
assert_equal(header['height'], 200)
@@ -287,7 +287,7 @@ class BlockchainTest(BitcoinTestFramework):
def assert_waitforheight(height, timeout=2):
assert_equal(
- node.waitforblockheight(height, timeout)['height'],
+ node.waitforblockheight(height=height, timeout=timeout)['height'],
current_height)
assert_waitforheight(0)
diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py
index 1e525214fa..0affddcf05 100755
--- a/test/functional/rpc_net.py
+++ b/test/functional/rpc_net.py
@@ -74,12 +74,12 @@ class NetTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
- self.nodes[0].setnetworkactive(False)
+ self.nodes[0].setnetworkactive(state=False)
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], False)
# Wait a bit for all sockets to close
wait_until(lambda: self.nodes[0].getnetworkinfo()['connections'] == 0, timeout=3)
- self.nodes[0].setnetworkactive(True)
+ self.nodes[0].setnetworkactive(state=True)
connect_nodes_bi(self.nodes, 0, 1)
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
@@ -88,7 +88,7 @@ class NetTest(BitcoinTestFramework):
assert_equal(self.nodes[0].getaddednodeinfo(), [])
# add a node (node2) to node0
ip_port = "127.0.0.1:{}".format(p2p_port(2))
- self.nodes[0].addnode(ip_port, 'add')
+ self.nodes[0].addnode(node=ip_port, command='add')
# check that the node has indeed been added
added_nodes = self.nodes[0].getaddednodeinfo(ip_port)
assert_equal(len(added_nodes), 1)
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index ba3818bf24..04d9bb65a6 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -146,6 +146,9 @@ class PSBTTest(BitcoinTestFramework):
# Make sure that a psbt with signatures cannot be converted
signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx['hex'])
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].converttopsbt, signedtx['hex'])
+ assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].converttopsbt, signedtx['hex'], False)
+ # Unless we allow it to convert and strip signatures
+ self.nodes[0].converttopsbt(signedtx['hex'], True)
# Explicitly allow converting non-empty txs
new_psbt = self.nodes[0].converttopsbt(rawtx['hex'])
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index ffff81e070..9dcc0e6d0e 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -118,22 +118,19 @@ class TestNode():
def get_mem_rss(self):
"""Get the memory usage (RSS) per `ps`.
- If process is stopped or `ps` is unavailable, return None.
+ Returns None if `ps` is unavailable.
"""
- if not (self.running and self.process):
- self.log.warning("Couldn't get memory usage; process isn't running.")
- return None
+ assert self.running
try:
return int(subprocess.check_output(
- "ps h -o rss {}".format(self.process.pid),
- shell=True, stderr=subprocess.DEVNULL).strip())
+ ["ps", "h", "-o", "rss", "{}".format(self.process.pid)],
+ stderr=subprocess.DEVNULL).split()[-1])
- # Catching `Exception` broadly to avoid failing on platforms where ps
- # isn't installed or doesn't work as expected, e.g. OpenBSD.
+ # Avoid failing on platforms where ps isn't installed.
#
# We could later use something like `psutils` to work across platforms.
- except Exception:
+ except (FileNotFoundError, subprocess.SubprocessError):
self.log.exception("Unable to get memory usage")
return None
@@ -308,7 +305,7 @@ class TestNode():
self.log.warning("Unable to detect memory usage (RSS) - skipping memory check.")
return
- perc_increase_memory_usage = 1 - (float(before_memory_usage) / after_memory_usage)
+ perc_increase_memory_usage = (after_memory_usage / before_memory_usage) - 1
if perc_increase_memory_usage > perc_increase_allowed:
self._raise_assertion_error(
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 90b333f45a..5541b44690 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -155,6 +155,7 @@ BASE_SCRIPTS = [
'feature_nulldummy.py',
'mempool_accept.py',
'wallet_import_rescan.py',
+ 'wallet_import_with_label.py',
'rpc_bind.py --ipv4',
'rpc_bind.py --ipv6',
'rpc_bind.py --nonloopback',
@@ -365,7 +366,7 @@ def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=
tmpdir=tmpdir,
test_list=test_list,
flags=flags,
- timeout_duration=20 * 60 if runs_ci else float('inf'), # in seconds
+ timeout_duration=40 * 60 if runs_ci else float('inf'), # in seconds
)
start_time = time.time()
test_results = []
diff --git a/test/functional/wallet_import_with_label.py b/test/functional/wallet_import_with_label.py
new file mode 100755
index 0000000000..95acaa752e
--- /dev/null
+++ b/test/functional/wallet_import_with_label.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python3
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test the behavior of RPC importprivkey on set and unset labels of
+addresses.
+
+It tests different cases in which an address is imported with importaddress
+with or without a label and then its private key is imported with importprivkey
+with and without a label.
+"""
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+
+class ImportWithLabel(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 2
+ self.setup_clean_chain = True
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def run_test(self):
+ """Main test logic"""
+
+ self.log.info(
+ "Test importaddress with label and importprivkey without label."
+ )
+ self.log.info("Import a watch-only address with a label.")
+ address = self.nodes[0].getnewaddress()
+ label = "Test Label"
+ self.nodes[1].importaddress(address, label)
+ address_assert = self.nodes[1].getaddressinfo(address)
+
+ assert_equal(address_assert["iswatchonly"], True)
+ assert_equal(address_assert["ismine"], False)
+ assert_equal(address_assert["label"], label)
+
+ self.log.info(
+ "Import the watch-only address's private key without a "
+ "label and the address should keep its label."
+ )
+ priv_key = self.nodes[0].dumpprivkey(address)
+ self.nodes[1].importprivkey(priv_key)
+
+ assert_equal(label, self.nodes[1].getaddressinfo(address)["label"])
+
+ self.log.info(
+ "Test importaddress without label and importprivkey with label."
+ )
+ self.log.info("Import a watch-only address without a label.")
+ address2 = self.nodes[0].getnewaddress()
+ self.nodes[1].importaddress(address2)
+ address_assert2 = self.nodes[1].getaddressinfo(address2)
+
+ assert_equal(address_assert2["iswatchonly"], True)
+ assert_equal(address_assert2["ismine"], False)
+ assert_equal(address_assert2["label"], "")
+
+ self.log.info(
+ "Import the watch-only address's private key with a "
+ "label and the address should have its label updated."
+ )
+ priv_key2 = self.nodes[0].dumpprivkey(address2)
+ label2 = "Test Label 2"
+ self.nodes[1].importprivkey(priv_key2, label2)
+
+ assert_equal(label2, self.nodes[1].getaddressinfo(address2)["label"])
+
+ self.log.info("Test importaddress with label and importprivkey with label.")
+ self.log.info("Import a watch-only address with a label.")
+ address3 = self.nodes[0].getnewaddress()
+ label3_addr = "Test Label 3 for importaddress"
+ self.nodes[1].importaddress(address3, label3_addr)
+ address_assert3 = self.nodes[1].getaddressinfo(address3)
+
+ assert_equal(address_assert3["iswatchonly"], True)
+ assert_equal(address_assert3["ismine"], False)
+ assert_equal(address_assert3["label"], label3_addr)
+
+ self.log.info(
+ "Import the watch-only address's private key with a "
+ "label and the address should have its label updated."
+ )
+ priv_key3 = self.nodes[0].dumpprivkey(address3)
+ label3_priv = "Test Label 3 for importprivkey"
+ self.nodes[1].importprivkey(priv_key3, label3_priv)
+
+ assert_equal(label3_priv, self.nodes[1].getaddressinfo(address3)["label"])
+
+ self.log.info(
+ "Test importprivkey won't label new dests with the same "
+ "label as others labeled dests for the same key."
+ )
+ self.log.info("Import a watch-only legacy address with a label.")
+ address4 = self.nodes[0].getnewaddress()
+ label4_addr = "Test Label 4 for importaddress"
+ self.nodes[1].importaddress(address4, label4_addr)
+ address_assert4 = self.nodes[1].getaddressinfo(address4)
+
+ assert_equal(address_assert4["iswatchonly"], True)
+ assert_equal(address_assert4["ismine"], False)
+ assert_equal(address_assert4["label"], label4_addr)
+
+ self.log.info("Asserts address has no embedded field with dests.")
+
+ assert_equal(address_assert4.get("embedded"), None)
+
+ self.log.info(
+ "Import the watch-only address's private key without a "
+ "label and new destinations for the key should have an "
+ "empty label while the 'old' destination should keep "
+ "its label."
+ )
+ priv_key4 = self.nodes[0].dumpprivkey(address4)
+ self.nodes[1].importprivkey(priv_key4)
+ address_assert4 = self.nodes[1].getaddressinfo(address4)
+
+ assert address_assert4.get("embedded")
+
+ bcaddress_assert = self.nodes[1].getaddressinfo(
+ address_assert4["embedded"]["address"]
+ )
+
+ assert_equal(address_assert4["label"], label4_addr)
+ assert_equal(bcaddress_assert["label"], "")
+
+ self.stop_nodes()
+
+
+if __name__ == "__main__":
+ ImportWithLabel().main()
diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py
index 9ba6860306..5c789b1c03 100755
--- a/test/functional/wallet_importmulti.py
+++ b/test/functional/wallet_importmulti.py
@@ -54,7 +54,7 @@ class ImportMultiTest(BitcoinTestFramework):
# RPC importmulti -----------------------------------------------
- # Bitcoin Address
+ # Bitcoin Address (implicit non-internal)
self.log.info("Should import an address")
address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
result = self.nodes[1].importmulti([{
@@ -68,6 +68,7 @@ class ImportMultiTest(BitcoinTestFramework):
assert_equal(address_assert['iswatchonly'], True)
assert_equal(address_assert['ismine'], False)
assert_equal(address_assert['timestamp'], timestamp)
+ assert_equal(address_assert['ischange'], False)
watchonly_address = address['address']
watchonly_timestamp = timestamp
@@ -95,6 +96,7 @@ class ImportMultiTest(BitcoinTestFramework):
assert_equal(address_assert['iswatchonly'], True)
assert_equal(address_assert['ismine'], False)
assert_equal(address_assert['timestamp'], timestamp)
+ assert_equal(address_assert['ischange'], True)
# ScriptPubKey + internal + label
self.log.info("Should not allow a label to be specified when internal is true")
@@ -126,7 +128,7 @@ class ImportMultiTest(BitcoinTestFramework):
assert_equal('timestamp' in address_assert, False)
- # Address + Public key + !Internal
+ # Address + Public key + !Internal(explicit)
self.log.info("Should import an address with public key")
address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress())
result = self.nodes[1].importmulti([{
@@ -134,7 +136,8 @@ class ImportMultiTest(BitcoinTestFramework):
"address": address['address']
},
"timestamp": "now",
- "pubkeys": [ address['pubkey'] ]
+ "pubkeys": [ address['pubkey'] ],
+ "internal": False
}])
assert_equal(result[0]['success'], True)
address_assert = self.nodes[1].getaddressinfo(address['address'])
@@ -152,7 +155,7 @@ class ImportMultiTest(BitcoinTestFramework):
"pubkeys": [ address['pubkey'] ],
"internal": True
}]
- result = self.nodes[1].importmulti(request)
+ result = self.nodes[1].importmulti(requests=request)
assert_equal(result[0]['success'], True)
address_assert = self.nodes[1].getaddressinfo(address['address'])
assert_equal(address_assert['iswatchonly'], True)
@@ -167,7 +170,7 @@ class ImportMultiTest(BitcoinTestFramework):
"timestamp": "now",
"pubkeys": [ address['pubkey'] ]
}]
- result = self.nodes[1].importmulti(request)
+ result = self.nodes[1].importmulti(requests=request)
assert_equal(result[0]['success'], False)
assert_equal(result[0]['error']['code'], -8)
assert_equal(result[0]['error']['message'], 'Internal must be set to true for nonstandard scriptPubKey imports.')
diff --git a/test/functional/wallet_importprunedfunds.py b/test/functional/wallet_importprunedfunds.py
index 26b181db33..78426018ef 100755
--- a/test/functional/wallet_importprunedfunds.py
+++ b/test/functional/wallet_importprunedfunds.py
@@ -81,7 +81,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
# Import with affiliated address with no rescan
self.nodes[1].importaddress(address=address2, rescan=False)
- self.nodes[1].importprunedfunds(rawtxn2, proof2)
+ self.nodes[1].importprunedfunds(rawtransaction=rawtxn2, txoutproof=proof2)
assert [tx for tx in self.nodes[1].listtransactions(include_watchonly=True) if tx['txid'] == txnid2]
# Import with private key with no rescan