diff options
-rw-r--r-- | build-aux/m4/bitcoin_qt.m4 | 94 | ||||
-rw-r--r-- | doc/bitcoin-conf.md | 2 | ||||
-rw-r--r-- | share/examples/bitcoin.conf | 14 | ||||
-rw-r--r-- | src/interfaces/chain.h | 3 | ||||
-rw-r--r-- | src/interfaces/node.h | 5 | ||||
-rw-r--r-- | src/net.cpp | 6 | ||||
-rw-r--r-- | src/net.h | 10 | ||||
-rw-r--r-- | src/netbase.h | 17 | ||||
-rw-r--r-- | src/node/interfaces.cpp | 8 | ||||
-rw-r--r-- | src/qt/clientmodel.cpp | 8 | ||||
-rw-r--r-- | src/qt/rpcconsole.cpp | 42 | ||||
-rw-r--r-- | src/qt/rpcconsole.h | 5 | ||||
-rw-r--r-- | src/qt/test/test_main.cpp | 7 | ||||
-rw-r--r-- | src/rpc/mining.cpp | 2 | ||||
-rw-r--r-- | src/rpc/net.cpp | 8 | ||||
-rw-r--r-- | src/test/fuzz/connman.cpp | 2 | ||||
-rw-r--r-- | src/test/fuzz/util.h | 6 | ||||
-rw-r--r-- | src/wallet/feebumper.cpp | 2 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 27 | ||||
-rw-r--r-- | src/wallet/wallet.h | 4 | ||||
-rwxr-xr-x | test/functional/rpc_fundrawtransaction.py | 20 |
21 files changed, 187 insertions, 105 deletions
diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4 index b559baba23..5b7c3948d9 100644 --- a/build-aux/m4/bitcoin_qt.m4 +++ b/build-aux/m4/bitcoin_qt.m4 @@ -111,9 +111,7 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ dnl For Qt5, we can check a header to find out whether Qt is build dnl statically. When Qt is built statically, some plugins must be linked into dnl the final binary as well. - dnl With Qt5, languages moved into core and the WindowsIntegration plugin was - dnl added. - dnl _BITCOIN_QT_CHECK_STATIC_PLUGINS does a quick link-check and appends the + dnl _BITCOIN_QT_CHECK_STATIC_PLUGIN does a quick link-check and appends the dnl results to QT_LIBS. BITCOIN_QT_CHECK([ TEMP_CPPFLAGS=$CPPFLAGS @@ -122,20 +120,31 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ CXXFLAGS="$PIC_FLAGS $CXXFLAGS" _BITCOIN_QT_IS_STATIC if test "x$bitcoin_cv_static_qt" = xyes; then - _BITCOIN_QT_FIND_STATIC_PLUGINS + _BITCOIN_QT_CHECK_STATIC_LIBS + + if test "x$qt_plugin_path" != x; then + QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms" + if test -d "$qt_plugin_path/accessible"; then + QT_LIBS="$QT_LIBS -L$qt_plugin_path/accessible" + fi + if test -d "$qt_plugin_path/platforms/android"; then + QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms/android -lqtfreetype -lEGL" + fi + fi + AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static]) if test "x$TARGET_OS" != xandroid; then - _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin)],[-lqminimal]) + _BITCOIN_QT_CHECK_STATIC_PLUGIN([QMinimalIntegrationPlugin], [-lqminimal]) AC_DEFINE(QT_QPA_PLATFORM_MINIMAL, 1, [Define this symbol if the minimal qt platform exists]) fi if test "x$TARGET_OS" = xwindows; then - _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)],[-lqwindows]) + _BITCOIN_QT_CHECK_STATIC_PLUGIN([QWindowsIntegrationPlugin], [-lqwindows]) AC_DEFINE(QT_QPA_PLATFORM_WINDOWS, 1, [Define this symbol if the qt platform is windows]) elif test "x$TARGET_OS" = xlinux; then - _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QXcbIntegrationPlugin)],[-lqxcb -lxcb-static]) + _BITCOIN_QT_CHECK_STATIC_PLUGIN([QXcbIntegrationPlugin], [-lqxcb -lxcb-static]) AC_DEFINE(QT_QPA_PLATFORM_XCB, 1, [Define this symbol if the qt platform is xcb]) elif test "x$TARGET_OS" = xdarwin; then - _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin)],[-lqcocoa]) + _BITCOIN_QT_CHECK_STATIC_PLUGIN([QCocoaIntegrationPlugin], [-lqcocoa]) AC_DEFINE(QT_QPA_PLATFORM_COCOA, 1, [Define this symbol if the qt platform is cocoa]) elif test "x$TARGET_OS" = xandroid; then QT_LIBS="-Wl,--export-dynamic,--undefined=JNI_OnLoad -lqtforandroid -ljnigraphics -landroid -lqtfreetype -lQt5EglSupport $QT_LIBS" @@ -284,51 +293,46 @@ AC_DEFUN([_BITCOIN_QT_IS_STATIC],[ ]) ]) -dnl Internal. Check if the link-requirements for static plugins are met. +dnl Internal. Check if the link-requirements for a static plugin are met. +dnl +dnl _BITCOIN_QT_CHECK_STATIC_PLUGIN(PLUGIN, LIBRARIES) +dnl -------------------------------------------------- +dnl dnl Requires: INCLUDES and LIBS must be populated as necessary. -dnl Inputs: $1: A series of Q_IMPORT_PLUGIN(). +dnl Inputs: $1: A static plugin name. dnl Inputs: $2: The libraries that resolve $1. dnl Output: QT_LIBS is prepended or configure exits. -AC_DEFUN([_BITCOIN_QT_CHECK_STATIC_PLUGINS],[ - AC_MSG_CHECKING(for static Qt plugins: $2) +AC_DEFUN([_BITCOIN_QT_CHECK_STATIC_PLUGIN], [ + AC_MSG_CHECKING([for $1 ($2)]) CHECK_STATIC_PLUGINS_TEMP_LIBS="$LIBS" LIBS="$2${qt_lib_suffix} $QT_LIBS $LIBS" - AC_LINK_IFELSE([AC_LANG_PROGRAM([[ - #define QT_STATICPLUGIN - #include <QtPlugin> - $1]], - [[return 0;]])], - [AC_MSG_RESULT(yes); QT_LIBS="$2${qt_lib_suffix} $QT_LIBS"], - [AC_MSG_RESULT(no); BITCOIN_QT_FAIL(Could not resolve: $2)]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <QtPlugin> Q_IMPORT_PLUGIN($1)]])], + [AC_MSG_RESULT([yes]); QT_LIBS="$2${qt_lib_suffix} $QT_LIBS"], + [AC_MSG_RESULT([no]); BITCOIN_QT_FAIL([$1 not found.])]) LIBS="$CHECK_STATIC_PLUGINS_TEMP_LIBS" ]) -dnl Internal. Find paths necessary for linking qt static plugins -dnl Inputs: qt_plugin_path. optional. -dnl Outputs: QT_LIBS is appended -AC_DEFUN([_BITCOIN_QT_FIND_STATIC_PLUGINS],[ - if test "x$qt_plugin_path" != x; then - QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms" - if test -d "$qt_plugin_path/accessible"; then - QT_LIBS="$QT_LIBS -L$qt_plugin_path/accessible" - fi - if test -d "$qt_plugin_path/platforms/android"; then - QT_LIBS="$QT_LIBS -L$qt_plugin_path/platforms/android -lqtfreetype -lEGL" - fi - PKG_CHECK_MODULES([QTFONTDATABASE], [Qt5FontDatabaseSupport${qt_lib_suffix}], [QT_LIBS="-lQt5FontDatabaseSupport${qt_lib_suffix} $QT_LIBS"]) - PKG_CHECK_MODULES([QTEVENTDISPATCHER], [Qt5EventDispatcherSupport${qt_lib_suffix}], [QT_LIBS="-lQt5EventDispatcherSupport${qt_lib_suffix} $QT_LIBS"]) - PKG_CHECK_MODULES([QTTHEME], [Qt5ThemeSupport${qt_lib_suffix}], [QT_LIBS="-lQt5ThemeSupport${qt_lib_suffix} $QT_LIBS"]) - PKG_CHECK_MODULES([QTDEVICEDISCOVERY], [Qt5DeviceDiscoverySupport${qt_lib_suffix}], [QT_LIBS="-lQt5DeviceDiscoverySupport${qt_lib_suffix} $QT_LIBS"]) - PKG_CHECK_MODULES([QTACCESSIBILITY], [Qt5AccessibilitySupport${qt_lib_suffix}], [QT_LIBS="-lQt5AccessibilitySupport${qt_lib_suffix} $QT_LIBS"]) - PKG_CHECK_MODULES([QTFB], [Qt5FbSupport${qt_lib_suffix}], [QT_LIBS="-lQt5FbSupport${qt_lib_suffix} $QT_LIBS"]) - if test "x$TARGET_OS" = xlinux; then - PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"]) - elif test "x$TARGET_OS" = xdarwin; then - PKG_CHECK_MODULES([QTCLIPBOARD], [Qt5ClipboardSupport${qt_lib_suffix}], [QT_LIBS="-lQt5ClipboardSupport${qt_lib_suffix} $QT_LIBS"]) - PKG_CHECK_MODULES([QTGRAPHICS], [Qt5GraphicsSupport${qt_lib_suffix}], [QT_LIBS="-lQt5GraphicsSupport${qt_lib_suffix} $QT_LIBS"]) - PKG_CHECK_MODULES([QTCGL], [Qt5CglSupport${qt_lib_suffix}], [QT_LIBS="-lQt5CglSupport${qt_lib_suffix} $QT_LIBS"]) - fi - fi +dnl Internal. Check Qt static libs with PKG_CHECK_MODULES. +dnl +dnl _BITCOIN_QT_CHECK_STATIC_LIBS +dnl ----------------------------- +dnl +dnl Inputs: no inputs. +dnl Outputs: QT_LIBS is prepended. +AC_DEFUN([_BITCOIN_QT_CHECK_STATIC_LIBS], [ + PKG_CHECK_MODULES([QTFONTDATABASE], [Qt5FontDatabaseSupport${qt_lib_suffix}], [QT_LIBS="-lQt5FontDatabaseSupport${qt_lib_suffix} $QT_LIBS"]) + PKG_CHECK_MODULES([QTEVENTDISPATCHER], [Qt5EventDispatcherSupport${qt_lib_suffix}], [QT_LIBS="-lQt5EventDispatcherSupport${qt_lib_suffix} $QT_LIBS"]) + PKG_CHECK_MODULES([QTTHEME], [Qt5ThemeSupport${qt_lib_suffix}], [QT_LIBS="-lQt5ThemeSupport${qt_lib_suffix} $QT_LIBS"]) + PKG_CHECK_MODULES([QTDEVICEDISCOVERY], [Qt5DeviceDiscoverySupport${qt_lib_suffix}], [QT_LIBS="-lQt5DeviceDiscoverySupport${qt_lib_suffix} $QT_LIBS"]) + PKG_CHECK_MODULES([QTACCESSIBILITY], [Qt5AccessibilitySupport${qt_lib_suffix}], [QT_LIBS="-lQt5AccessibilitySupport${qt_lib_suffix} $QT_LIBS"]) + PKG_CHECK_MODULES([QTFB], [Qt5FbSupport${qt_lib_suffix}], [QT_LIBS="-lQt5FbSupport${qt_lib_suffix} $QT_LIBS"]) + if test "x$TARGET_OS" = xlinux; then + PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"]) + elif test "x$TARGET_OS" = xdarwin; then + PKG_CHECK_MODULES([QTCLIPBOARD], [Qt5ClipboardSupport${qt_lib_suffix}], [QT_LIBS="-lQt5ClipboardSupport${qt_lib_suffix} $QT_LIBS"]) + PKG_CHECK_MODULES([QTGRAPHICS], [Qt5GraphicsSupport${qt_lib_suffix}], [QT_LIBS="-lQt5GraphicsSupport${qt_lib_suffix} $QT_LIBS"]) + PKG_CHECK_MODULES([QTCGL], [Qt5CglSupport${qt_lib_suffix}], [QT_LIBS="-lQt5CglSupport${qt_lib_suffix} $QT_LIBS"]) + fi ]) dnl Internal. Find Qt libraries using pkg-config. diff --git a/doc/bitcoin-conf.md b/doc/bitcoin-conf.md index f4a8edec75..9a312bc33c 100644 --- a/doc/bitcoin-conf.md +++ b/doc/bitcoin-conf.md @@ -27,7 +27,7 @@ Comments may appear in two ways: ### Network specific options Network specific options can be: -- placed into sections with headers `[main]` (not `[mainnet]`), `[test]` (not `[testnet]`) or `[regtest]`; +- placed into sections with headers `[main]` (not `[mainnet]`), `[test]` (not `[testnet]`), `[signet]` or `[regtest]`; - prefixed with a chain name; e.g., `regtest.maxmempool=100`. Network specific options take precedence over non-network specific options. diff --git a/share/examples/bitcoin.conf b/share/examples/bitcoin.conf index 90a592cc63..5b7fc776a4 100644 --- a/share/examples/bitcoin.conf +++ b/share/examples/bitcoin.conf @@ -4,13 +4,16 @@ # Network-related settings: -# Note that if you use testnet or regtest, particularly with the options +# Note that if you use testnet, signet or regtest, particularly with the options # addnode, connect, port, bind, rpcport, rpcbind or wallet, you will also # want to read "[Sections]" further down. -# Run on the test network instead of the real bitcoin network. +# Run on the testnet network #testnet=0 +# Run on a signet network +#signet=0 + # Run a regression test network #regtest=0 @@ -57,7 +60,7 @@ # Listening mode, enabled by default except when 'connect' is being used #listen=1 -# Port on which to listen for connections (default: 8333, testnet: 18333, regtest: 18444) +# Port on which to listen for connections (default: 8333, testnet: 18333, signet: 38333, regtest: 18444) #port= # Maximum number of inbound+outbound connections. @@ -155,7 +158,7 @@ #minimizetotray=1 # [Sections] -# Most options apply to mainnet, testnet and regtest. +# Most options apply to mainnet, testnet, signet and regtest. # If you want to confine an option to just one network, you should add it in the # relevant section below. # EXCEPTIONS: The options addnode, connect, port, bind, rpcport, rpcbind and wallet @@ -167,5 +170,8 @@ # Options only for testnet [test] +# Options only for signet +[signet] + # Options only for regtest [regtest] diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h index 1a49518d69..ebc5466173 100644 --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -159,6 +159,9 @@ public: //! Check if transaction is RBF opt in. virtual RBFTransactionState isRBFOptIn(const CTransaction& tx) = 0; + //! Check if transaction is in mempool. + virtual bool isInMempool(const uint256& txid) = 0; + //! Check if transaction has descendants in mempool. virtual bool hasDescendantsInMempool(const uint256& txid) = 0; diff --git a/src/interfaces/node.h b/src/interfaces/node.h index 15f7ef6256..1dd1e92e2f 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -6,9 +6,10 @@ #define BITCOIN_INTERFACES_NODE_H #include <amount.h> // For CAmount -#include <net.h> // For CConnman::NumConnections +#include <net.h> // For NodeId #include <net_types.h> // For banmap_t #include <netaddress.h> // For Network +#include <netbase.h> // For ConnectionDirection #include <support/allocators/secure.h> // For SecureString #include <util/translation.h> @@ -88,7 +89,7 @@ public: virtual bool getProxy(Network net, proxyType& proxy_info) = 0; //! Get number of connections. - virtual size_t getNodeCount(CConnman::NumConnections flags) = 0; + virtual size_t getNodeCount(ConnectionDirection flags) = 0; //! Get stats for connected nodes. using NodesStats = std::vector<std::tuple<CNodeStats, bool, CNodeStateStats>>; diff --git a/src/net.cpp b/src/net.cpp index 513328897b..cf7d3e50ba 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2725,15 +2725,15 @@ bool CConnman::RemoveAddedNode(const std::string& strNode) return false; } -size_t CConnman::GetNodeCount(NumConnections flags) +size_t CConnman::GetNodeCount(ConnectionDirection flags) { LOCK(cs_vNodes); - if (flags == CConnman::CONNECTIONS_ALL) // Shortcut if we want total + if (flags == ConnectionDirection::Both) // Shortcut if we want total return vNodes.size(); int nNum = 0; for (const auto& pnode : vNodes) { - if (flags & (pnode->IsInboundConn() ? CONNECTIONS_IN : CONNECTIONS_OUT)) { + if (flags & (pnode->IsInboundConn() ? ConnectionDirection::In : ConnectionDirection::Out)) { nNum++; } } @@ -17,6 +17,7 @@ #include <i2p.h> #include <net_permissions.h> #include <netaddress.h> +#include <netbase.h> #include <optional.h> #include <policy/feerate.h> #include <protocol.h> @@ -801,13 +802,6 @@ class CConnman { public: - enum NumConnections { - CONNECTIONS_NONE = 0, - CONNECTIONS_IN = (1U << 0), - CONNECTIONS_OUT = (1U << 1), - CONNECTIONS_ALL = (CONNECTIONS_IN | CONNECTIONS_OUT), - }; - struct Options { ServiceFlags nLocalServices = NODE_NONE; @@ -976,7 +970,7 @@ public: */ bool AddConnection(const std::string& address, ConnectionType conn_type); - size_t GetNodeCount(NumConnections num); + size_t GetNodeCount(ConnectionDirection); void GetNodeStats(std::vector<CNodeStats>& vstats); bool DisconnectNode(const std::string& node); bool DisconnectNode(const CSubNet& subnet); diff --git a/src/netbase.h b/src/netbase.h index b225f128e7..751f7eb3f0 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -18,6 +18,7 @@ #include <memory> #include <stdint.h> #include <string> +#include <type_traits> #include <vector> extern int nConnectTimeout; @@ -28,6 +29,22 @@ static const int DEFAULT_CONNECT_TIMEOUT = 5000; //! -dns default static const int DEFAULT_NAME_LOOKUP = true; +enum class ConnectionDirection { + None = 0, + In = (1U << 0), + Out = (1U << 1), + Both = (In | Out), +}; +static inline ConnectionDirection& operator|=(ConnectionDirection& a, ConnectionDirection b) { + using underlying = typename std::underlying_type<ConnectionDirection>::type; + a = ConnectionDirection(underlying(a) | underlying(b)); + return a; +} +static inline bool operator&(ConnectionDirection a, ConnectionDirection b) { + using underlying = typename std::underlying_type<ConnectionDirection>::type; + return (underlying(a) & underlying(b)); +} + class proxyType { public: diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index ec976fe9bf..62ef72ef38 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -96,7 +96,7 @@ public: bool shutdownRequested() override { return ShutdownRequested(); } void mapPort(bool use_upnp, bool use_natpmp) override { StartMapPort(use_upnp, use_natpmp); } bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); } - size_t getNodeCount(CConnman::NumConnections flags) override + size_t getNodeCount(ConnectionDirection flags) override { return m_context->connman ? m_context->connman->GetNodeCount(flags) : 0; } @@ -526,6 +526,12 @@ public: LOCK(m_node.mempool->cs); return IsRBFOptIn(tx, *m_node.mempool); } + bool isInMempool(const uint256& txid) override + { + if (!m_node.mempool) return false; + LOCK(m_node.mempool->cs); + return m_node.mempool->exists(txid); + } bool hasDescendantsInMempool(const uint256& txid) override { if (!m_node.mempool) return false; diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 56813b2d19..b244bc94f2 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -71,14 +71,14 @@ ClientModel::~ClientModel() int ClientModel::getNumConnections(unsigned int flags) const { - CConnman::NumConnections connections = CConnman::CONNECTIONS_NONE; + ConnectionDirection connections = ConnectionDirection::None; if(flags == CONNECTIONS_IN) - connections = CConnman::CONNECTIONS_IN; + connections = ConnectionDirection::In; else if (flags == CONNECTIONS_OUT) - connections = CConnman::CONNECTIONS_OUT; + connections = ConnectionDirection::Out; else if (flags == CONNECTIONS_ALL) - connections = CConnman::CONNECTIONS_ALL; + connections = ConnectionDirection::Both; return m_node.getNodeCount(connections); } diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 68adad4ebd..b258219894 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -474,9 +474,9 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty const QString list{"<ul><li>" + Join(CONNECTION_TYPE_DOC, QString("</li><li>")) + "</li></ul>"}; ui->peerConnectionTypeLabel->setToolTip(ui->peerConnectionTypeLabel->toolTip().arg(list)); const QString hb_list{"<ul><li>\"" - + tr("To") + "\" – " + tr("we selected the peer for high bandwidth relay") + "</li><li>\"" - + tr("From") + "\" – " + tr("the peer selected us for high bandwidth relay") + "</li><li>\"" - + tr("No") + "\" – " + tr("no high bandwidth relay selected") + "</li></ul>"}; + + ts.to + "\" – " + tr("we selected the peer for high bandwidth relay") + "</li><li>\"" + + ts.from + "\" – " + tr("the peer selected us for high bandwidth relay") + "</li><li>\"" + + ts.no + "\" – " + tr("no high bandwidth relay selected") + "</li></ul>"}; ui->peerHighBandwidthLabel->setToolTip(ui->peerHighBandwidthLabel->toolTip().arg(hb_list)); ui->dataDir->setToolTip(ui->dataDir->toolTip().arg(QString(nonbreaking_hyphen) + "datadir")); ui->blocksDir->setToolTip(ui->blocksDir->toolTip().arg(QString(nonbreaking_hyphen) + "blocksdir")); @@ -619,10 +619,10 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_ // create peer table context menu actions QAction* disconnectAction = new QAction(tr("&Disconnect"), this); - QAction* banAction1h = new QAction(tr("Ban for") + " " + tr("1 &hour"), this); - QAction* banAction24h = new QAction(tr("Ban for") + " " + tr("1 &day"), this); - QAction* banAction7d = new QAction(tr("Ban for") + " " + tr("1 &week"), this); - QAction* banAction365d = new QAction(tr("Ban for") + " " + tr("1 &year"), this); + QAction* banAction1h = new QAction(ts.ban_for + " " + tr("1 &hour"), this); + QAction* banAction24h = new QAction(ts.ban_for + " " + tr("1 &day"), this); + QAction* banAction7d = new QAction(ts.ban_for + " " + tr("1 &week"), this); + QAction* banAction365d = new QAction(ts.ban_for + " " + tr("1 &year"), this); // create peer table context menu peersTableContextMenu = new QMenu(this); @@ -1114,11 +1114,11 @@ void RPCConsole::updateDetailWidget() peerAddrDetails += "<br />" + tr("via %1").arg(QString::fromStdString(stats->nodeStats.addrLocal)); ui->peerHeading->setText(peerAddrDetails); ui->peerServices->setText(GUIUtil::formatServicesStr(stats->nodeStats.nServices)); - ui->peerRelayTxes->setText(stats->nodeStats.fRelayTxes ? "Yes" : "No"); + ui->peerRelayTxes->setText(stats->nodeStats.fRelayTxes ? ts.yes : ts.no); QString bip152_hb_settings; - if (stats->nodeStats.m_bip152_highbandwidth_to) bip152_hb_settings += "To"; - if (stats->nodeStats.m_bip152_highbandwidth_from) bip152_hb_settings += (bip152_hb_settings == "" ? "From" : "/From"); - if (bip152_hb_settings == "") bip152_hb_settings = "No"; + if (stats->nodeStats.m_bip152_highbandwidth_to) bip152_hb_settings = ts.to; + if (stats->nodeStats.m_bip152_highbandwidth_from) bip152_hb_settings += (bip152_hb_settings.isEmpty() ? ts.from : QLatin1Char('/') + ts.from); + if (bip152_hb_settings.isEmpty()) bip152_hb_settings = ts.no; ui->peerHighBandwidth->setText(bip152_hb_settings); const int64_t time_now{GetSystemTimeInSeconds()}; ui->peerConnTime->setText(GUIUtil::formatDurationStr(time_now - stats->nodeStats.nTimeConnected)); @@ -1136,7 +1136,7 @@ void RPCConsole::updateDetailWidget() ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type, /* prepend_direction */ true)); ui->peerNetwork->setText(GUIUtil::NetworkToQString(stats->nodeStats.m_network)); if (stats->nodeStats.m_permissionFlags == PF_NONE) { - ui->peerPermissions->setText(tr("N/A")); + ui->peerPermissions->setText(ts.na); } else { QStringList permissions; for (const auto& permission : NetPermissions::ToStrings(stats->nodeStats.m_permissionFlags)) { @@ -1144,23 +1144,23 @@ void RPCConsole::updateDetailWidget() } ui->peerPermissions->setText(permissions.join(" & ")); } - ui->peerMappedAS->setText(stats->nodeStats.m_mapped_as != 0 ? QString::number(stats->nodeStats.m_mapped_as) : tr("N/A")); + ui->peerMappedAS->setText(stats->nodeStats.m_mapped_as != 0 ? QString::number(stats->nodeStats.m_mapped_as) : ts.na); // This check fails for example if the lock was busy and // nodeStateStats couldn't be fetched. if (stats->fNodeStateStatsAvailable) { // Sync height is init to -1 - if (stats->nodeStateStats.nSyncHeight > -1) + if (stats->nodeStateStats.nSyncHeight > -1) { ui->peerSyncHeight->setText(QString("%1").arg(stats->nodeStateStats.nSyncHeight)); - else - ui->peerSyncHeight->setText(tr("Unknown")); - + } else { + ui->peerSyncHeight->setText(ts.unknown); + } // Common height is init to -1 - if (stats->nodeStateStats.nCommonHeight > -1) + if (stats->nodeStateStats.nCommonHeight > -1) { ui->peerCommonHeight->setText(QString("%1").arg(stats->nodeStateStats.nCommonHeight)); - else - ui->peerCommonHeight->setText(tr("Unknown")); - + } else { + ui->peerCommonHeight->setText(ts.unknown); + } ui->peerHeight->setText(QString::number(stats->nodeStateStats.m_starting_height)); ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStateStats.m_ping_wait)); } diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 27d4c42eb4..b9806e40c9 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -136,6 +136,11 @@ Q_SIGNALS: void cmdRequest(const QString &command, const WalletModel* wallet_model); private: + struct TranslatedStrings { + const QString yes{tr("Yes")}, no{tr("No")}, to{tr("To")}, from{tr("From")}, + ban_for{tr("Ban for")}, na{tr("N/A")}, unknown{tr("Unknown")}; + } const ts; + void startExecutor(); void setTrafficGraphRange(int mins); diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index 9ef5fe8fc7..a1baf6a402 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -54,6 +54,13 @@ int main(int argc, char* argv[]) NodeContext node_context; std::unique_ptr<interfaces::Node> node = interfaces::MakeNode(&node_context); + gArgs.ForceSetArg("-listen", "0"); + gArgs.ForceSetArg("-listenonion", "0"); + gArgs.ForceSetArg("-discover", "0"); + gArgs.ForceSetArg("-dnsseed", "0"); + gArgs.ForceSetArg("-fixedseeds", "0"); + gArgs.ForceSetArg("-upnp", "0"); + gArgs.ForceSetArg("-natpmp", "0"); bool fInvalid = false; diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 50987a735b..f29f556517 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -659,7 +659,7 @@ static RPCHelpMan getblocktemplate() throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); if (!Params().IsTestChain()) { - if (node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0) { + if (node.connman->GetNodeCount(ConnectionDirection::Both) == 0) { throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!"); } diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 6d33654c6f..cf4d46cf2c 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -57,7 +57,7 @@ static RPCHelpMan getconnectioncount() if(!node.connman) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); - return (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL); + return (int)node.connman->GetNodeCount(ConnectionDirection::Both); }, }; } @@ -630,9 +630,9 @@ static RPCHelpMan getnetworkinfo() obj.pushKV("timeoffset", GetTimeOffset()); if (node.connman) { obj.pushKV("networkactive", node.connman->GetNetworkActive()); - obj.pushKV("connections", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL)); - obj.pushKV("connections_in", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_IN)); - obj.pushKV("connections_out", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_OUT)); + obj.pushKV("connections", (int)node.connman->GetNodeCount(ConnectionDirection::Both)); + obj.pushKV("connections_in", (int)node.connman->GetNodeCount(ConnectionDirection::In)); + obj.pushKV("connections_out", (int)node.connman->GetNodeCount(ConnectionDirection::Out)); } obj.pushKV("networks", GetNetworksInfo()); obj.pushKV("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())); diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp index 663d4fee8b..ae77a45e44 100644 --- a/src/test/fuzz/connman.cpp +++ b/src/test/fuzz/connman.cpp @@ -95,7 +95,7 @@ FUZZ_TARGET_INIT(connman, initialize_connman) (void)connman.GetDeterministicRandomizer(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); }, [&] { - (void)connman.GetNodeCount(fuzzed_data_provider.PickValueInArray({CConnman::CONNECTIONS_NONE, CConnman::CONNECTIONS_IN, CConnman::CONNECTIONS_OUT, CConnman::CONNECTIONS_ALL})); + (void)connman.GetNodeCount(fuzzed_data_provider.PickValueInArray({ConnectionDirection::None, ConnectionDirection::In, ConnectionDirection::Out, ConnectionDirection::Both})); }, [&] { connman.MarkAddressGood(random_address); diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h index f2d43032f4..d8c536e8b1 100644 --- a/src/test/fuzz/util.h +++ b/src/test/fuzz/util.h @@ -543,6 +543,12 @@ public: { } + FuzzedSock& operator=(Sock&& other) override + { + assert(false && "Not implemented yet."); + return *this; + } + SOCKET Get() const override { assert(false && "Not implemented yet."); diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 5e319d4f95..08adf09df4 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -190,7 +190,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo if (coin_control.m_feerate) { // The user provided a feeRate argument. // We calculate this here to avoid compiler warning on the cs_wallet lock - const int64_t maxTxSize = CalculateMaximumSignedTxSize(*wtx.tx, &wallet); + const int64_t maxTxSize = CalculateMaximumSignedTxSize(*wtx.tx, &wallet).first; Result res = CheckFeeRate(wallet, wtx, *new_coin_control.m_feerate, maxTxSize, errors); if (res != Result::OK) { return res; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 08e480225d..58ab124061 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -791,6 +791,12 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash) wtx.mapValue["replaced_by_txid"] = newHash.ToString(); + // Refresh mempool status without waiting for transactionRemovedFromMempool + // notification so the wallet is in an internally consistent state and + // immediately knows the old transaction should not be considered trusted + // and is eligible to be abandoned + wtx.fInMempool = chain().isInMempool(originalHash); + WalletBatch batch(GetDatabase()); bool success = true; @@ -1611,14 +1617,15 @@ bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScri return true; } -int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig) +// Returns pair of vsize and weight +std::pair<int64_t, int64_t> CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig) { std::vector<CTxOut> txouts; for (const CTxIn& input : tx.vin) { const auto mi = wallet->mapWallet.find(input.prevout.hash); // Can not estimate size without knowing the input details if (mi == wallet->mapWallet.end()) { - return -1; + return std::make_pair(-1, -1); } assert(input.prevout.n < mi->second.tx->vout.size()); txouts.emplace_back(mi->second.tx->vout[input.prevout.n]); @@ -1627,13 +1634,16 @@ int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wall } // txouts needs to be in the order of tx.vin -int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig) +std::pair<int64_t, int64_t> CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig) { CMutableTransaction txNew(tx); if (!wallet->DummySignTx(txNew, txouts, use_max_sig)) { - return -1; + return std::make_pair(-1, -1); } - return GetVirtualTransactionSize(CTransaction(txNew)); + CTransaction ctx(txNew); + int64_t vsize = GetVirtualTransactionSize(ctx); + int64_t weight = GetTransactionWeight(ctx); + return std::make_pair(vsize, weight); } int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet, bool use_max_sig) @@ -2779,6 +2789,7 @@ bool CWallet::CreateTransactionInternal( CMutableTransaction txNew; FeeCalculation feeCalc; CAmount nFeeNeeded; + std::pair<int64_t, int64_t> tx_sizes; int nBytes; { std::set<CInputCoin> setCoins; @@ -2962,7 +2973,8 @@ bool CWallet::CreateTransactionInternal( txNew.vin.push_back(CTxIn(coin.outpoint,CScript())); } - nBytes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly); + tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly); + nBytes = tx_sizes.first; if (nBytes < 0) { error = _("Signing transaction failed"); return false; @@ -3072,7 +3084,8 @@ bool CWallet::CreateTransactionInternal( tx = MakeTransactionRef(std::move(txNew)); // Limit size - if (GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT) + if ((sign && GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT) || + (!sign && tx_sizes.second > MAX_STANDARD_TX_WEIGHT)) { error = _("Transaction too large"); return false; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index eb797938cd..3a76163dd2 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1334,8 +1334,8 @@ public: // Use DummySignatureCreator, which inserts 71 byte signatures everywhere. // NOTE: this requires that all inputs must be in mapWallet (eg the tx should // be IsAllFromMe). -int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet); -int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false); +std::pair<int64_t, int64_t> CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet); +std::pair<int64_t, int64_t> CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false); //! Add wallet name to persistent configuration so it will be loaded on startup. bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name); diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py index 569471dc87..6b300e7231 100755 --- a/test/functional/rpc_fundrawtransaction.py +++ b/test/functional/rpc_fundrawtransaction.py @@ -94,6 +94,7 @@ class RawTransactionsTest(BitcoinTestFramework): self.test_address_reuse() self.test_option_subtract_fee_from_outputs() self.test_subtract_fee_with_presets() + self.test_transaction_too_large() def test_change_position(self): """Ensure setting changePosition in fundraw with an exact match is handled properly.""" @@ -907,5 +908,24 @@ class RawTransactionsTest(BitcoinTestFramework): signedtx = self.nodes[0].signrawtransactionwithwallet(fundedtx['hex']) self.nodes[0].sendrawtransaction(signedtx['hex']) + def test_transaction_too_large(self): + self.log.info("Test fundrawtx where BnB solution would result in a too large transaction, but Knapsack would not") + + self.nodes[0].createwallet("large") + wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name) + recipient = self.nodes[0].get_wallet_rpc("large") + outputs = {} + rawtx = recipient.createrawtransaction([], {wallet.getnewaddress(): 147.99899260}) + + # Make 1500 0.1 BTC outputs + # The amount that we target for funding is in the BnB range when these outputs are used. + # However if these outputs are selected, the transaction will end up being too large, so it shouldn't use BnB and instead fallback to Knapsack + # but that behavior is not implemented yet. For now we just check that we get an error. + for i in range(0, 1500): + outputs[recipient.getnewaddress()] = 0.1 + wallet.sendmany("", outputs) + self.nodes[0].generate(10) + assert_raises_rpc_error(-4, "Transaction too large", recipient.fundrawtransaction, rawtx) + if __name__ == '__main__': RawTransactionsTest().main() |