Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify network-adjusted time warning logic #29623

Merged
merged 6 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ BITCOIN_CORE_H = \
node/peerman_args.h \
node/protocol_version.h \
node/psbt.h \
node/timeoffsets.h \
node/transaction.h \
node/txreconciliation.h \
node/utxo_snapshot.h \
Expand Down Expand Up @@ -283,7 +284,6 @@ BITCOIN_CORE_H = \
support/lockedpool.h \
sync.h \
threadsafety.h \
timedata.h \
torcontrol.h \
txdb.h \
txmempool.h \
Expand Down Expand Up @@ -435,6 +435,7 @@ libbitcoin_node_a_SOURCES = \
node/minisketchwrapper.cpp \
node/peerman_args.cpp \
node/psbt.cpp \
node/timeoffsets.cpp \
node/transaction.cpp \
node/txreconciliation.cpp \
node/utxo_snapshot.cpp \
Expand Down Expand Up @@ -462,7 +463,6 @@ libbitcoin_node_a_SOURCES = \
rpc/txoutproof.cpp \
script/sigcache.cpp \
signet.cpp \
timedata.cpp \
torcontrol.cpp \
txdb.cpp \
txmempool.cpp \
Expand Down
4 changes: 2 additions & 2 deletions src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ BITCOIN_TESTS =\
test/streams_tests.cpp \
test/sync_tests.cpp \
test/system_tests.cpp \
test/timedata_tests.cpp \
test/timeoffsets_tests.cpp \
test/torcontrol_tests.cpp \
test/transaction_tests.cpp \
test/translation_tests.cpp \
Expand Down Expand Up @@ -381,7 +381,7 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/string.cpp \
test/fuzz/strprintf.cpp \
test/fuzz/system.cpp \
test/fuzz/timedata.cpp \
test/fuzz/timeoffsets.cpp \
test/fuzz/torcontrol.cpp \
test/fuzz/transaction.cpp \
test/fuzz/tx_in.cpp \
Expand Down
1 change: 0 additions & 1 deletion src/addrman_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
#include <protocol.h>
#include <serialize.h>
#include <sync.h>
#include <timedata.h>
#include <uint256.h>
#include <util/time.h>

