diff --git a/.travis.yml b/.travis.yml index 32c6728b1ad23..a8ccd46c0df43 100644 --- a/.travis.yml +++ b/.travis.yml @@ -81,7 +81,9 @@ before_install: install: - set -o errexit; source ./ci/test/04_install.sh before_script: - - set -o errexit; source ./ci/test/05_before_script.sh + # Temporary workaround for https://github.com/bitcoin/bitcoin/issues/16368 + - for i in {1..4}; do echo "$(sleep 500)" ; done & + - set -o errexit; source ./ci/test/05_before_script.sh &> "/dev/null" script: - if [ $SECONDS -gt 1200 ]; then set +o errexit; echo "Travis early exit to cache current state"; false; else set -o errexit; source .travis/test_06_script.sh; fi after_script: diff --git a/ci/dash/test_unittests.sh b/ci/dash/test_unittests.sh index 0b7bbf46db202..1b0796b34084c 100755 --- a/ci/dash/test_unittests.sh +++ b/ci/dash/test_unittests.sh @@ -24,7 +24,6 @@ export BOOST_TEST_LOG_LEVEL=test_suite cd build-ci/dashcore-$BUILD_TARGET -bash -c "${CI_WAIT}" & # Print dots in case the unit tests take a long time to run if [ "$DIRECT_WINE_EXEC_TESTS" = "true" ]; then # Inside Docker, binfmt isn't working so we can't trust in make invoking windows binaries correctly wine ./src/test/test_dash.exe diff --git a/configure.ac b/configure.ac index b2a755eadd56d..8ec97e8591a92 100644 --- a/configure.ac +++ b/configure.ac @@ -1023,7 +1023,21 @@ AC_CHECK_DECLS([bswap_16, bswap_32, bswap_64],,, #include #endif]) -AC_CHECK_DECLS([__builtin_clz, __builtin_clzl, __builtin_clzll]) +AC_MSG_CHECKING(for __builtin_clzl) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]], [[ + (void) __builtin_clzl(0); + ]])], + [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_BUILTIN_CLZL, 1, [Define this symbol if you have __builtin_clzl])], + [ AC_MSG_RESULT(no)] +) + +AC_MSG_CHECKING(for __builtin_clzll) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]], [[ + (void) __builtin_clzll(0); + ]])], + [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_BUILTIN_CLZLL, 1, [Define this symbol if you have __builtin_clzll])], + [ AC_MSG_RESULT(no)] +) dnl Check for mallopt(M_ARENA_MAX) (to set glibc arenas) AC_MSG_CHECKING(for mallopt M_ARENA_MAX) diff --git a/src/crypto/common.h b/src/crypto/common.h index e14af1f4c40d2..841eb5115ef3e 100644 --- a/src/crypto/common.h +++ b/src/crypto/common.h @@ -89,12 +89,12 @@ void static inline WriteBE64(unsigned char* ptr, uint64_t x) /** Return the smallest number n such that (x >> n) == 0 (or 64 if the highest bit in x is set. */ uint64_t static inline CountBits(uint64_t x) { -#if HAVE_DECL___BUILTIN_CLZL +#if HAVE_BUILTIN_CLZL if (sizeof(unsigned long) >= sizeof(uint64_t)) { return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0; } #endif -#if HAVE_DECL___BUILTIN_CLZLL +#if HAVE_BUILTIN_CLZLL if (sizeof(unsigned long long) >= sizeof(uint64_t)) { return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0; } diff --git a/src/index/base.cpp b/src/index/base.cpp index cb860f544d4ab..5a55e0bbfd4b7 100644 --- a/src/index/base.cpp +++ b/src/index/base.cpp @@ -22,7 +22,7 @@ template static void FatalError(const char* fmt, const Args&... args) { std::string strMessage = tfm::format(fmt, args...); - SetMiscWarning(strMessage); + SetMiscWarning(Untranslated(strMessage)); LogPrintf("*** %s\n", strMessage); AbortError(_("A fatal internal error occurred, see debug.log for details")); StartShutdown(); diff --git a/src/interfaces/node.h b/src/interfaces/node.h index a0ed8c7bfa30f..fb39826705819 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -11,6 +11,7 @@ #include // For Network #include // For SecureString #include +#include #include #include @@ -171,7 +172,7 @@ class Node virtual void initParameterInteraction() = 0; //! Get warnings. - virtual std::string getWarnings() = 0; + virtual bilingual_str getWarnings() = 0; // Get log flags. virtual uint64_t getLogCategories() = 0; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index f2889fbbe3de2..2c630dc42aab3 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2119,7 +2119,7 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic // mempool entries added before this time have likely expired from mapRelay const std::chrono::seconds longlived_mempool_time = GetTime() - RELAY_TX_CACHE_TIME; // Get last mempool request time - const std::chrono::seconds mempool_req = !pfrom.RelayAddrsWithConn() ? pfrom.m_tx_relay->m_last_mempool_req.load() + const std::chrono::seconds mempool_req = pfrom.RelayAddrsWithConn() ? pfrom.m_tx_relay->m_last_mempool_req.load() : std::chrono::seconds::min(); // Process as many TX items from the front of the getdata queue as @@ -2823,14 +2823,6 @@ void PeerManagerImpl::ProcessMessage( PeerRef peer = GetPeerRef(pfrom.GetId()); if (peer == nullptr) return; - if (!(pfrom.GetLocalServices() & NODE_BLOOM) && - (msg_type == NetMsgType::FILTERLOAD || - msg_type == NetMsgType::FILTERADD)) - { - Misbehaving(pfrom.GetId(), 100); - return; - } - if (msg_type == NetMsgType::VERSION) { // Each connection can only send one version message if (pfrom.nVersion != 0) @@ -4190,6 +4182,10 @@ void PeerManagerImpl::ProcessMessage( } if (msg_type == NetMsgType::FILTERLOAD) { + if (!(pfrom.GetLocalServices() & NODE_BLOOM)) { + pfrom.fDisconnect = true; + return; + } CBloomFilter filter; vRecv >> filter; @@ -4208,6 +4204,10 @@ void PeerManagerImpl::ProcessMessage( } if (msg_type == NetMsgType::FILTERADD) { + if (!(pfrom.GetLocalServices() & NODE_BLOOM)) { + pfrom.fDisconnect = true; + return; + } std::vector vData; vRecv >> vData; @@ -4231,13 +4231,15 @@ void PeerManagerImpl::ProcessMessage( } if (msg_type == NetMsgType::FILTERCLEAR) { + if (!(pfrom.GetLocalServices() & NODE_BLOOM)) { + pfrom.fDisconnect = true; + return; + } if (!pfrom.RelayAddrsWithConn()) { return; } LOCK(pfrom.m_tx_relay->cs_filter); - if (pfrom.GetLocalServices() & NODE_BLOOM) { - pfrom.m_tx_relay->pfilter = nullptr; - } + pfrom.m_tx_relay->pfilter = nullptr; pfrom.m_tx_relay->fRelayTxes = true; return; } diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index dad926ed0a976..abbf8fa145656 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -246,7 +246,7 @@ class NodeImpl : public Node std::string getNetwork() override { return Params().NetworkIDString(); } void initLogging() override { InitLogging(gArgs); } void initParameterInteraction() override { InitParameterInteraction(gArgs); } - std::string getWarnings() override { return GetWarnings(true); } + bilingual_str getWarnings() override { return GetWarnings(true); } uint64_t getLogCategories() override { return LogInstance().GetCategoryMask(); } bool baseInitialize() override { diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 3a3697f13f477..b85b72cf4d8b9 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -143,7 +143,7 @@ BitcoinCore::BitcoinCore(interfaces::Node& node) : void BitcoinCore::handleRunawayException(const std::exception_ptr e) { PrintExceptionContinue(e, "Runaway exception"); - Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings())); + Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings().translated)); } void BitcoinCore::initialize() @@ -747,7 +747,7 @@ int GuiMain(int argc, char* argv[]) } } catch (...) { PrintExceptionContinue(std::current_exception(), "Runaway exception"); - app.handleRunawayException(QString::fromStdString(node->getWarnings())); + app.handleRunawayException(QString::fromStdString(node->getWarnings().translated)); } return rv; } diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 83c0cf0e187ef..c04305881db67 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -92,7 +92,7 @@ class AmountLineEdit: public QLineEdit if (valid) { val = qBound(m_min_amount, val, m_max_amount); - setText(BitcoinUnits::format(currentUnit, val, false, BitcoinUnits::separatorAlways)); + setText(BitcoinUnits::format(currentUnit, val, false, BitcoinUnits::SeparatorStyle::ALWAYS)); } } @@ -103,7 +103,7 @@ class AmountLineEdit: public QLineEdit void setValue(const CAmount& value) { - setText(BitcoinUnits::format(currentUnit, value, false, BitcoinUnits::separatorAlways)); + setText(BitcoinUnits::format(currentUnit, value, false, BitcoinUnits::SeparatorStyle::ALWAYS)); Q_EMIT valueChanged(); } @@ -130,7 +130,7 @@ class AmountLineEdit: public QLineEdit currentUnit = unit; amountValidator->updateUnit(unit); - setPlaceholderText(BitcoinUnits::format(currentUnit, m_min_amount, false, BitcoinUnits::separatorAlways)); + setPlaceholderText(BitcoinUnits::format(currentUnit, m_min_amount, false, BitcoinUnits::SeparatorStyle::ALWAYS)); if(valid) setValue(val); else @@ -142,7 +142,7 @@ class AmountLineEdit: public QLineEdit ensurePolished(); const QFontMetrics fm(fontMetrics()); int h = 0; - int w = GUIUtil::TextWidth(fm, BitcoinUnits::format(BitcoinUnits::DASH, BitcoinUnits::maxMoney(), false, BitcoinUnits::separatorAlways)); + int w = GUIUtil::TextWidth(fm, BitcoinUnits::format(BitcoinUnits::DASH, BitcoinUnits::maxMoney(), false, BitcoinUnits::SeparatorStyle::ALWAYS)); w += 2; // cursor blinking space w += GUIUtil::dashThemeActive() ? 24 : 0; // counteract padding from css return QSize(w, h); diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 9f7ab54f47b72..126fac8e82b31 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -139,7 +139,7 @@ QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, Separator // confused with the decimal marker. QChar thin_sp(THIN_SP_CP); int q_size = quotient_str.size(); - if (separators == separatorAlways || (separators == separatorStandard && q_size > 4)) + if (separators == SeparatorStyle::ALWAYS || (separators == SeparatorStyle::STANDARD && q_size > 4)) for (int i = 3; i < q_size; i += 3) quotient_str.insert(q_size - i, thin_sp); diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index e3e727881f64d..55bd934a57115 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -47,11 +47,11 @@ class BitcoinUnits: public QAbstractListModel duffs }; - enum SeparatorStyle + enum class SeparatorStyle { - separatorNever, - separatorStandard, - separatorAlways + NEVER, + STANDARD, + ALWAYS }; //! @name Static API @@ -71,16 +71,16 @@ class BitcoinUnits: public QAbstractListModel //! Number of decimals left static int decimals(int unit); //! Format as string - static QString format(int unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = separatorStandard, bool justify = false); - static QString simpleFormat(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=separatorStandard); + static QString format(int unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = SeparatorStyle::STANDARD, bool justify = false); + static QString simpleFormat(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD); //! Format as string (with unit) - static QString formatWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=separatorStandard); + static QString formatWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD); //! Format as HTML string (with unit) - static QString formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=separatorStandard); + static QString formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD); //! Format as string (with unit) of fixed length to preserve privacy, if it is set. static QString formatWithPrivacy(int unit, const CAmount& amount, SeparatorStyle separators, bool privacy); //! Format as string (with unit) but floor value up to "digits" settings - static QString floorWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=separatorStandard); + static QString floorWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD); static QString floorHtmlWithPrivacy(int unit, const CAmount& amount, SeparatorStyle separators, bool privacy); //! Parse string to coin amount static bool parse(int unit, const QString &value, CAmount *val_out); diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 23f15b4b236a9..10e4b7125a05c 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -202,7 +202,7 @@ enum BlockSource ClientModel::getBlockSource() const QString ClientModel::getStatusBarWarnings() const { - return QString::fromStdString(m_node.getWarnings()); + return QString::fromStdString(m_node.getWarnings().translated); } OptionsModel *ClientModel::getOptionsModel() diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index 3a89a5b8e8145..ba7a97036b440 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -929,12 +929,6 @@ 426 - - - 300 - 0 - - diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 1a82f2de4a26c..ca885f490c10a 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -406,7 +406,7 @@ QString formatBitcoinURI(const SendCoinsRecipient &info) if (info.amount) { - ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::DASH, info.amount, false, BitcoinUnits::separatorNever)); + ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::DASH, info.amount, false, BitcoinUnits::SeparatorStyle::NEVER)); paramCount++; } diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 682ebb5d9ea52..c417d8adaa45e 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -81,7 +81,7 @@ class TxViewDelegate : public QAbstractItemDelegate colorForeground = qvariant_cast(indexAmount.data(Qt::ForegroundRole)); // Note: do NOT use Qt::DisplayRole, have format properly here qint64 nAmount = index.data(TransactionTableModel::AmountRole).toLongLong(); - QString strAmount = BitcoinUnits::floorWithUnit(unit, nAmount, true, BitcoinUnits::separatorAlways); + QString strAmount = BitcoinUnits::floorWithUnit(unit, nAmount, true, BitcoinUnits::SeparatorStyle::ALWAYS); painter->setPen(colorForeground); painter->drawText(rectTopHalf, Qt::AlignRight | Qt::AlignVCenter, strAmount); @@ -209,20 +209,20 @@ void OverviewPage::setBalance(const interfaces::WalletBalances& balances) int unit = walletModel->getOptionsModel()->getDisplayUnit(); m_balances = balances; if (walletModel->wallet().privateKeysDisabled()) { - ui->labelBalance->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.watch_only_balance, BitcoinUnits::separatorAlways, m_privacy)); - ui->labelUnconfirmed->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.unconfirmed_watch_only_balance, BitcoinUnits::separatorAlways, m_privacy)); - ui->labelImmature->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.immature_watch_only_balance, BitcoinUnits::separatorAlways, m_privacy)); - ui->labelTotal->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, BitcoinUnits::separatorAlways, m_privacy)); + ui->labelBalance->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); + ui->labelUnconfirmed->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.unconfirmed_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); + ui->labelImmature->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.immature_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); + ui->labelTotal->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); } else { - ui->labelBalance->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.balance, BitcoinUnits::separatorAlways, m_privacy)); - ui->labelUnconfirmed->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.unconfirmed_balance, BitcoinUnits::separatorAlways, m_privacy)); - ui->labelImmature->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.immature_balance, BitcoinUnits::separatorAlways, m_privacy)); - ui->labelAnonymized->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.anonymized_balance, BitcoinUnits::separatorAlways, m_privacy)); - ui->labelTotal->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, BitcoinUnits::separatorAlways, m_privacy)); - ui->labelWatchAvailable->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.watch_only_balance, BitcoinUnits::separatorAlways, m_privacy)); - ui->labelWatchPending->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.unconfirmed_watch_only_balance, BitcoinUnits::separatorAlways, m_privacy)); - ui->labelWatchImmature->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.immature_watch_only_balance, BitcoinUnits::separatorAlways, m_privacy)); - ui->labelWatchTotal->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, BitcoinUnits::separatorAlways, m_privacy)); + ui->labelBalance->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); + ui->labelUnconfirmed->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.unconfirmed_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); + ui->labelImmature->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); + ui->labelAnonymized->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.anonymized_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); + ui->labelTotal->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); + ui->labelWatchAvailable->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); + ui->labelWatchPending->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.unconfirmed_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); + ui->labelWatchImmature->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.immature_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); + ui->labelWatchTotal->setText(BitcoinUnits::floorHtmlWithPrivacy(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); } // only show immature (newly mined) balance if it's non-zero, so as not to complicate things // for the non-mining users @@ -350,7 +350,7 @@ void OverviewPage::updateCoinJoinProgress() if (!walletModel || !clientModel || clientModel->node().shutdownRequested() || !clientModel->masternodeSync().isBlockchainSynced()) return; QString strAmountAndRounds; - QString strCoinJoinAmount = BitcoinUnits::formatHtmlWithUnit(nDisplayUnit, clientModel->coinJoinOptions().getAmount() * COIN, false, BitcoinUnits::separatorAlways); + QString strCoinJoinAmount = BitcoinUnits::formatHtmlWithUnit(nDisplayUnit, clientModel->coinJoinOptions().getAmount() * COIN, false, BitcoinUnits::SeparatorStyle::ALWAYS); if(m_balances.balance == 0) { @@ -381,7 +381,7 @@ void OverviewPage::updateCoinJoinProgress() strCoinJoinAmount = strCoinJoinAmount.remove(strCoinJoinAmount.indexOf("."), BitcoinUnits::decimals(nDisplayUnit) + 1); strAmountAndRounds = strCoinJoinAmount + " / " + tr("%n Rounds", "", clientModel->coinJoinOptions().getRounds()); } else { - QString strMaxToAnonymize = BitcoinUnits::formatHtmlWithUnit(nDisplayUnit, nMaxToAnonymize, false, BitcoinUnits::separatorAlways); + QString strMaxToAnonymize = BitcoinUnits::formatHtmlWithUnit(nDisplayUnit, nMaxToAnonymize, false, BitcoinUnits::SeparatorStyle::ALWAYS); ui->labelAmountRounds->setToolTip(tr("Not enough compatible inputs to mix %2,
" "will mix %3 instead") .arg(GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_ERROR)) diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp index 5544742e1e10e..b11ccf06df4e7 100644 --- a/src/qt/recentrequeststablemodel.cpp +++ b/src/qt/recentrequeststablemodel.cpp @@ -84,7 +84,7 @@ QVariant RecentRequestsTableModel::data(const QModelIndex &index, int role) cons if (rec->recipient.amount == 0 && role == Qt::DisplayRole) return tr("(no amount requested)"); else if (role == Qt::EditRole) - return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), rec->recipient.amount, false, BitcoinUnits::separatorNever); + return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), rec->recipient.amount, false, BitcoinUnits::SeparatorStyle::NEVER); else return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), rec->recipient.amount); } diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index d948276aa22cc..fc01d1fcb2b90 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -144,7 +144,7 @@ void TestGUI(interfaces::Node& node) QString balanceText = balanceLabel->text(); int unit = walletModel.getOptionsModel()->getDisplayUnit(); CAmount balance = walletModel.wallet().getBalance(); - QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false /*, BitcoinUnits::separatorAlways*/); + QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false /*, BitcoinUnits::SeparatorStyle::ALWAYS*/); QCOMPARE(balanceText, balanceComparison); } @@ -165,7 +165,7 @@ void TestGUI(interfaces::Node& node) QString balanceText = balanceLabel->text().trimmed(); int unit = walletModel.getOptionsModel()->getDisplayUnit(); CAmount balance = walletModel.wallet().getBalance(); - QString balanceComparison = BitcoinUnits::floorHtmlWithPrivacy(unit, balance, BitcoinUnits::separatorAlways, false); + QString balanceComparison = BitcoinUnits::floorHtmlWithPrivacy(unit, balance, BitcoinUnits::SeparatorStyle::ALWAYS, false); QCOMPARE(balanceText, balanceComparison); // Check Request Payment button diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 06f20e3effce8..de49edcbd61f4 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -603,7 +603,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const case ToAddress: return formatTxToAddress(rec, false); case Amount: - return formatTxAmount(rec, true, BitcoinUnits::separatorAlways); + return formatTxAmount(rec, true, BitcoinUnits::SeparatorStyle::ALWAYS); } break; case Qt::EditRole: @@ -688,14 +688,14 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const details.append(QString::fromStdString(rec->strAddress)); details.append(" "); } - details.append(formatTxAmount(rec, false, BitcoinUnits::separatorNever)); + details.append(formatTxAmount(rec, false, BitcoinUnits::SeparatorStyle::NEVER)); return details; } case ConfirmedRole: return rec->status.status == TransactionStatus::Status::Confirming || rec->status.status == TransactionStatus::Status::Confirmed; case FormattedAmountRole: // Used for copy/export, so don't include separators - return formatTxAmount(rec, false, BitcoinUnits::separatorNever); + return formatTxAmount(rec, false, BitcoinUnits::SeparatorStyle::NEVER); case StatusRole: return rec->status.status; } diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 16e85b935ea06..9a5df9fd09d5e 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -105,7 +105,7 @@ class TransactionTableModel : public QAbstractTableModel QString formatTxDate(const TransactionRecord *wtx) const; QString formatTxType(const TransactionRecord *wtx) const; QString formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const; - QString formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true, BitcoinUnits::SeparatorStyle separators=BitcoinUnits::separatorStandard) const; + QString formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true, BitcoinUnits::SeparatorStyle separators=BitcoinUnits::SeparatorStyle::STANDARD) const; QVariant amountColor(const TransactionRecord *rec) const; QString formatTooltip(const TransactionRecord *rec) const; QVariant txStatusDecoration(const TransactionRecord *wtx) const; diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index dbef3450ed370..7c777b4c11ee2 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -583,7 +583,7 @@ void TransactionView::computeSum() for (QModelIndex index : selection){ amount += index.data(TransactionTableModel::AmountRole).toLongLong(); } - QString strAmount(BitcoinUnits::formatWithUnit(nDisplayUnit, amount, true, BitcoinUnits::separatorAlways)); + QString strAmount(BitcoinUnits::formatWithUnit(nDisplayUnit, amount, true, BitcoinUnits::SeparatorStyle::ALWAYS)); if (amount < 0) strAmount = "" + strAmount + ""; Q_EMIT trxAmount(strAmount); } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index d39985e0303d1..d09184abadcf8 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -1766,7 +1767,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) obj.pushKV("softforks", softforks); - obj.pushKV("warnings", GetWarnings(false)); + obj.pushKV("warnings", GetWarnings(false).original); return obj; } diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 7e584ea74f9eb..999f2f2e60d2b 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -272,7 +273,7 @@ static UniValue generatetoaddress(const JSONRPCRequest& request) RPCExamples{ "\nGenerate 11 blocks to myaddress\n" + HelpExampleCli("generatetoaddress", "11 \"myaddress\"") - + "If you are running the Dash Core wallet, you can get a new address to send the newly generated coins to with:\n" + + "If you are using the " PACKAGE_NAME " wallet, you can get a new address to send the newly generated coins to with:\n" + HelpExampleCli("getnewaddress", "")}, }.Check(request); @@ -459,7 +460,7 @@ static UniValue getmininginfo(const JSONRPCRequest& request) obj.pushKV("networkhashps", getnetworkhashps(request)); obj.pushKV("pooledtx", (uint64_t)mempool.size()); obj.pushKV("chain", Params().NetworkIDString()); - obj.pushKV("warnings", GetWarnings(false)); + obj.pushKV("warnings", GetWarnings(false).original); return obj; } diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 7a7ba6be8aa6c..14876c148832f 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -594,7 +595,7 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request) } } obj.pushKV("localaddresses", localAddresses); - obj.pushKV("warnings", GetWarnings(false)); + obj.pushKV("warnings", GetWarnings(false).original); return obj; } diff --git a/src/script/standard.h b/src/script/standard.h index a2983bd50fa74..51feadd34b5c8 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -87,6 +87,7 @@ struct ScriptHash : public BaseHash // These don't do what you'd expect. // Use ScriptHash(GetScriptForDestination(...)) instead. explicit ScriptHash(const PKHash& hash) = delete; + explicit ScriptHash(const uint160& hash) : BaseHash(hash) {} explicit ScriptHash(const CScript& script); explicit ScriptHash(const CScriptID& script); diff --git a/src/shutdown.cpp b/src/shutdown.cpp index 8421f4f81bf68..33c02106fa661 100644 --- a/src/shutdown.cpp +++ b/src/shutdown.cpp @@ -23,7 +23,7 @@ bool AbortNode(const std::string& strMessage, bilingual_str user_message) { - SetMiscWarning(strMessage); + SetMiscWarning(Untranslated(strMessage)); LogPrintf("*** %s\n", strMessage); if (user_message.empty()) { user_message = _("A fatal internal error occurred, see debug.log for details"); diff --git a/src/test/timedata_tests.cpp b/src/test/timedata_tests.cpp index 8c880babd12aa..1dcee23bbbc61 100644 --- a/src/test/timedata_tests.cpp +++ b/src/test/timedata_tests.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -66,7 +67,7 @@ BOOST_AUTO_TEST_CASE(addtimedata) MultiAddTimeData(1, DEFAULT_MAX_TIME_ADJUSTMENT + 1); //filter size 5 } - BOOST_CHECK(GetWarnings(true).find("clock is wrong") != std::string::npos); + BOOST_CHECK(GetWarnings(true).original.find("clock is wrong") != std::string::npos); // nTimeOffset is not changed if the median of offsets exceeds DEFAULT_MAX_TIME_ADJUSTMENT BOOST_CHECK_EQUAL(GetTimeOffset(), 0); diff --git a/src/timedata.cpp b/src/timedata.cpp index 51dc62a6458ab..f53fbe231be68 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -92,7 +92,7 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample) if (!fMatch) { fDone = true; bilingual_str strMessage = strprintf(_("Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly."), PACKAGE_NAME); - SetMiscWarning(strMessage.translated); + SetMiscWarning(strMessage); uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING); } } diff --git a/src/validation.cpp b/src/validation.cpp index 4210ae99e7159..33b78d5e1abf9 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2655,20 +2655,20 @@ void CChainState::PruneAndFlush() } } -static void DoWarning(const std::string& strWarning) +static void DoWarning(const bilingual_str& warning) { static bool fWarned = false; - SetMiscWarning(strWarning); + SetMiscWarning(warning); if (!fWarned) { - AlertNotify(strWarning); + AlertNotify(warning.original); fWarned = true; } } /** Private helper function that concatenates warning messages. */ -static void AppendWarning(std::string& res, const std::string& warn) +static void AppendWarning(bilingual_str& res, const bilingual_str& warn) { - if (!res.empty()) res += ", "; + if (!res.empty()) res += Untranslated(", "); res += warn; } @@ -2685,7 +2685,7 @@ void CChainState::UpdateTip(const CBlockIndex* pindexNew) g_best_block_cv.notify_all(); } - std::string warningMessages; + bilingual_str warning_messages; assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); if (!this->IsInitialBlockDownload()) { @@ -2694,11 +2694,11 @@ void CChainState::UpdateTip(const CBlockIndex* pindexNew) WarningBitsConditionChecker checker(bit); ThresholdState state = checker.GetStateFor(pindex, m_params.GetConsensus(), warningcache[bit]); if (state == ThresholdState::ACTIVE || state == ThresholdState::LOCKED_IN) { - const std::string strWarning = strprintf(_("Warning: unknown new rules activated (versionbit %i)").translated, bit); + const bilingual_str warning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit); if (state == ThresholdState::ACTIVE) { - DoWarning(strWarning); + DoWarning(warning); } else { - AppendWarning(warningMessages, strWarning); + AppendWarning(warning_messages, warning); } } } @@ -2710,7 +2710,7 @@ void CChainState::UpdateTip(const CBlockIndex* pindexNew) FormatISO8601DateTime(pindexNew->GetBlockTime()), GuessVerificationProgress(m_params.TxData(), pindexNew), this->CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), this->CoinsTip().GetCacheSize(), m_evoDb.GetMemoryUsage() * (1.0 / (1<<20)), - !warningMessages.empty() ? strprintf(" warning='%s'", warningMessages) : ""); + !warning_messages.empty() ? strprintf(" warning='%s'", warning_messages.original) : ""); } /** Disconnect m_chain's tip. diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 4bff43c3d6e7a..f01e2c2f3f568 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -689,9 +689,8 @@ bool LegacyScriptPubKeyMan::SignTransaction(CMutableTransaction& tx, const std:: SigningResult LegacyScriptPubKeyMan::SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const { - CKeyID key_id(ToKeyID(pkhash)); CKey key; - if (!GetKey(key_id, key)) { + if (!GetKey(ToKeyID(pkhash), key)) { return SigningResult::PRIVATE_KEY_NOT_AVAILABLE; } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c23fcea7c672e..3df7610e80acc 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4689,7 +4689,7 @@ std::shared_ptr CWallet::Create(interfaces::Chain& chain, interfaces::C // Warn user every time a non-encrypted HD wallet is started if (walletInstance->IsHDEnabled() && !walletInstance->IsLocked()) { - SetMiscWarning(_("Make sure to encrypt your wallet and delete all non-encrypted backups after you have verified that the wallet works!").translated); + SetMiscWarning(_("Make sure to encrypt your wallet and delete all non-encrypted backups after you have verified that the wallet works!")); } if (gArgs.IsArgSet("-mintxfee")) { diff --git a/src/warnings.cpp b/src/warnings.cpp index c718712162feb..71c8ad5fb4892 100644 --- a/src/warnings.cpp +++ b/src/warnings.cpp @@ -6,18 +6,21 @@ #include #include +#include #include #include #include +#include + static Mutex g_warnings_mutex; -static std::string strMiscWarning GUARDED_BY(g_warnings_mutex); +static bilingual_str g_misc_warnings GUARDED_BY(g_warnings_mutex); static bool fLargeWorkInvalidChainFound GUARDED_BY(g_warnings_mutex) = false; -void SetMiscWarning(const std::string& strWarning) +void SetMiscWarning(const bilingual_str& warning) { LOCK(g_warnings_mutex); - strMiscWarning = strWarning; + g_misc_warnings = warning; } void SetfLargeWorkInvalidChainFound(bool flag) @@ -26,31 +29,33 @@ void SetfLargeWorkInvalidChainFound(bool flag) fLargeWorkInvalidChainFound = flag; } -std::string GetWarnings(bool verbose) +bilingual_str GetWarnings(bool verbose) { - std::string warnings_concise; - std::string warnings_verbose; - const std::string warning_separator = "
"; + bilingual_str warnings_concise; + std::vector warnings_verbose; LOCK(g_warnings_mutex); // Pre-release build warning if (!CLIENT_VERSION_IS_RELEASE) { - warnings_concise = "This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"; - warnings_verbose = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications").translated; + warnings_concise = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"); + warnings_verbose.emplace_back(warnings_concise); } // Misc warnings like out of disk space and clock is wrong - if (strMiscWarning != "") { - warnings_concise = strMiscWarning; - warnings_verbose += (warnings_verbose.empty() ? "" : warning_separator) + strMiscWarning; + if (!g_misc_warnings.empty()) { + warnings_concise = g_misc_warnings; + warnings_verbose.emplace_back(warnings_concise); } if (fLargeWorkInvalidChainFound) { - warnings_concise = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."; - warnings_verbose += (warnings_verbose.empty() ? "" : warning_separator) + _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.").translated; + warnings_concise = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."); + warnings_verbose.emplace_back(warnings_concise); + } + + if (verbose) { + return Join(warnings_verbose, Untranslated("
")); } - if (verbose) return warnings_verbose; - else return warnings_concise; + return warnings_concise; } diff --git a/src/warnings.h b/src/warnings.h index 0b880b74b15f5..e87b64a86ddb7 100644 --- a/src/warnings.h +++ b/src/warnings.h @@ -8,14 +8,16 @@ #include -void SetMiscWarning(const std::string& strWarning); +struct bilingual_str; + +void SetMiscWarning(const bilingual_str& warning); void SetfLargeWorkInvalidChainFound(bool flag); /** Format a string that describes several potential problems detected by the core. * @param[in] verbose bool - * - if true, get all warnings, translated (where possible), separated by
+ * - if true, get all warnings separated by
* - if false, get the most important warning * @returns the warning string */ -std::string GetWarnings(bool verbose); +bilingual_str GetWarnings(bool verbose); #endif // BITCOIN_WARNINGS_H diff --git a/test/functional/p2p_filter.py b/test/functional/p2p_filter.py index f5daa890922ac..430b68f21d76b 100755 --- a/test/functional/p2p_filter.py +++ b/test/functional/p2p_filter.py @@ -16,13 +16,15 @@ msg_filterclear, msg_filterload, msg_getdata, + msg_mempool, + msg_version, ) -from test_framework.mininode import P2PInterface +from test_framework.mininode import P2PInterface, mininode_lock from test_framework.script import MAX_SCRIPT_ELEMENT_SIZE from test_framework.test_framework import BitcoinTestFramework -class FilterNode(P2PInterface): +class P2PBloomFilter(P2PInterface): # This is a P2SH watch-only wallet watch_script_pubkey = 'a914ffffffffffffffffffffffffffffffffffffffff87' # The initial filter (n=10, fp=0.000001) with just the above scriptPubKey added @@ -34,6 +36,11 @@ class FilterNode(P2PInterface): nFlags=1, ) + def __init__(self): + super().__init__() + self._tx_received = False + self._merkleblock_received = False + def on_inv(self, message): want = msg_getdata() for i in message.inv: @@ -46,10 +53,30 @@ def on_inv(self, message): self.send_message(want) def on_merkleblock(self, message): - self.merkleblock_received = True + self._merkleblock_received = True def on_tx(self, message): - self.tx_received = True + self._tx_received = True + + @property + def tx_received(self): + with mininode_lock: + return self._tx_received + + @tx_received.setter + def tx_received(self, value): + with mininode_lock: + self._tx_received = value + + @property + def merkleblock_received(self): + with mininode_lock: + return self._merkleblock_received + + @merkleblock_received.setter + def merkleblock_received(self, value): + with mininode_lock: + self._merkleblock_received = value class FilterTest(BitcoinTestFramework): @@ -63,106 +90,143 @@ def set_test_params(self): def skip_test_if_missing_module(self): self.skip_if_no_wallet() - def test_size_limits(self, filter_node): + def test_size_limits(self, filter_peer): self.log.info('Check that too large filter is rejected') with self.nodes[0].assert_debug_log(['Misbehaving']): - filter_node.send_and_ping(msg_filterload(data=b'\xbb'*(MAX_BLOOM_FILTER_SIZE+1))) + filter_peer.send_and_ping(msg_filterload(data=b'\xbb'*(MAX_BLOOM_FILTER_SIZE+1))) self.log.info('Check that max size filter is accepted') with self.nodes[0].assert_debug_log([], unexpected_msgs=['Misbehaving']): - filter_node.send_and_ping(msg_filterload(data=b'\xbb'*(MAX_BLOOM_FILTER_SIZE))) - filter_node.send_and_ping(msg_filterclear()) + filter_peer.send_and_ping(msg_filterload(data=b'\xbb'*(MAX_BLOOM_FILTER_SIZE))) + filter_peer.send_and_ping(msg_filterclear()) self.log.info('Check that filter with too many hash functions is rejected') with self.nodes[0].assert_debug_log(['Misbehaving']): - filter_node.send_and_ping(msg_filterload(data=b'\xaa', nHashFuncs=MAX_BLOOM_HASH_FUNCS+1)) + filter_peer.send_and_ping(msg_filterload(data=b'\xaa', nHashFuncs=MAX_BLOOM_HASH_FUNCS+1)) self.log.info('Check that filter with max hash functions is accepted') with self.nodes[0].assert_debug_log([], unexpected_msgs=['Misbehaving']): - filter_node.send_and_ping(msg_filterload(data=b'\xaa', nHashFuncs=MAX_BLOOM_HASH_FUNCS)) + filter_peer.send_and_ping(msg_filterload(data=b'\xaa', nHashFuncs=MAX_BLOOM_HASH_FUNCS)) # Don't send filterclear until next two filteradd checks are done self.log.info('Check that max size data element to add to the filter is accepted') with self.nodes[0].assert_debug_log([], unexpected_msgs=['Misbehaving']): - filter_node.send_and_ping(msg_filteradd(data=b'\xcc'*(MAX_SCRIPT_ELEMENT_SIZE))) + filter_peer.send_and_ping(msg_filteradd(data=b'\xcc'*(MAX_SCRIPT_ELEMENT_SIZE))) self.log.info('Check that too large data element to add to the filter is rejected') with self.nodes[0].assert_debug_log(['Misbehaving']): - filter_node.send_and_ping(msg_filteradd(data=b'\xcc'*(MAX_SCRIPT_ELEMENT_SIZE+1))) + filter_peer.send_and_ping(msg_filteradd(data=b'\xcc'*(MAX_SCRIPT_ELEMENT_SIZE+1))) - filter_node.send_and_ping(msg_filterclear()) + filter_peer.send_and_ping(msg_filterclear()) - def run_test(self): - filter_node = self.nodes[0].add_p2p_connection(FilterNode()) - - self.test_size_limits(filter_node) + def test_msg_mempool(self): + self.log.info("Check that a node with bloom filters enabled services p2p mempool messages") + filter_peer = P2PBloomFilter() - filter_node = self.nodes[0].add_p2p_connection(FilterNode()) - - self.log.info('Check that too large filter is rejected') - with self.nodes[0].assert_debug_log(['Misbehaving']): - filter_node.send_and_ping(msg_filterload(data=b'\xaa', nHashFuncs=MAX_BLOOM_HASH_FUNCS+1)) - with self.nodes[0].assert_debug_log(['Misbehaving']): - filter_node.send_and_ping(msg_filterload(data=b'\xbb'*(MAX_BLOOM_FILTER_SIZE+1))) - - self.log.info('Check that too large data element to add to the filter is rejected') - with self.nodes[0].assert_debug_log(['Misbehaving']): - filter_node.send_and_ping(msg_filteradd(data=b'\xcc'*(MAX_SCRIPT_ELEMENT_SIZE+1))) + self.log.debug("Create a tx relevant to the peer before connecting") + filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['addresses'][0] + txid = self.nodes[0].sendtoaddress(filter_address, 90) - self.log.info('Add filtered P2P connection to the node') - filter_node.send_and_ping(filter_node.watch_filter_init) - filter_address = self.nodes[0].decodescript(filter_node.watch_script_pubkey)['addresses'][0] + self.log.debug("Send a mempool msg after connecting and check that the tx is received") + self.nodes[0].add_p2p_connection(filter_peer) + filter_peer.send_and_ping(filter_peer.watch_filter_init) + self.nodes[0].p2p.send_message(msg_mempool()) + filter_peer.wait_for_tx(txid) + + def test_frelay_false(self, filter_peer): + self.log.info("Check that a node with fRelay set to false does not receive invs until the filter is set") + filter_peer.tx_received = False + filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['addresses'][0] + self.nodes[0].sendtoaddress(filter_address, 90) + # Sync to make sure the reason filter_peer doesn't receive the tx is not p2p delays + filter_peer.sync_with_ping() + assert not filter_peer.tx_received + + # Clear the mempool so that this transaction does not impact subsequent tests + self.nodes[0].generate(1) + + def test_filter(self, filter_peer): + # Set the bloomfilter using filterload + filter_peer.send_and_ping(filter_peer.watch_filter_init) + # If fRelay is not already True, sending filterload sets it to True + assert self.nodes[0].getpeerinfo()[0]['relaytxes'] + filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['addresses'][0] self.log.info('Check that we receive merkleblock and tx if the filter matches a tx in a block') block_hash = self.nodes[0].generatetoaddress(1, filter_address)[0] txid = self.nodes[0].getblock(block_hash)['tx'][0] - filter_node.wait_for_merkleblock(block_hash) - filter_node.wait_for_tx(txid) + filter_peer.wait_for_merkleblock(block_hash) + filter_peer.wait_for_tx(txid) self.log.info('Check that we only receive a merkleblock if the filter does not match a tx in a block') - filter_node.tx_received = False + filter_peer.tx_received = False block_hash = self.nodes[0].generatetoaddress(1, self.nodes[0].getnewaddress())[0] - filter_node.wait_for_merkleblock(block_hash) - assert not filter_node.tx_received + filter_peer.wait_for_merkleblock(block_hash) + assert not filter_peer.tx_received self.log.info('Check that we not receive a tx if the filter does not match a mempool tx') - filter_node.merkleblock_received = False - filter_node.tx_received = False + filter_peer.merkleblock_received = False + filter_peer.tx_received = False self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 90) - filter_node.sync_with_ping() - filter_node.sync_with_ping() - assert not filter_node.merkleblock_received - assert not filter_node.tx_received + filter_peer.sync_with_ping() + filter_peer.sync_with_ping() + assert not filter_peer.merkleblock_received + assert not filter_peer.tx_received - self.log.info('Check that we receive a tx in reply to a mempool msg if the filter matches a mempool tx') - filter_node.merkleblock_received = False + self.log.info('Check that we receive a tx if the filter matches a mempool tx') + filter_peer.merkleblock_received = False txid = self.nodes[0].sendtoaddress(filter_address, 90) - filter_node.wait_for_tx(txid) - assert not filter_node.merkleblock_received + filter_peer.wait_for_tx(txid) + assert not filter_peer.merkleblock_received self.log.info('Check that after deleting filter all txs get relayed again') - filter_node.send_and_ping(msg_filterclear()) + filter_peer.send_and_ping(msg_filterclear()) for _ in range(5): txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 7) - filter_node.wait_for_tx(txid) + filter_peer.wait_for_tx(txid) self.log.info('Check that request for filtered blocks is ignored if no filter is set') - filter_node.merkleblock_received = False - filter_node.tx_received = False + filter_peer.merkleblock_received = False + filter_peer.tx_received = False with self.nodes[0].assert_debug_log(expected_msgs=['received getdata']): block_hash = self.nodes[0].generatetoaddress(1, self.nodes[0].getnewaddress())[0] - filter_node.wait_for_inv([CInv(MSG_BLOCK, int(block_hash, 16))]) - filter_node.sync_with_ping() - assert not filter_node.merkleblock_received - assert not filter_node.tx_received + filter_peer.wait_for_inv([CInv(MSG_BLOCK, int(block_hash, 16))]) + filter_peer.sync_with_ping() + assert not filter_peer.merkleblock_received + assert not filter_peer.tx_received self.log.info('Check that sending "filteradd" if no filter is set is treated as misbehavior') with self.nodes[0].assert_debug_log(['Misbehaving']): - filter_node.send_and_ping(msg_filteradd(data=b'letsmisbehave')) + filter_peer.send_and_ping(msg_filteradd(data=b'letsmisbehave')) self.log.info("Check that division-by-zero remote crash bug [CVE-2013-5700] is fixed") - filter_node.send_and_ping(msg_filterload(data=b'', nHashFuncs=1)) - filter_node.send_and_ping(msg_filteradd(data=b'letstrytocrashthisnode')) + filter_peer.send_and_ping(msg_filterload(data=b'', nHashFuncs=1)) + filter_peer.send_and_ping(msg_filteradd(data=b'letstrytocrashthisnode')) + self.nodes[0].disconnect_p2ps() + + def run_test(self): + filter_peer = self.nodes[0].add_p2p_connection(P2PBloomFilter()) + self.log.info('Test filter size limits') + self.test_size_limits(filter_peer) + + self.log.info('Test BIP 37 for a node with fRelay = True (default)') + self.test_filter(filter_peer) + self.nodes[0].disconnect_p2ps() + + self.log.info('Test BIP 37 for a node with fRelay = False') + # Add peer but do not send version yet + filter_peer_without_nrelay = self.nodes[0].add_p2p_connection(P2PBloomFilter(), send_version=False, wait_for_verack=False) + # Send version with fRelay=False + filter_peer_without_nrelay.wait_until(lambda: filter_peer_without_nrelay.is_connected, timeout=10) + version_without_fRelay = msg_version() + version_without_fRelay.nRelay = 0 + filter_peer_without_nrelay.send_message(version_without_fRelay) + filter_peer_without_nrelay.wait_for_verack() + assert not self.nodes[0].getpeerinfo()[0]['relaytxes'] + self.test_frelay_false(filter_peer_without_nrelay) + self.test_filter(filter_peer_without_nrelay) + + self.test_msg_mempool() if __name__ == '__main__': diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py index ed97b97d8fd98..37c68302f1ca8 100755 --- a/test/functional/p2p_invalid_messages.py +++ b/test/functional/p2p_invalid_messages.py @@ -4,9 +4,6 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test node responses to invalid network messages.""" -import struct -import sys - from test_framework.messages import ( CBlockHeader, @@ -27,6 +24,8 @@ wait_until, ) +MSG_LIMIT = 3 * 1024 * 1024 # 3MB, per MAX_PROTOCOL_MESSAGE_LENGTH +VALID_DATA_LIMIT = MSG_LIMIT - 5 # Account for the 5-byte length prefix class msg_unrecognized: """Nonsensical message. Modeled after similar types in test_framework.messages.""" @@ -49,115 +48,13 @@ def set_test_params(self): self.setup_clean_chain = True def run_test(self): - """ - . Test msg header - 0. Send a bunch of large (3MB) messages of an unrecognized type. Check to see - that it isn't an effective DoS against the node. - - 1. Send an oversized (3MB+) message and check that we're disconnected. - - 2. Send a few messages with an incorrect data size in the header, ensure the - messages are ignored. - """ self.test_buffer() self.test_magic_bytes() self.test_checksum() self.test_size() self.test_msgtype() self.test_large_inv() - - node = self.nodes[0] - self.node = node - node.add_p2p_connection(P2PDataStore()) - conn2 = node.add_p2p_connection(P2PDataStore()) - - msg_limit = 3 * 1024 * 1024 # 3MB, per MAX_PROTOCOL_MESSAGE_LENGTH - valid_data_limit = msg_limit - 5 # Account for the 4-byte length prefix - - # - # 0. - # - # Send as large a message as is valid, ensure we aren't disconnected but - # also can't exhaust resources. - # - msg_at_size = msg_unrecognized(str_data="b" * valid_data_limit) - assert len(msg_at_size.serialize()) == msg_limit - - self.log.info("Sending a bunch of large, junk messages to test memory exhaustion. May take a bit...") - - # Run a bunch of times to test for memory exhaustion. - for _ in range(80): - node.p2p.send_message(msg_at_size) - - # Check that, even though the node is being hammered by nonsense from one - # connection, it can still service other peers in a timely way. - for _ in range(20): - conn2.sync_with_ping(timeout=2) - - # Peer 1, despite serving up a bunch of nonsense, should still be connected. - self.log.info("Waiting for node to drop junk messages.") - node.p2p.sync_with_ping(timeout=400) - assert node.p2p.is_connected - - # - # 1. - # - # Send an oversized message, ensure we're disconnected. - # - # Under macOS this test is skipped due to an unexpected error code - # returned from the closing socket which python/asyncio does not - # yet know how to handle. - # - if sys.platform != 'darwin': - msg_over_size = msg_unrecognized(str_data="b" * (valid_data_limit + 1)) - assert len(msg_over_size.serialize()) == (msg_limit + 1) - - # An unknown message type (or *any* message type) over - # MAX_PROTOCOL_MESSAGE_LENGTH should result in a disconnect. - node.p2p.send_message(msg_over_size) - node.p2p.wait_for_disconnect(timeout=4) - - node.disconnect_p2ps() - conn = node.add_p2p_connection(P2PDataStore()) - conn.wait_for_verack() - else: - self.log.info("Skipping test p2p_invalid_messages/1 (oversized message) under macOS") - - # - # 2. - # - # Send messages with an incorrect data size in the header. - # - actual_size = 100 - msg = msg_unrecognized(str_data="b" * actual_size) - - # TODO: handle larger-than cases. I haven't been able to pin down what behavior to expect. - for wrong_size in (2, 77, 78, 79): - self.log.info("Sending a message with incorrect size of {}".format(wrong_size)) - - # Unmodified message should submit okay. - node.p2p.send_and_ping(msg) - - # A message lying about its data size results in a disconnect when the incorrect - # data size is less than the actual size. - # - # TODO: why does behavior change at 78 bytes? - # - node.p2p.send_raw_message(self._tweak_msg_data_size(msg, wrong_size)) - - # For some reason unknown to me, we sometimes have to push additional data to the - # peer in order for it to realize a disconnect. - try: - node.p2p.send_message(msg_ping(nonce=123123)) - except IOError: - pass - - node.p2p.wait_for_disconnect(timeout=10) - node.disconnect_p2ps() - node.add_p2p_connection(P2PDataStore()) - - # Node is still up. - conn = node.add_p2p_connection(P2PDataStore()) + self.test_resource_exhaustion() def test_buffer(self): self.log.info("Test message with header split across two buffers, should be received") @@ -202,14 +99,10 @@ def test_checksum(self): def test_size(self): conn = self.nodes[0].add_p2p_connection(P2PDataStore()) - with self.nodes[0].assert_debug_log(['HEADER ERROR - SIZE (badmsg, 33554433 bytes)']): - msg = conn.build_message(msg_unrecognized(str_data="d")) - cut_len = ( - 4 + # magic - 12 # msgtype - ) - # modify len to MAX_SIZE + 1 - msg = msg[:cut_len] + struct.pack("