diff --git a/ci/README.md b/ci/README.md index 3c5f04c39e348..19228ffbaaf0b 100644 --- a/ci/README.md +++ b/ci/README.md @@ -15,10 +15,10 @@ testing compared to other parts of the codebase. If you want to keep the work tr system in a virtual machine with a Linux operating system of your choice. To allow for a wide range of tested environments, but also ensure reproducibility to some extent, the test stage -requires `docker` to be installed. To install all requirements on Ubuntu, run +requires `docker` to be installed. To run on different architectures than the host `qemu` is also required. To install all requirements on Ubuntu, run ``` -sudo apt install docker.io bash +sudo apt install docker.io bash qemu-user-static ``` To run the default test stage, diff --git a/ci/test/00_setup_env_s390x.sh b/ci/test/00_setup_env_s390x.sh index 05c062cb56de4..5d95535daf924 100755 --- a/ci/test/00_setup_env_s390x.sh +++ b/ci/test/00_setup_env_s390x.sh @@ -22,4 +22,4 @@ export RUN_UNIT_TESTS=true export TEST_RUNNER_EXTRA="--exclude rpc_bind,feature_bind_extra" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547 export RUN_FUNCTIONAL_TESTS=true export GOAL="install" -export BITCOIN_CONFIG="--enable-reduce-exports --disable-gui-tests --with-boost-process" # GUI tests disabled for now, see https://github.com/bitcoin/bitcoin/issues/23730 +export BITCOIN_CONFIG="--enable-reduce-exports --with-boost-process" diff --git a/depends/funcs.mk b/depends/funcs.mk index aeaff2288b1b2..3c0dc7a7fcab5 100644 --- a/depends/funcs.mk +++ b/depends/funcs.mk @@ -138,9 +138,9 @@ $(1)_config_env+=PKG_CONFIG_LIBDIR=$($($(1)_type)_prefix)/lib/pkgconfig $(1)_config_env+=PKG_CONFIG_PATH=$($($(1)_type)_prefix)/share/pkgconfig $(1)_config_env+=PKG_CONFIG_SYSROOT_DIR=/ $(1)_config_env+=CMAKE_MODULE_PATH=$($($(1)_type)_prefix)/lib/cmake -$(1)_config_env+=PATH=$(build_prefix)/bin:$(PATH) -$(1)_build_env+=PATH=$(build_prefix)/bin:$(PATH) -$(1)_stage_env+=PATH=$(build_prefix)/bin:$(PATH) +$(1)_config_env+=PATH="$(build_prefix)/bin:$(PATH)" +$(1)_build_env+=PATH="$(build_prefix)/bin:$(PATH)" +$(1)_stage_env+=PATH="$(build_prefix)/bin:$(PATH)" # Setting a --build type that differs from --host will explicitly enable # cross-compilation mode. Note that --build defaults to the output of diff --git a/doc/build-openbsd.md b/doc/build-openbsd.md index c80ecdaf83fde..afcc43355dd51 100644 --- a/doc/build-openbsd.md +++ b/doc/build-openbsd.md @@ -1,6 +1,6 @@ OpenBSD build guide ====================== -(updated for OpenBSD 6.7) +**Updated for OpenBSD [7.4](https://www.openbsd.org/74.html)** This guide describes how to build dashd, dash-qt, and command-line utilities on OpenBSD. @@ -40,6 +40,8 @@ from ports, for the same reason as boost above (g++/libstd++ incompatibility). If you have to build it yourself, you can use [the installation script included in contrib/](/contrib/install_db4.sh) like so: +Refer to [depends/README.md](/depends/README.md) for detailed instructions. + ```bash ./contrib/install_db4.sh `pwd` CC=cc CXX=c++ ``` diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 5de6fa61e8ee4..67f377b200e1f 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -110,6 +110,8 @@ code. - `nullptr` is preferred over `NULL` or `(void*)0`. - `static_assert` is preferred over `assert` where possible. Generally; compile-time checking is preferred over run-time checking. - Align pointers and references to the left i.e. use `type& var` and not `type &var`. + - Prefer [`list initialization ({})`](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-list) where possible. + For example `int x{0};` instead of `int x = 0;` or `int x(0);` For function calls a namespace should be specified explicitly, unless such functions have been declared within it. Otherwise, [argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl), also known as ADL, could be @@ -135,7 +137,7 @@ int main() Block style example: ```c++ -int g_count = 0; +int g_count{0}; namespace foo { class Class @@ -147,7 +149,7 @@ public: { // Comment summarising what this section of code does for (int i = 0; i < n; ++i) { - int total_sum = 0; + int total_sum{0}; // When something fails, return early if (!Something()) return false; ... diff --git a/doc/policy/README.md b/doc/policy/README.md index a24298616d1ad..1aa4f3847cd42 100644 --- a/doc/policy/README.md +++ b/doc/policy/README.md @@ -2,8 +2,8 @@ **Policy** (Mempool or Transaction Relay Policy) is the node's set of validation rules, in addition to consensus, enforced for unconfirmed transactions before submitting them to the mempool. These -rules are local to the node and configurable (e.g. `-minrelaytxfee`, `-limitancestorsize`, -`-incrementalRelayFee`). Policy may include restrictions on the transaction itself, the transaction +rules are local to the node and configurable, see "Node relay options" when running `-help`. +Policy may include restrictions on the transaction itself, the transaction in relation to the current chain tip, and the transaction in relation to the node's mempool contents. Policy is *not* applied to transactions in blocks. diff --git a/doc/reduce-memory.md b/doc/reduce-memory.md index 980c2b06ab9a7..f966eb65e245b 100644 --- a/doc/reduce-memory.md +++ b/doc/reduce-memory.md @@ -43,7 +43,7 @@ threads take up 8MiB for the thread stack on a 64-bit system, and 4MiB in a ## Linux specific -By default, glibc's implementation of `malloc` may use more than one arena. This is known to cause excessive memory usage in some scenarios. To avoid this, make a script that sets `MALLOC_ARENA_MAX` before starting dashd: +By default, glibc will create up to two heap arenas per core. This is known to cause excessive memory usage in some scenarios. To avoid this make a script that sets `MALLOC_ARENA_MAX` before starting dashd: ```bash #!/usr/bin/env bash diff --git a/src/httpserver.cpp b/src/httpserver.cpp index dc4d6c5dc0d43..fe8c8879ea438 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -219,8 +219,10 @@ std::string RequestMethodString(HTTPRequest::RequestMethod m) /** HTTP request callback */ static void http_request_cb(struct evhttp_request* req, void* arg) { - // Disable reading to work around a libevent bug, fixed in 2.2.0. - if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) { + // Disable reading to work around a libevent bug, fixed in 2.1.9 + // See https://github.com/libevent/libevent/commit/5ff8eb26371c4dc56f384b2de35bea2d87814779 + // and https://github.com/bitcoin/bitcoin/pull/11593. + if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02010900) { evhttp_connection* conn = evhttp_request_get_connection(req); if (conn) { bufferevent* bev = evhttp_connection_get_bufferevent(conn); @@ -616,7 +618,7 @@ void HTTPRequest::WriteReply(int nStatus, const std::string& strReply) evhttp_send_reply(req_copy, nStatus, nullptr, nullptr); // Re-enable reading from the socket. This is the second part of the libevent // workaround above. - if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) { + if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02010900) { evhttp_connection* conn = evhttp_request_get_connection(req_copy); if (conn) { bufferevent* bev = evhttp_connection_get_bufferevent(conn); diff --git a/src/net.cpp b/src/net.cpp index ae09e0c5b7e54..fb56676f3d2e0 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -48,8 +48,6 @@ #ifdef WIN32 #include -#else -#include #endif #if HAVE_DECL_GETIFADDRS && HAVE_DECL_FREEIFADDRS diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 080aea566223a..fb6f2238470cf 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -695,7 +695,7 @@ class PeerManagerImpl final : public PeerManager * * Changes here may need to be reflected in TxRelayMayResultInDisconnect(). */ - bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message = "") + bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); /** Maybe disconnect a peer and discourage future connections from its address. @@ -1872,7 +1872,7 @@ bool PeerManagerImpl::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidati return false; } -bool PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message) { +bool PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state) { switch (state.GetResult()) { case TxValidationResult::TX_RESULT_UNSET: break; @@ -1880,7 +1880,7 @@ bool PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationStat case TxValidationResult::TX_CONSENSUS: { LOCK(cs_main); - Misbehaving(nodeid, 100, message); + Misbehaving(nodeid, 100); return true; } // Conflicting (but not necessarily invalid) data or different policy: @@ -1897,9 +1897,6 @@ bool PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationStat case TxValidationResult::TX_CONFLICT_LOCK: break; } - if (message != "") { - LogPrint(BCLog::NET, "peer=%d: %s\n", nodeid, message); - } return false; } diff --git a/src/netbase.cpp b/src/netbase.cpp index 95d95f3f31ccf..64034b919b505 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -28,14 +28,6 @@ #include #endif -#ifndef WIN32 -#include -#endif - -#ifdef USE_POLL -#include -#endif - // Settings static Mutex g_proxyinfo_mutex; static Proxy proxyInfo[NET_MAX] GUARDED_BY(g_proxyinfo_mutex); diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 4fb6728d29cc5..593caf5ba252b 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -55,7 +55,6 @@ CoinControlDialog::CoinControlDialog(CCoinControl& coin_control, WalletModel* _m GUIUtil::setFont({ui->labelCoinControlQuantityText, ui->labelCoinControlBytesText, ui->labelCoinControlAmountText, - ui->labelCoinControlLowOutputText, ui->labelCoinControlFeeText, ui->labelCoinControlAfterFeeText, ui->labelCoinControlChangeText @@ -82,7 +81,6 @@ CoinControlDialog::CoinControlDialog(CCoinControl& coin_control, WalletModel* _m QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this); QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this); QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this); - QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this); QAction *clipboardChangeAction = new QAction(tr("Copy change"), this); connect(clipboardQuantityAction, &QAction::triggered, this, &CoinControlDialog::clipboardQuantity); @@ -90,7 +88,6 @@ CoinControlDialog::CoinControlDialog(CCoinControl& coin_control, WalletModel* _m connect(clipboardFeeAction, &QAction::triggered, this, &CoinControlDialog::clipboardFee); connect(clipboardAfterFeeAction, &QAction::triggered, this, &CoinControlDialog::clipboardAfterFee); connect(clipboardBytesAction, &QAction::triggered, this, &CoinControlDialog::clipboardBytes); - connect(clipboardLowOutputAction, &QAction::triggered, this, &CoinControlDialog::clipboardLowOutput); connect(clipboardChangeAction, &QAction::triggered, this, &CoinControlDialog::clipboardChange); ui->labelCoinControlQuantity->addAction(clipboardQuantityAction); @@ -98,7 +95,6 @@ CoinControlDialog::CoinControlDialog(CCoinControl& coin_control, WalletModel* _m ui->labelCoinControlFee->addAction(clipboardFeeAction); ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction); ui->labelCoinControlBytes->addAction(clipboardBytesAction); - ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction); ui->labelCoinControlChange->addAction(clipboardChangeAction); // toggle tree/list mode @@ -349,12 +345,6 @@ void CoinControlDialog::clipboardBytes() GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, "")); } -// copy label "Dust" to clipboard -void CoinControlDialog::clipboardLowOutput() -{ - GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text()); -} - // copy label "Change" to clipboard void CoinControlDialog::clipboardChange() { @@ -452,17 +442,8 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel * // nPayAmount CAmount nPayAmount = 0; - bool fDust = false; - for (const CAmount &amount : CoinControlDialog::payAmounts) - { + for (const CAmount &amount : CoinControlDialog::payAmounts) { nPayAmount += amount; - - if (amount > 0) - { - // Assumes a p2pkh script size - CTxOut txout(amount, CScript() << std::vector(24, 0)); - fDust |= IsDust(txout, model->node().getDustRelayFee()); - } } CAmount nAmount = 0; @@ -573,12 +554,9 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel * QLabel *l3 = dialog->findChild("labelCoinControlFee"); QLabel *l4 = dialog->findChild("labelCoinControlAfterFee"); QLabel *l5 = dialog->findChild("labelCoinControlBytes"); - QLabel *l7 = dialog->findChild("labelCoinControlLowOutput"); QLabel *l8 = dialog->findChild("labelCoinControlChange"); - // enable/disable "dust" and "change" - dialog->findChild("labelCoinControlLowOutputText")->setEnabled(nPayAmount > 0); - dialog->findChild("labelCoinControlLowOutput") ->setEnabled(nPayAmount > 0); + // enable/disable "change" dialog->findChild("labelCoinControlChangeText") ->setEnabled(nPayAmount > 0); dialog->findChild("labelCoinControlChange") ->setEnabled(nPayAmount > 0); @@ -588,7 +566,6 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel * l3->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nPayFee)); // Fee l4->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAfterFee)); // After Fee l5->setText(((nBytes > 0) ? ASYMP_UTF8 : "") + QString::number(nBytes)); // Bytes - l7->setText(fDust ? tr("yes") : tr("no")); // Dust l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change if (nPayFee > 0) { @@ -598,12 +575,6 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel * l8->setText(ASYMP_UTF8 + l8->text()); } - // turn label red when dust - l7->setStyleSheet((fDust) ? GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_ERROR) : ""); - - // tool tips - QString toolTipDust = tr("This label turns red if any recipient receives an amount smaller than the current dust threshold."); - // how many satoshis the estimated fee can vary per byte we guess wrong double dFeeVary = (nBytes != 0) ? (double)nPayFee / nBytes : 0; @@ -611,12 +582,10 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel * l3->setToolTip(toolTip4); l4->setToolTip(toolTip4); - l7->setToolTip(toolTipDust); l8->setToolTip(toolTip4); dialog->findChild("labelCoinControlFeeText") ->setToolTip(l3->toolTip()); dialog->findChild("labelCoinControlAfterFeeText") ->setToolTip(l4->toolTip()); dialog->findChild("labelCoinControlBytesText") ->setToolTip(l5->toolTip()); - dialog->findChild("labelCoinControlLowOutputText")->setToolTip(l7->toolTip()); dialog->findChild("labelCoinControlChangeText") ->setToolTip(l8->toolTip()); // Insufficient funds diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index 7158f778d01a5..f602afb408bc4 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -102,7 +102,6 @@ private Q_SLOTS: void clipboardFee(); void clipboardAfterFee(); void clipboardBytes(); - void clipboardLowOutput(); void clipboardChange(); void radioTreeMode(bool); void radioListMode(bool); diff --git a/src/qt/forms/coincontroldialog.ui b/src/qt/forms/coincontroldialog.ui index d5d6e5f182608..f24eb76221b2d 100644 --- a/src/qt/forms/coincontroldialog.ui +++ b/src/qt/forms/coincontroldialog.ui @@ -24,6 +24,9 @@ + + Qt::AlignLeft|Qt::AlignVCenter + 10 @@ -121,35 +124,6 @@ - - - - false - - - Dust: - - - - - - - false - - - IBeamCursor - - - Qt::ActionsContextMenu - - - no - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - @@ -193,6 +167,9 @@ + + Qt::AlignLeft|Qt::AlignVCenter + 10 diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index b6381cc23296e..bb2dee6216f84 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -183,6 +183,9 @@ + + Qt::AlignLeft|Qt::AlignVCenter + 10 @@ -295,29 +298,6 @@ - - - - Dust: - - - - - - - IBeamCursor - - - Qt::ActionsContextMenu - - - no - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - @@ -367,6 +347,9 @@ + + Qt::AlignLeft|Qt::AlignVCenter + 10 diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index e51a55286913c..26088439f1e91 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -79,7 +79,6 @@ SendCoinsDialog::SendCoinsDialog(bool _fCoinJoin, QWidget* parent) : ui->labelCoinControlQuantityText, ui->labelCoinControlBytesText, ui->labelCoinControlAmountText, - ui->labelCoinControlLowOutputText, ui->labelCoinControlFeeText, ui->labelCoinControlAfterFeeText, ui->labelCoinControlChangeText, @@ -115,21 +114,18 @@ SendCoinsDialog::SendCoinsDialog(bool _fCoinJoin, QWidget* parent) : QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this); QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this); QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this); - QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this); QAction *clipboardChangeAction = new QAction(tr("Copy change"), this); connect(clipboardQuantityAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardQuantity); connect(clipboardAmountAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardAmount); connect(clipboardFeeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardFee); connect(clipboardAfterFeeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardAfterFee); connect(clipboardBytesAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardBytes); - connect(clipboardLowOutputAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardLowOutput); connect(clipboardChangeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardChange); ui->labelCoinControlQuantity->addAction(clipboardQuantityAction); ui->labelCoinControlAmount->addAction(clipboardAmountAction); ui->labelCoinControlFee->addAction(clipboardFeeAction); ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction); ui->labelCoinControlBytes->addAction(clipboardBytesAction); - ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction); ui->labelCoinControlChange->addAction(clipboardChangeAction); // init transaction fee section @@ -911,12 +907,6 @@ void SendCoinsDialog::coinControlClipboardBytes() GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, "")); } -// Coin Control: copy label "Dust" to clipboard -void SendCoinsDialog::coinControlClipboardLowOutput() -{ - GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text()); -} - // Coin Control: copy label "Change" to clipboard void SendCoinsDialog::coinControlClipboardChange() { diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 1376f781b43e7..7d9ea5196d223 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -100,7 +100,6 @@ private Q_SLOTS: void coinControlClipboardFee(); void coinControlClipboardAfterFee(); void coinControlClipboardBytes(); - void coinControlClipboardLowOutput(); void coinControlClipboardChange(); void updateFeeSectionControls(); void updateNumberOfBlocks(int count, const QDateTime& blockDate, const QString& blockHash, double nVerificationProgress, bool header, SynchronizationState sync_state); diff --git a/src/qt/test/uritests.cpp b/src/qt/test/uritests.cpp index 0633282274080..ae1a23474b830 100644 --- a/src/qt/test/uritests.cpp +++ b/src/qt/test/uritests.cpp @@ -58,12 +58,38 @@ void URITests::uriTests() uri.setUrl(QString("dash:XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg?req-message=Some Example Address")); QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + // Commas in amounts are not allowed. uri.setUrl(QString("dash:XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg?amount=1,000&label=Some Example")); QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); uri.setUrl(QString("dash:XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg?amount=1,000.0&label=Some Example")); QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); + // There are two amount specifications. The last value wins. + uri.setUrl(QString("dash:XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg?amount=100&amount=200&label=Wikipedia Example")); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(rv.address == QString("XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg")); + QVERIFY(rv.amount == 20000000000LL); + QVERIFY(rv.label == QString("Wikipedia Example")); + + // The first amount value is correct. However, the second amount value is not valid. Hence, the URI is not valid. + uri.setUrl(QString("dash:XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg?amount=100&amount=1,000&label=Wikipedia Example")); + QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); + + // Test label containing a question mark ('?'). + uri.setUrl(QString("dash:XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg?amount=100&label=?")); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(rv.address == QString("XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg")); + QVERIFY(rv.amount == 10000000000LL); + QVERIFY(rv.label == QString("?")); + + // Escape sequences are not supported. + uri.setUrl(QString("dash:XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg?amount=100&label=%3F")); + QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(rv.address == QString("XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg")); + QVERIFY(rv.amount == 10000000000LL); + QVERIFY(rv.label == QString("%3F")); + uri.setUrl(QString("dash:XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg?amount=100&label=Some Example&message=Some Example Message")); QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); QVERIFY(rv.address == QString("XwnLY9Tf7Zsef8gMGL2fhWA9ZmMjt4KPwg")); diff --git a/src/rpc/node.cpp b/src/rpc/node.cpp index 0e9d6dd023ee5..97d6188828b93 100644 --- a/src/rpc/node.cpp +++ b/src/rpc/node.cpp @@ -892,7 +892,7 @@ static RPCHelpMan getmemoryinfo() { {"mode", RPCArg::Type::STR, RPCArg::Default{"stats"}, "determines what kind of information is returned.\n" " - \"stats\" returns general statistics about memory usage in the daemon.\n" - " - \"mallocinfo\" returns an XML string describing low-level heap state (only available if compiled with glibc 2.10+)."}, + " - \"mallocinfo\" returns an XML string describing low-level heap state (only available if compiled with glibc)."}, }, { RPCResult{"mode \"stats\"", diff --git a/src/wallet/sqlite.h b/src/wallet/sqlite.h index d7135679ff8af..96eba8d333bf1 100644 --- a/src/wallet/sqlite.h +++ b/src/wallet/sqlite.h @@ -8,10 +8,11 @@ #include #include -#include - struct bilingual_str; +struct sqlite3_stmt; +struct sqlite3; + namespace wallet { class SQLiteDatabase; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index ae580390bd0b6..f5b38c98d5798 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3062,6 +3062,7 @@ std::shared_ptr CWallet::Create(WalletContext& context, const std::stri walletInstance->TopUpKeyPool(); if (chain && !AttachChain(walletInstance, *chain, error, warnings)) { + walletInstance->m_chain_notifications_handler.reset(); // Reset this pointer so that the wallet will actually be unloaded return nullptr; } diff --git a/test/functional/test_framework/authproxy.py b/test/functional/test_framework/authproxy.py index aace16fd979b7..9492cb9ef6763 100644 --- a/test/functional/test_framework/authproxy.py +++ b/test/functional/test_framework/authproxy.py @@ -59,7 +59,7 @@ def __init__(self, rpc_error, http_status=None): self.http_status = http_status -def EncodeDecimal(o): +def serialization_fallback(o): if isinstance(o, decimal.Decimal): return str(o) raise TypeError(repr(o) + " is not JSON serializable") @@ -126,7 +126,7 @@ def get_request(self, *args, **argsn): log.debug("-{}-> {} {}".format( AuthServiceProxy.__id_count, self._service_name, - json.dumps(args or argsn, default=EncodeDecimal, ensure_ascii=self.ensure_ascii), + json.dumps(args or argsn, default=serialization_fallback, ensure_ascii=self.ensure_ascii), )) if args and argsn: params = dict(args=args, **argsn) @@ -140,7 +140,7 @@ def get_request(self, *args, **argsn): 'id': AuthServiceProxy.__id_count} def __call__(self, *args, **argsn): - postdata = json.dumps(self.get_request(*args, **argsn), default=EncodeDecimal, ensure_ascii=self.ensure_ascii) + postdata = json.dumps(self.get_request(*args, **argsn), default=serialization_fallback, ensure_ascii=self.ensure_ascii) response, status = self._request('POST', self.__url.path, postdata.encode('utf-8')) if response['error'] is not None: raise JSONRPCException(response['error'], status) @@ -154,7 +154,7 @@ def __call__(self, *args, **argsn): return response['result'] def batch(self, rpc_call_list): - postdata = json.dumps(list(rpc_call_list), default=EncodeDecimal, ensure_ascii=self.ensure_ascii) + postdata = json.dumps(list(rpc_call_list), default=serialization_fallback, ensure_ascii=self.ensure_ascii) log.debug("--> " + postdata) response, status = self._request('POST', self.__url.path, postdata.encode('utf-8')) if status != HTTPStatus.OK: @@ -187,7 +187,7 @@ def _get_response(self): response = json.loads(responsedata, parse_float=decimal.Decimal) elapsed = time.time() - req_start_time if "error" in response and response["error"] is None: - log.debug("<-%s- [%.6f] %s" % (response["id"], elapsed, json.dumps(response["result"], default=EncodeDecimal, ensure_ascii=self.ensure_ascii))) + log.debug("<-%s- [%.6f] %s" % (response["id"], elapsed, json.dumps(response["result"], default=serialization_fallback, ensure_ascii=self.ensure_ascii))) else: log.debug("<-- [%.6f] %s" % (elapsed, responsedata)) return response, http_response.status diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 1139af6457d08..46acff0d46b5e 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -22,7 +22,10 @@ import collections from pathlib import Path -from .authproxy import JSONRPCException +from .authproxy import ( + JSONRPCException, + serialization_fallback, +) from .descriptors import descsum_create from .messages import NODE_P2P_V2 from .p2p import P2P_SERVICES, P2P_SUBVERSION @@ -37,7 +40,6 @@ wait_until_helper, p2p_port, get_chain_folder, - EncodeDecimal, ) BITCOIND_PROC_WAIT_TIMEOUT = 60 @@ -811,7 +813,7 @@ def arg_to_cli(arg): elif arg is None: return 'null' elif isinstance(arg, dict) or isinstance(arg, list): - return json.dumps(arg, default=EncodeDecimal) + return json.dumps(arg, default=serialization_fallback) else: return str(arg) diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 314dddb9d1371..1fde5301fbca8 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -228,12 +228,6 @@ def check_json_precision(): raise RuntimeError("JSON encode/decode loses precision") -def EncodeDecimal(o): - if isinstance(o, Decimal): - return str(o) - raise TypeError(repr(o) + " is not JSON serializable") - - def count_bytes(hex_string): return len(bytearray.fromhex(hex_string)) diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 45210f23c4d97..89e9893a23a64 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -431,6 +431,8 @@ def main(): parser.add_argument('--tmpdirprefix', '-t', default=tempfile.gettempdir(), help="Root directory for datadirs") parser.add_argument('--failfast', '-F', action='store_true', help='stop execution after the first test failure') parser.add_argument('--filter', help='filter scripts to run by regular expression') + parser.add_argument('--skipunit', '-u', action='store_true', help='skip unit tests for the test framework') + args, unknown_args = parser.parse_known_args() if not args.ansi: @@ -542,9 +544,10 @@ def main(): combined_logs_len=args.combinedlogslen, failfast=args.failfast, use_term_control=args.ansi, + skipunit=args.skipunit, ) -def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, attempts=1, enable_coverage=False, args=None, combined_logs_len=0,failfast=False, use_term_control): +def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, attempts=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False, use_term_control, skipunit=False): args = args or [] # Warn if dashd is already running @@ -561,20 +564,20 @@ def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, attempts=1, enab if os.path.isdir(cache_dir): print("%sWARNING!%s There is a cache directory here: %s. If tests fail unexpectedly, try deleting the cache directory." % (BOLD[1], BOLD[0], cache_dir)) - # Test Framework Tests - print("Running Unit Tests for Test Framework Modules") tests_dir = src_dir + '/test/functional/' # This allows `test_runner.py` to work from an out-of-source build directory using a symlink, # a hard link or a copy on any platform. See https://github.com/bitcoin/bitcoin/pull/27561. sys.path.append(tests_dir) - test_framework_tests = unittest.TestSuite() - for module in TEST_FRAMEWORK_MODULES: - test_framework_tests.addTest(unittest.TestLoader().loadTestsFromName("test_framework.{}".format(module))) - result = unittest.TextTestRunner(verbosity=1, failfast=True).run(test_framework_tests) - if not result.wasSuccessful(): - sys.exit("Early exiting after failure in TestFramework unit tests") + if not skipunit: + print("Running Unit Tests for Test Framework Modules") + test_framework_tests = unittest.TestSuite() + for module in TEST_FRAMEWORK_MODULES: + test_framework_tests.addTest(unittest.TestLoader().loadTestsFromName("test_framework.{}".format(module))) + result = unittest.TextTestRunner(verbosity=1, failfast=True).run(test_framework_tests) + if not result.wasSuccessful(): + sys.exit("Early exiting after failure in TestFramework unit tests") flags = ['--cachedir={}'.format(cache_dir)] + args