Expand Down
2 changes: 0 additions & 2 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@
#include <scheduler.h>
#include <script/sigcache.h>
#include <sync.h>
#include <timedata.h>
#include <torcontrol.h>
#include <txdb.h>
#include <txmempool.h>
Expand Down Expand Up @@ -523,7 +522,6 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-maxconnections=<n>", strprintf("Maintain at most <n> automatic connections to peers (default: %u). This limit does not apply to connections manually added via -addnode or the addnode RPC, which have a separate limit of %u.", DEFAULT_MAX_PEER_CONNECTIONS, MAX_ADDNODE_CONNECTIONS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-maxreceivebuffer=<n>", strprintf("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXRECEIVEBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-maxsendbuffer=<n>", strprintf("Maximum per-connection memory usage for the send buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXSENDBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-maxtimeadjustment", strprintf("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by outbound peers forward or backward by this amount (default: %u seconds).", DEFAULT_MAX_TIME_ADJUSTMENT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target per 24h. Limit does not apply to peers with 'download' permission or blocks created within past week. 0 = no limit (default: %s). Optional suffix units [k|K|m|M|g|G|t|T] (default: M). Lowercase is 1000 base while uppercase is 1024 base", DEFAULT_MAX_UPLOAD_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
#if HAVE_SOCKADDR_UN
argsman.AddArg("-onion=<ip:port|path>", "Use separate SOCKS5 proxy to reach peers via Tor onion services, set -noonion to disable (default: -proxy). May be a local file path prefixed with 'unix:'.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
Expand Down
1 change: 0 additions & 1 deletion src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,6 @@ void CNode::CopyStats(CNodeStats& stats)
X(m_last_tx_time);
X(m_last_block_time);
X(m_connected);
X(nTimeOffset);
X(m_addr_name);
X(nVersion);
{
Expand Down
2 changes: 0 additions & 2 deletions src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,6 @@ class CNodeStats
std::chrono::seconds m_last_tx_time;
std::chrono::seconds m_last_block_time;
std::chrono::seconds m_connected;
int64_t nTimeOffset;
std::string m_addr_name;
int nVersion;
std::string cleanSubVer;
Expand Down Expand Up @@ -703,7 +702,6 @@ class CNode
std::atomic<std::chrono::seconds> m_last_recv{0s};
//! Unix epoch time at peer connection
const std::chrono::seconds m_connected;
std::atomic<int64_t> nTimeOffset{0};
// Address of this peer
const CAddress addr;
// Bind address of our side of the connection
Expand Down
27 changes: 21 additions & 6 deletions src/net_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <netbase.h>
#include <netmessagemaker.h>
#include <node/blockstorage.h>
#include <node/timeoffsets.h>
#include <node/txreconciliation.h>
#include <policy/fees.h>
#include <policy/policy.h>
Expand All @@ -34,7 +35,6 @@
#include <scheduler.h>
#include <streams.h>
#include <sync.h>
#include <timedata.h>
#include <tinyformat.h>
#include <txmempool.h>
#include <txorphanage.h>
Expand Down Expand Up @@ -390,6 +390,10 @@ struct Peer {
/** Whether this peer wants invs or headers (when possible) for block announcements */
bool m_prefers_headers GUARDED_BY(NetEventsInterface::g_msgproc_mutex){false};

/** Time offset computed during the version handshake based on the
* timestamp the peer sent in the version message. */
std::atomic<std::chrono::seconds> m_time_offset{0s};
stickies-v marked this conversation as resolved.
Show resolved Hide resolved

explicit Peer(NodeId id, ServiceFlags our_services)
: m_id{id}
, m_our_services{our_services}
Expand Down Expand Up @@ -513,7 +517,7 @@ class PeerManagerImpl final : public PeerManager
std::optional<std::string> FetchBlock(NodeId peer_id, const CBlockIndex& block_index) override
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
bool IgnoresIncomingTxs() override { return m_opts.ignore_incoming_txs; }
PeerManagerInfo GetInfo() const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void SendPings() override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void RelayTransaction(const uint256& txid, const uint256& wtxid) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
void SetBestBlock(int height, std::chrono::seconds time) override
Expand Down Expand Up @@ -749,6 +753,8 @@ class PeerManagerImpl final : public PeerManager
/** Next time to check for stale tip */
std::chrono::seconds m_stale_tip_check_time GUARDED_BY(cs_main){0s};

TimeOffsets m_outbound_time_offsets;
stickies-v marked this conversation as resolved.
Show resolved Hide resolved

const Options m_opts;

bool RejectIncomingTxs(const CNode& peer) const;
Expand Down Expand Up @@ -1792,10 +1798,19 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) c
stats.presync_height = peer->m_headers_sync->GetPresyncHeight();
}
}
stats.time_offset = peer->m_time_offset;

return true;
}

PeerManagerInfo PeerManagerImpl::GetInfo() const
{
return PeerManagerInfo{
.median_outbound_time_offset = m_outbound_time_offsets.Median(),
.ignores_incoming_txs = m_opts.ignore_incoming_txs,
};
}

void PeerManagerImpl::AddToCompactExtraTransactions(const CTransactionRef& tx)
{
if (m_opts.max_extra_txs <= 0)
Expand Down Expand Up @@ -3666,12 +3681,12 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
peer->m_starting_height, addrMe.ToStringAddrPort(), fRelay, pfrom.GetId(),
remoteAddr, (mapped_as ? strprintf(", mapped_as=%d", mapped_as) : ""));

int64_t nTimeOffset = nTime - GetTime();
pfrom.nTimeOffset = nTimeOffset;
peer->m_time_offset = NodeSeconds{std::chrono::seconds{nTime}} - Now<NodeSeconds>();
if (!pfrom.IsInboundConn()) {
// Don't use timedata samples from inbound peers to make it
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After 2ef71c7, timedata is not a thing anymore, so this may need updating

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated to Don't use time offset samples, thanks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this was covered (or it may have been regressed)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I think it must have regressed with this update in the next line - will fix if I have to retouch.

// harder for others to tamper with our adjusted time.
AddTimeData(pfrom.addr, nTimeOffset);
// harder for others to create false warnings about our clock being out of sync.
m_outbound_time_offsets.Add(peer->m_time_offset);
m_outbound_time_offsets.WarnIfOutOfSync();
}

// If the peer is old enough to have the old alert system, send it the final alert.
Expand Down
12 changes: 10 additions & 2 deletions src/net_processing.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include <net.h>
#include <validationinterface.h>

#include <chrono>

class AddrMan;
class CChainParams;
class CTxMemPool;
Expand Down Expand Up @@ -41,6 +43,12 @@ struct CNodeStateStats {
bool m_addr_relay_enabled{false};
ServiceFlags their_services;
int64_t presync_height{-1};
std::chrono::seconds time_offset{0};
};

struct PeerManagerInfo {
std::chrono::seconds median_outbound_time_offset{0s};
bool ignores_incoming_txs{false};
};

class PeerManager : public CValidationInterface, public NetEventsInterface
Expand Down Expand Up @@ -83,8 +91,8 @@ class PeerManager : public CValidationInterface, public NetEventsInterface
/** Get statistics from node state */
virtual bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const = 0;

/** Whether this node ignores txs received over p2p. */
virtual bool IgnoresIncomingTxs() = 0;
/** Get peer manager info. */
virtual PeerManagerInfo GetInfo() const = 0;

/** Relay transaction to all peers. */
virtual void RelayTransaction(const uint256& txid, const uint256& wtxid) = 0;
Expand Down
69 changes: 69 additions & 0 deletions src/node/timeoffsets.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (c) 2024-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <logging.h>
#include <node/interface_ui.h>
#include <node/timeoffsets.h>
#include <sync.h>
#include <tinyformat.h>
#include <util/time.h>
#include <util/translation.h>
#include <warnings.h>

#include <algorithm>
#include <chrono>
#include <cstdint>
#include <deque>
#include <limits>
#include <optional>

using namespace std::chrono_literals;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e6d7bcf doesn't look like chrono_literals is required here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed it and this seems to run fine, at least in my system

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We #include <util/time.h> which has using namespace std::chrono_literals; which is why it compiles. I thought we would prefer (IWYU-style) to be explicit about used namespaces, but upon further investigation the purpose of #20602 seems to have been to avoid that, so I'll remove this if I re-touch.


void TimeOffsets::Add(std::chrono::seconds offset)
stickies-v marked this conversation as resolved.
Show resolved Hide resolved
{
LOCK(m_mutex);

if (m_offsets.size() >= MAX_SIZE) {
m_offsets.pop_front();
}
m_offsets.push_back(offset);
LogDebug(BCLog::NET, "Added time offset %+ds, total samples %d\n",
Ticks<std::chrono::seconds>(offset), m_offsets.size());
}

std::chrono::seconds TimeOffsets::Median() const
{
LOCK(m_mutex);

// Only calculate the median if we have 5 or more offsets
stickies-v marked this conversation as resolved.
Show resolved Hide resolved
if (m_offsets.size() < 5) return 0s;

auto sorted_copy = m_offsets;
stickies-v marked this conversation as resolved.
Show resolved Hide resolved
std::sort(sorted_copy.begin(), sorted_copy.end());
return sorted_copy[sorted_copy.size() / 2]; // approximate median is good enough, keep it simple
}

bool TimeOffsets::WarnIfOutOfSync() const
{
// when median == std::numeric_limits<int64_t>::min(), calling std::chrono::abs is UB
auto median{std::max(Median(), std::chrono::seconds(std::numeric_limits<int64_t>::min() + 1))};
if (std::chrono::abs(median) <= WARN_THRESHOLD) {
SetMedianTimeOffsetWarning(std::nullopt);
uiInterface.NotifyAlertChanged();
return false;
}

bilingual_str msg{strprintf(_(
"Your computer's date and time appear to be more than %d minutes out of sync with the network, "
"this may lead to consensus failure. After you've confirmed your computer's clock, this message "
"should no longer appear when you restart your node. Without a restart, it should stop showing "
"automatically after you've connected to a sufficient number of new outbound peers, which may "
"take some time. You can inspect the `timeoffset` field of the `getpeerinfo` and `getnetworkinfo` "
"RPC methods to get more info."
), Ticks<std::chrono::minutes>(WARN_THRESHOLD))};
LogWarning("%s\n", msg.original);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit in 9f2904b: This may keep printing the warning, even if the user adjusted the clock, because the offsets are not discarded when the clock changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The log message has been updated to highlight that the message may persist for a while, thanks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grepping in my node's log for how frequent outbound connections are made - it would take about 1.5 days to make 200 new outbound connections. Since this is about the median, I guess 101 new connections after fixing the clock will be enough for us to stop the warning. That is like 20ish hours. Quite long. Can we do better?

Currently we only save the difference between our time and the peer's time at the start of the connection and we have no clue that this difference has changed due to our clock being adjusted/corrected. If we save the value of the system_clock and steady_clock at the start of the connection, in addition to the peer's offset, at a later time we can deduce that our clock has been adjusted and add that diff/adjustment to the peer's offset to immediately figure out that "now our clock is in sync with that peer's clock".

So, instead of peer_offset, it would be a matter of using the following instead:
peer_offset + steady_clock_diff - system_clock_diff, assuming:
steady_clock_diff = steady_clock_now - steady_clock_when_connected
system_clock_diff = system_clock_now - system_clock_when_connected.

Example:

Lets say our clock is wrong, 15 minutes ahead. When we connect to a peer at 17:00:00 our time they send us a time that is 15 minutes ago and we save:
peer_offset = -15min,
system_clock_when_connected = 17:00:00,
steady_clock_when_connected = 1000 (some obscure value [seconds]).
One hour later, our clock has been fixed/corrected, by moving it 15 min backwards (or stopping to move for 15 min). So,
system_clock_now = 17:45:00,
steady_clock_now = 4600 (the obscure value 1000 plus 3600 because 1 hour has passed).
And then:
steady_clock_diff = 4600 - 1000 = 60 min
system_clock_diff = 17:45:00 - 17:00:00 = 45 min
The difference between those is 15 minutes and adding that to peer_offset gives us offset 0, meaning that now our clock is in sync with the peer's clock.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using both system and steady clock is an interesting idea and one I hadn't considered, thanks. I'm going to look into this a bit more and will follow up shortly.

it would take about 1.5 days to make 200 new outbound connections

FYI this PR reduces 200 -> 50 outbounds so your calculations are a bit off.

SetMedianTimeOffsetWarning(msg);
uiInterface.NotifyAlertChanged();
return true;
}
39 changes: 39 additions & 0 deletions src/node/timeoffsets.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) 2024-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_NODE_TIMEOFFSETS_H
#define BITCOIN_NODE_TIMEOFFSETS_H

#include <sync.h>

#include <chrono>
#include <cstddef>
#include <deque>

class TimeOffsets
{
//! Maximum number of timeoffsets stored.
static constexpr size_t MAX_SIZE{50};
//! Minimum difference between system and network time for a warning to be raised.
static constexpr std::chrono::minutes WARN_THRESHOLD{10};

mutable Mutex m_mutex;
/** The observed time differences between our local clock and those of our outbound peers. A
* positive offset means our peer's clock is ahead of our local clock. */
std::deque<std::chrono::seconds> m_offsets GUARDED_BY(m_mutex){};

public:
/** Add a new time offset sample. */
void Add(std::chrono::seconds offset) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);

/** Compute and return the median of the collected time offset samples. The median is returned
* as 0 when there are less than 5 samples. */
std::chrono::seconds Median() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);

