diff --git a/src/Makefile.am b/src/Makefile.am index 9ca10e66d03f..472d88aa2c9d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -286,6 +286,7 @@ BITCOIN_CORE_H = \ rpc/blockchain.h \ rpc/client.h \ rpc/mining.h \ + rpc/net.h \ rpc/protocol.h \ rpc/rawtransaction_util.h \ rpc/register.h \ diff --git a/src/net.cpp b/src/net.cpp index e2739b610642..d39a891f8b07 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -588,9 +588,9 @@ bool CNode::IsBlockRelayOnly() const { return (ignores_incoming_txs && !HasPermission(NetPermissionFlags::Relay)) || IsBlockOnlyConn(); } -std::string CNode::ConnectionTypeAsString() const +std::string ConnectionTypeAsString(ConnectionType conn_type) { - switch (m_conn_type) { + switch (conn_type) { case ConnectionType::INBOUND: return "inbound"; case ConnectionType::MANUAL: @@ -700,7 +700,7 @@ void CNode::copyStats(CNodeStats &stats, const std::vector &m_asmap) X(verifiedPubKeyHash); } X(m_masternode_connection); - stats.m_conn_type_string = ConnectionTypeAsString(); + X(m_conn_type); } #undef X @@ -2169,7 +2169,7 @@ void CConnman::ThreadDNSAddressSeed() { LOCK(cs_vNodes); for (const CNode* pnode : vNodes) { - if (pnode->fSuccessfullyConnected && !pnode->IsOutboundOrBlockRelayConn() && !pnode->m_masternode_probe_connection) ++nRelevant; + if (pnode->fSuccessfullyConnected && !pnode->IsFullOutboundConn() && !pnode->m_masternode_probe_connection) ++nRelevant; } } if (nRelevant >= 2) { @@ -2256,7 +2256,7 @@ void CConnman::ProcessAddrFetch() } } -bool CConnman::GetTryNewOutboundPeer() +bool CConnman::GetTryNewOutboundPeer() const { return m_try_another_outbound_peer; } @@ -2273,7 +2273,7 @@ void CConnman::SetTryNewOutboundPeer(bool flag) // Also exclude peers that haven't finished initial connection handshake yet // (so that we don't decide we're over our desired connection limit, and then // evict some peer that has finished the handshake) -int CConnman::GetExtraFullOutboundCount() +int CConnman::GetExtraFullOutboundCount() const { int full_outbound_peers = 0; { @@ -2291,7 +2291,7 @@ int CConnman::GetExtraFullOutboundCount() return std::max(full_outbound_peers - m_max_outbound_full_relay, 0); } -int CConnman::GetExtraBlockRelayCount() +int CConnman::GetExtraBlockRelayCount() const { int block_relay_peers = 0; { @@ -2619,7 +2619,7 @@ std::vector CConnman::GetCurrentBlockRelayOnlyConns() const return ret; } -std::vector CConnman::GetAddedNodeInfo() +std::vector CConnman::GetAddedNodeInfo() const { std::vector ret; @@ -2965,6 +2965,7 @@ void CConnman::ThreadMessageHandler() { int64_t nLastSendMessagesTimeMasternodes = 0; + FastRandomContext rng; while (!flagInterruptMsgProc) { std::vector vNodesCopy = CopyNodeVector(); @@ -2976,6 +2977,10 @@ void CConnman::ThreadMessageHandler() fSkipSendMessagesForMasternodes = false; nLastSendMessagesTimeMasternodes = GetTimeMillis(); } + // Randomize the order in which we process messages from/to our peers. + // This prevents attacks in which an attacker exploits having multiple + // consecutive connections in the vNodes list. + Shuffle(vNodesCopy.begin(), vNodesCopy.end(), rng); for (CNode* pnode : vNodesCopy) { @@ -3616,7 +3621,7 @@ CConnman::~CConnman() Stop(); } -std::vector CConnman::GetAddresses(size_t max_addresses, size_t max_pct, std::optional network) +std::vector CConnman::GetAddresses(size_t max_addresses, size_t max_pct, std::optional network) const { std::vector addresses = addrman.GetAddr(max_addresses, max_pct, network); if (m_banman) { @@ -3837,7 +3842,7 @@ void CConnman::AddPendingProbeConnections(const std::set &proTxHashes) masternodePendingProbes.insert(proTxHashes.begin(), proTxHashes.end()); } -size_t CConnman::GetNodeCount(ConnectionDirection flags) +size_t CConnman::GetNodeCount(ConnectionDirection flags) const { LOCK(cs_vNodes); @@ -3864,7 +3869,7 @@ size_t CConnman::GetMaxOutboundNodeCount() return m_max_outbound; } -void CConnman::GetNodeStats(std::vector& vstats) +void CConnman::GetNodeStats(std::vector& vstats) const { vstats.clear(); LOCK(cs_vNodes); @@ -4003,18 +4008,18 @@ void CConnman::RecordBytesSent(uint64_t bytes) nMaxOutboundTotalBytesSentInCycle += bytes; } -uint64_t CConnman::GetMaxOutboundTarget() +uint64_t CConnman::GetMaxOutboundTarget() const { LOCK(cs_totalBytesSent); return nMaxOutboundLimit; } -std::chrono::seconds CConnman::GetMaxOutboundTimeframe() +std::chrono::seconds CConnman::GetMaxOutboundTimeframe() const { return MAX_UPLOAD_TIMEFRAME; } -std::chrono::seconds CConnman::GetMaxOutboundTimeLeftInCycle() +std::chrono::seconds CConnman::GetMaxOutboundTimeLeftInCycle() const { LOCK(cs_totalBytesSent); if (nMaxOutboundLimit == 0) @@ -4028,7 +4033,7 @@ std::chrono::seconds CConnman::GetMaxOutboundTimeLeftInCycle() return (cycleEndTime < now) ? 0s : cycleEndTime - now; } -bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit) +bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit) const { LOCK(cs_totalBytesSent); if (nMaxOutboundLimit == 0) @@ -4048,7 +4053,7 @@ bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit) return false; } -uint64_t CConnman::GetOutboundTargetBytesLeft() +uint64_t CConnman::GetOutboundTargetBytesLeft() const { LOCK(cs_totalBytesSent); if (nMaxOutboundLimit == 0) @@ -4057,13 +4062,13 @@ uint64_t CConnman::GetOutboundTargetBytesLeft() return (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) ? 0 : nMaxOutboundLimit - nMaxOutboundTotalBytesSentInCycle; } -uint64_t CConnman::GetTotalBytesRecv() +uint64_t CConnman::GetTotalBytesRecv() const { LOCK(cs_totalBytesRecv); return nTotalBytesRecv; } -uint64_t CConnman::GetTotalBytesSent() +uint64_t CConnman::GetTotalBytesSent() const { LOCK(cs_totalBytesSent); return nTotalBytesSent; diff --git a/src/net.h b/src/net.h index 0930b936aca9..a2a11b346c20 100644 --- a/src/net.h +++ b/src/net.h @@ -213,6 +213,8 @@ enum class ConnectionType { ADDR_FETCH, }; +/** Convert ConnectionType enum to a string value */ +std::string ConnectionTypeAsString(ConnectionType conn_type); void Discover(); uint16_t GetListenPort(); @@ -307,7 +309,7 @@ class CNodeStats // In case this is a verified MN, this value is the hashed operator pubkey of the MN uint256 verifiedPubKeyHash; bool m_masternode_connection; - std::string m_conn_type_string; + ConnectionType m_conn_type; }; @@ -578,11 +580,6 @@ class CNode assert(false); } -protected: - mapMsgCmdSize mapSendBytesPerMsgCmd GUARDED_BY(cs_vSend); - mapMsgCmdSize mapRecvBytesPerMsgCmd GUARDED_BY(cs_vRecv); - -public: struct TxRelay { mutable RecursiveMutex cs_filter; // We use fRelayTxes for two purposes - @@ -647,50 +644,6 @@ class CNode CNode(const CNode&) = delete; CNode& operator=(const CNode&) = delete; -private: - const NodeId id; - const uint64_t nLocalHostNonce; - const ConnectionType m_conn_type; - std::atomic m_greatest_common_version{INIT_PROTO_VERSION}; - - //! Services offered to this peer. - //! - //! This is supplied by the parent CConnman during peer connection - //! (CConnman::ConnectNode()) from its attribute of the same name. - //! - //! This is const because there is no protocol defined for renegotiating - //! services initially offered to a peer. The set of local services we - //! offer should not change after initialization. - //! - //! An interesting example of this is NODE_NETWORK and initial block - //! download: a node which starts up from scratch doesn't have any blocks - //! to serve, but still advertises NODE_NETWORK because it will eventually - //! fulfill this role after IBD completes. P2P code is written in such a - //! way that it can gracefully handle peers who don't make good on their - //! service advertisements. - const ServiceFlags nLocalServices; - - std::list vRecvMsg; // Used only by SocketHandler thread - - mutable RecursiveMutex cs_addrName; - std::string addrName GUARDED_BY(cs_addrName); - - // Our address, as reported by the peer - CService addrLocal GUARDED_BY(cs_addrLocal); - mutable RecursiveMutex cs_addrLocal; - - //! Whether this peer is an inbound onion, e.g. connected via our Tor onion service. - const bool m_inbound_onion{false}; - - // Challenge sent in VERSION to be answered with MNAUTH (only happens between MNs) - mutable Mutex cs_mnauth; - uint256 sentMNAuthChallenge GUARDED_BY(cs_mnauth); - uint256 receivedMNAuthChallenge GUARDED_BY(cs_mnauth); - uint256 verifiedProRegTxHash GUARDED_BY(cs_mnauth); - uint256 verifiedPubKeyHash GUARDED_BY(cs_mnauth); - -public: - NodeId GetId() const { return id; } @@ -782,7 +735,7 @@ class CNode void MaybeSetAddrName(const std::string& addrNameIn); - std::string ConnectionTypeAsString() const; + std::string ConnectionTypeAsString() const { return ::ConnectionTypeAsString(m_conn_type); } /** A ping-pong round trip has completed successfully. Update latest and minimum ping times. */ void PongReceived(std::chrono::microseconds ping_time) { @@ -835,6 +788,51 @@ class CNode LOCK(cs_mnauth); verifiedPubKeyHash = newVerifiedPubKeyHash; } + +private: + const NodeId id; + const uint64_t nLocalHostNonce; + const ConnectionType m_conn_type; + std::atomic m_greatest_common_version{INIT_PROTO_VERSION}; + + //! Services offered to this peer. + //! + //! This is supplied by the parent CConnman during peer connection + //! (CConnman::ConnectNode()) from its attribute of the same name. + //! + //! This is const because there is no protocol defined for renegotiating + //! services initially offered to a peer. The set of local services we + //! offer should not change after initialization. + //! + //! An interesting example of this is NODE_NETWORK and initial block + //! download: a node which starts up from scratch doesn't have any blocks + //! to serve, but still advertises NODE_NETWORK because it will eventually + //! fulfill this role after IBD completes. P2P code is written in such a + //! way that it can gracefully handle peers who don't make good on their + //! service advertisements. + const ServiceFlags nLocalServices; + + std::list vRecvMsg; // Used only by SocketHandler thread + + mutable RecursiveMutex cs_addrName; + std::string addrName GUARDED_BY(cs_addrName); + + // Our address, as reported by the peer + CService addrLocal GUARDED_BY(cs_addrLocal); + mutable RecursiveMutex cs_addrLocal; + + //! Whether this peer is an inbound onion, e.g. connected via our Tor onion service. + const bool m_inbound_onion{false}; + + // Challenge sent in VERSION to be answered with MNAUTH (only happens between MNs) + mutable Mutex cs_mnauth; + uint256 sentMNAuthChallenge GUARDED_BY(cs_mnauth); + uint256 receivedMNAuthChallenge GUARDED_BY(cs_mnauth); + uint256 verifiedProRegTxHash GUARDED_BY(cs_mnauth); + uint256 verifiedPubKeyHash GUARDED_BY(cs_mnauth); + + mapMsgCmdSize mapSendBytesPerMsgCmd GUARDED_BY(cs_vSend); + mapMsgCmdSize mapRecvBytesPerMsgCmd GUARDED_BY(cs_vRecv); }; /** @@ -1132,7 +1130,7 @@ friend class CNode; * @param[in] max_pct Maximum percentage of addresses to return (0 = all). * @param[in] network Select only addresses of this network (nullopt = all). */ - std::vector GetAddresses(size_t max_addresses, size_t max_pct, std::optional network); + std::vector GetAddresses(size_t max_addresses, size_t max_pct, std::optional network) const; /** * Cache is used to minimize topology leaks, so it should @@ -1145,7 +1143,7 @@ friend class CNode; // This allows temporarily exceeding m_max_outbound_full_relay, with the goal of finding // a peer that is better than all our current peers. void SetTryNewOutboundPeer(bool flag); - bool GetTryNewOutboundPeer(); + bool GetTryNewOutboundPeer() const; void StartExtraBlockRelayPeers() { LogPrint(BCLog::NET, "net: enabling extra block-relay-only peers\n"); @@ -1158,13 +1156,13 @@ friend class CNode; // return a value less than (num_outbound_connections - num_outbound_slots) // in cases where some outbound connections are not yet fully connected, or // not yet fully disconnected. - int GetExtraFullOutboundCount(); + int GetExtraFullOutboundCount() const; // Count the number of block-relay-only peers we have over our limit. - int GetExtraBlockRelayCount(); + int GetExtraBlockRelayCount() const; bool AddNode(const std::string& node); bool RemoveAddedNode(const std::string& node); - std::vector GetAddedNodeInfo(); + std::vector GetAddedNodeInfo() const; /** * Attempts to open a connection. Currently only used from tests. @@ -1191,9 +1189,9 @@ friend class CNode; bool IsMasternodeQuorumRelayMember(const uint256& protxHash); void AddPendingProbeConnections(const std::set& proTxHashes); - size_t GetNodeCount(ConnectionDirection); + size_t GetNodeCount(ConnectionDirection) const; size_t GetMaxOutboundNodeCount(); - void GetNodeStats(std::vector& vstats); + void GetNodeStats(std::vector& vstats) const; bool DisconnectNode(const std::string& node); bool DisconnectNode(const CSubNet& subnet); bool DisconnectNode(const CNetAddr& addr); @@ -1207,24 +1205,24 @@ friend class CNode; //! that peer during `net_processing.cpp:PushNodeVersion()`. ServiceFlags GetLocalServices() const; - uint64_t GetMaxOutboundTarget(); - std::chrono::seconds GetMaxOutboundTimeframe(); + uint64_t GetMaxOutboundTarget() const; + std::chrono::seconds GetMaxOutboundTimeframe() const; //! check if the outbound target is reached //! if param historicalBlockServingLimit is set true, the function will //! response true if the limit for serving historical blocks has been reached - bool OutboundTargetReached(bool historicalBlockServingLimit); + bool OutboundTargetReached(bool historicalBlockServingLimit) const; //! response the bytes left in the current max outbound cycle //! in case of no limit, it will always response 0 - uint64_t GetOutboundTargetBytesLeft(); + uint64_t GetOutboundTargetBytesLeft() const; //! returns the time left in the current max outbound cycle //! in case of no limit, it will always return 0 - std::chrono::seconds GetMaxOutboundTimeLeftInCycle(); + std::chrono::seconds GetMaxOutboundTimeLeftInCycle() const; - uint64_t GetTotalBytesRecv(); - uint64_t GetTotalBytesSent(); + uint64_t GetTotalBytesRecv() const; + uint64_t GetTotalBytesSent() const; /** Get a unique deterministic randomizer. */ CSipHasher GetDeterministicRandomizer(uint64_t id) const; @@ -1348,8 +1346,8 @@ friend class CNode; void UnregisterEvents(CNode* pnode); // Network usage totals - RecursiveMutex cs_totalBytesRecv; - RecursiveMutex cs_totalBytesSent; + mutable RecursiveMutex cs_totalBytesRecv; + mutable RecursiveMutex cs_totalBytesSent; uint64_t nTotalBytesRecv GUARDED_BY(cs_totalBytesRecv) {0}; uint64_t nTotalBytesSent GUARDED_BY(cs_totalBytesSent) {0}; @@ -1375,7 +1373,7 @@ friend class CNode; std::deque m_addr_fetches GUARDED_BY(m_addr_fetches_mutex); RecursiveMutex m_addr_fetches_mutex; std::vector vAddedNodes GUARDED_BY(cs_vAddedNodes); - RecursiveMutex cs_vAddedNodes; + mutable RecursiveMutex cs_vAddedNodes; std::vector vPendingMasternodes; mutable RecursiveMutex cs_vPendingMasternodes; std::map, std::set> masternodeQuorumNodes GUARDED_BY(cs_vPendingMasternodes); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 63e7df604d90..7a6b5c23921d 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -355,7 +355,7 @@ class PeerManagerImpl final : public PeerManager /** Implement PeerManager */ void CheckForStaleTipAndEvictPeers() override; - bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) override; + bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const override; bool IgnoresIncomingTxs() override { return m_ignore_incoming_txs; } void SendPings() override; void RelayTransaction(const uint256& txid) override; @@ -1440,7 +1440,7 @@ PeerRef PeerManagerImpl::RemovePeer(NodeId id) return ret; } -bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) +bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const { { LOCK(cs_main); diff --git a/src/net_processing.h b/src/net_processing.h index 2ef1b8473c22..6b29d2e8ff73 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -63,7 +63,7 @@ class PeerManager : public CValidationInterface, public NetEventsInterface virtual ~PeerManager() { } /** Get statistics from node state */ - virtual bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) = 0; + virtual bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const = 0; /** Whether this node ignores txs received over p2p. */ virtual bool IgnoresIncomingTxs() = 0; diff --git a/src/protocol.cpp b/src/protocol.cpp index b80f439d37e0..a376eeb333dc 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -202,25 +202,16 @@ const static std::string netMessageTypesViolateBlocksOnly[] = { }; const static std::set netMessageTypesViolateBlocksOnlySet(std::begin(netMessageTypesViolateBlocksOnly), std::end(netMessageTypesViolateBlocksOnly)); -CMessageHeader::CMessageHeader() -{ - memset(pchMessageStart, 0, MESSAGE_START_SIZE); - memset(pchCommand, 0, sizeof(pchCommand)); - memset(pchChecksum, 0, CHECKSUM_SIZE); -} - CMessageHeader::CMessageHeader(const MessageStartChars& pchMessageStartIn, const char* pszCommand, unsigned int nMessageSizeIn) { memcpy(pchMessageStart, pchMessageStartIn, MESSAGE_START_SIZE); - // Copy the command name, zero-padding to COMMAND_SIZE bytes + // Copy the command name size_t i = 0; for (; i < COMMAND_SIZE && pszCommand[i] != 0; ++i) pchCommand[i] = pszCommand[i]; assert(pszCommand[i] == 0); // Assert that the command name passed in is not longer than COMMAND_SIZE - for (; i < COMMAND_SIZE; ++i) pchCommand[i] = 0; nMessageSize = nMessageSizeIn; - memset(pchChecksum, 0, CHECKSUM_SIZE); } std::string CMessageHeader::GetCommand() const diff --git a/src/protocol.h b/src/protocol.h index f68d3f7bce6e..eba54aef77bb 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -39,7 +39,7 @@ class CMessageHeader static constexpr size_t HEADER_SIZE = MESSAGE_START_SIZE + COMMAND_SIZE + MESSAGE_SIZE_SIZE + CHECKSUM_SIZE; typedef unsigned char MessageStartChars[MESSAGE_START_SIZE]; - explicit CMessageHeader(); + explicit CMessageHeader() = default; /** Construct a P2P message header from message-start characters, a command and the size of the message. * @note Passing in a `pszCommand` longer than COMMAND_SIZE will result in a run-time assertion error. @@ -51,10 +51,10 @@ class CMessageHeader SERIALIZE_METHODS(CMessageHeader, obj) { READWRITE(obj.pchMessageStart, obj.pchCommand, obj.nMessageSize, obj.pchChecksum); } - char pchMessageStart[MESSAGE_START_SIZE]; - char pchCommand[COMMAND_SIZE]; + char pchMessageStart[MESSAGE_START_SIZE]{}; + char pchCommand[COMMAND_SIZE]{}; uint32_t nMessageSize{std::numeric_limits::max()}; - uint8_t pchChecksum[CHECKSUM_SIZE]; + uint8_t pchChecksum[CHECKSUM_SIZE]{}; }; /** diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index 7faa1d25d387..90f6d8eb3477 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -1026,14 +1026,17 @@ - + + + The type of peer connection:<ul><li>Inbound: initiated by peer</li><li>Outbound Full Relay: default</li><li>Outbound Block Relay: does not relay transactions or addresses</li><li>Outbound Manual: added using RPC %1 or %2/%3 configuration options</li><li>Outbound Feeler: short-lived, for testing addresses</li><li>Outbound Address Fetch: short-lived, for soliciting addresses</li></ul> + - Direction + Connection Type - + IBeamCursor diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 2fd1f3261152..d6fb85473bba 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -1660,6 +1660,19 @@ QString NetworkToQString(Network net) assert(false); } +QString ConnectionTypeToQString(ConnectionType conn_type) +{ + switch (conn_type) { + case ConnectionType::INBOUND: return QObject::tr("Inbound"); + case ConnectionType::OUTBOUND_FULL_RELAY: return QObject::tr("Outbound Full Relay"); + case ConnectionType::BLOCK_RELAY: return QObject::tr("Outbound Block Relay"); + case ConnectionType::MANUAL: return QObject::tr("Outbound Manual"); + case ConnectionType::FEELER: return QObject::tr("Outbound Feeler"); + case ConnectionType::ADDR_FETCH: return QObject::tr("Outbound Address Fetch"); + } // no default case, so the compiler can warn about missing cases + assert(false); +} + QString formatDurationStr(int secs) { QStringList strList; diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index d53ac4522c02..cbe8cb239c6a 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -15,13 +16,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include @@ -396,6 +397,9 @@ namespace GUIUtil /** Convert enum Network to QString */ QString NetworkToQString(Network net); + /** Convert enum ConnectionType to QString */ + QString ConnectionTypeToQString(ConnectionType conn_type); + /** Convert seconds into a QString with days, hours, mins, secs */ QString formatDurationStr(int secs); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index dd7f52c56d57..59b25abde8fb 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -479,6 +479,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, QWidget* parent, Qt::WindowFlags ui->dataDir->setToolTip(ui->dataDir->toolTip().arg(QString(nonbreaking_hyphen) + "datadir")); ui->blocksDir->setToolTip(ui->blocksDir->toolTip().arg(QString(nonbreaking_hyphen) + "blocksdir")); ui->openDebugLogfileButton->setToolTip(ui->openDebugLogfileButton->toolTip().arg(PACKAGE_NAME)); + ui->peerConnectionTypeLabel->setToolTip(ui->peerConnectionTypeLabel->toolTip().arg("addnode").arg(QString(nonbreaking_hyphen) + "addnode").arg(QString(nonbreaking_hyphen) + "connect")); setButtonIcons(); @@ -1249,11 +1250,7 @@ void RPCConsole::updateDetailWidget() ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset)); ui->peerVersion->setText(QString::number(stats->nodeStats.nVersion)); ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer)); - ui->peerDirection->setText(stats->nodeStats.fInbound - ? tr("Inbound") - : stats->nodeStats.fRelayTxes - ? tr("Outbound") - : tr("Outbound block-relay")); + ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type)); ui->peerNetwork->setText(GUIUtil::NetworkToQString(stats->nodeStats.m_network)); if (stats->nodeStats.m_permissionFlags == NetPermissionFlags::None) { ui->peerPermissions->setText(tr("N/A")); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index ead13eddf3d1..82242f14ddc4 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include