/** Raise warnings if the median time offset exceeds the warnings threshold. Returns true if
* warnings were raised. */
bool WarnIfOutOfSync() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
};

#endif // BITCOIN_NODE_TIMEOFFSETS_H
4 changes: 2 additions & 2 deletions src/qt/guiutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -771,9 +771,9 @@ QString formatPingTime(std::chrono::microseconds ping_time)
QObject::tr("%1 ms").arg(QString::number((int)(count_microseconds(ping_time) / 1000), 10));
}

QString formatTimeOffset(int64_t nTimeOffset)
QString formatTimeOffset(int64_t time_offset)
{
return QObject::tr("%1 s").arg(QString::number((int)nTimeOffset, 10));
return QObject::tr("%1 s").arg(QString::number((int)time_offset, 10));
}

QString formatNiceTimeOffset(qint64 secs)
Expand Down
4 changes: 2 additions & 2 deletions src/qt/guiutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,8 @@ namespace GUIUtil
/** Format a CNodeStats.m_last_ping_time into a user-readable string or display N/A, if 0 */
QString formatPingTime(std::chrono::microseconds ping_time);

/** Format a CNodeCombinedStats.nTimeOffset into a user-readable string */
QString formatTimeOffset(int64_t nTimeOffset);
/** Format a CNodeStateStats.time_offset into a user-readable string */
QString formatTimeOffset(int64_t time_offset);

QString formatNiceTimeOffset(qint64 secs);

Expand Down
3 changes: 2 additions & 1 deletion src/qt/rpcconsole.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <rpc/server.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/time.h>
#include <util/threadnames.h>

#include <univalue.h>
Expand Down Expand Up @@ -1196,7 +1197,6 @@ void RPCConsole::updateDetailWidget()
ui->peerBytesRecv->setText(GUIUtil::formatBytes(stats->nodeStats.nRecvBytes));
ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.m_last_ping_time));
ui->peerMinPing->setText(GUIUtil::formatPingTime(stats->nodeStats.m_min_ping_time));
ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset));
if (stats->nodeStats.nVersion) {
ui->peerVersion->setText(QString::number(stats->nodeStats.nVersion));
}
Expand Down Expand Up @@ -1228,6 +1228,7 @@ void RPCConsole::updateDetailWidget()
// This check fails for example if the lock was busy and
// nodeStateStats couldn't be fetched.
if (stats->fNodeStateStatsAvailable) {
ui->timeoffset->setText(GUIUtil::formatTimeOffset(Ticks<std::chrono::seconds>(stats->nodeStateStats.time_offset)));
ui->peerServices->setText(GUIUtil::formatServicesStr(stats->nodeStateStats.their_services));
// Sync height is init to -1
if (stats->nodeStateStats.nSyncHeight > -1) {
Expand Down
8 changes: 4 additions & 4 deletions src/rpc/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
#include <rpc/server_util.h>
#include <rpc/util.h>
#include <sync.h>
#include <timedata.h>
#include <util/chaintype.h>
#include <util/strencodings.h>
#include <util/string.h>
Expand Down Expand Up @@ -239,7 +238,7 @@ static RPCHelpMan getpeerinfo()
obj.pushKV("bytessent", stats.nSendBytes);
obj.pushKV("bytesrecv", stats.nRecvBytes);
obj.pushKV("conntime", count_seconds(stats.m_connected));
obj.pushKV("timeoffset", stats.nTimeOffset);
obj.pushKV("timeoffset", Ticks<std::chrono::seconds>(statestats.time_offset));
if (stats.m_last_ping_time > 0us) {
obj.pushKV("pingtime", Ticks<SecondsDouble>(stats.m_last_ping_time));
}
Expand Down Expand Up @@ -679,9 +678,10 @@ static RPCHelpMan getnetworkinfo()
obj.pushKV("localservicesnames", GetServicesNames(services));
}
if (node.peerman) {
obj.pushKV("localrelay", !node.peerman->IgnoresIncomingTxs());
auto peerman_info{node.peerman->GetInfo()};
obj.pushKV("localrelay", !peerman_info.ignores_incoming_txs);
obj.pushKV("timeoffset", Ticks<std::chrono::seconds>(peerman_info.median_outbound_time_offset));
stickies-v marked this conversation as resolved.
Show resolved Hide resolved
}
obj.pushKV("timeoffset", GetTimeOffset());
if (node.connman) {
obj.pushKV("networkactive", node.connman->GetNetworkActive());
obj.pushKV("connections", node.connman->GetNodeCount(ConnectionDirection::Both));
Expand Down