refactor: Use NodeClock::time_point for m_last_send/recv and m_ping_start
What changed, and why it matters
This is a code cleanup change in Bitcoin Core. It changes how the program stores timestamps for the last time data was sent or received with a network peer, switching from raw second counts to proper clock time points. The commit message explicitly says this should not change behavior, and the diff shows equivalent conversions where the values are used. There is no indication of a security fix or vulnerability.
No security action needed. Treat as routine refactoring. Reviewers may verify that all time comparisons remain semantically equivalent, particularly the inactivity check and ping timeout arithmetic.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors m_last_send, m_last_recv, and m_ping_start from std::chrono::seconds/microseconds durations to NodeClock::time_point. It updates call sites to use NodeClock::now() and converts back to epoch seconds only at RPC output boundaries. The inactivity check logic is adjusted to compare time_points/durations consistently. No security-relevant behavioral change is evident.
Changed components
src/net.cppsrc/net.hsrc/net_processing.cppsrc/qt/rpcconsole.cppsrc/qt/rpcconsole.hsrc/rpc/net.cppInspect captured patch +35 / −28
diff --git a/src/net.cpp b/src/net.cpp
index 399b41f3..7152f62a 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -664,7 +664,7 @@ bool CNode::ReceiveMsgBytes(std::span<const uint8_t> msg_bytes, bool& complete)
complete = false;
const auto time{NodeClock::now()};
LOCK(cs_vRecv);
- m_last_recv = std::chrono::duration_cast<std::chrono::seconds>(time.time_since_epoch());
+ m_last_recv = time;
nRecvBytes += msg_bytes.size();
while (msg_bytes.size() > 0) {
// absorb network data
@@ -1643,7 +1643,7 @@ std::pair<size_t, bool> CConnman::SocketSendData(CNode& node) const
nBytes = node.m_sock->Send(data.data(), data.size(), flags);
}
if (nBytes > 0) {
- node.m_last_send = GetTime<std::chrono::seconds>();
+ node.m_last_send = NodeClock::now();
node.nSendBytes += nBytes;
// Notify transport that bytes have been processed.
node.m_transport->MarkBytesSent(nBytes);
@@ -2005,12 +2005,12 @@ void CConnman::NotifyNumConnectionsChanged()
}
}
-bool CConnman::ShouldRunInactivityChecks(const CNode& node, std::chrono::microseconds now) const
+bool CConnman::ShouldRunInactivityChecks(const CNode& node, NodeClock::time_point now) const
{
- return node.m_connected + m_peer_connect_timeout < now;
+ return node.m_connected + m_peer_connect_timeout < now.time_since_epoch();
}
-bool CConnman::InactivityCheck(const CNode& node, std::chrono::microseconds now) const
+bool CConnman::InactivityCheck(const CNode& node, NodeClock::time_point now) const
{
// Tests that see disconnects after using mocktime can start nodes with a
// large timeout. For example, -peertimeout=999999999.
@@ -2019,8 +2019,8 @@ bool CConnman::InactivityCheck(const CNode& node, std::chrono::microseconds now)
if (!ShouldRunInactivityChecks(node, now)) return false;
- bool has_received{last_recv.count() != 0};
- bool has_sent{last_send.count() != 0};
+ bool has_received{last_recv > NodeClock::epoch};
+ bool has_sent{last_send > NodeClock::epoch};
if (!has_received || !has_sent) {
std::string has_never;
@@ -2127,7 +2127,7 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
{
AssertLockNotHeld(m_total_bytes_sent_mutex);
- auto now = GetTime<std::chrono::microseconds>();
+ const auto now{NodeClock::now()};
for (CNode* pnode : nodes) {
if (m_interrupt_net->interrupted()) {
diff --git a/src/net.h b/src/net.h
index acef9035..4f082b4e 100644
--- a/src/net.h
+++ b/src/net.h
@@ -192,8 +192,8 @@ class CNodeStats
{
public:
NodeId nodeid;
- std::chrono::seconds m_last_send;
- std::chrono::seconds m_last_recv;
+ NodeClock::time_point m_last_send;
+ NodeClock::time_point m_last_recv;
std::chrono::seconds m_last_tx_time;
std::chrono::seconds m_last_block_time;
std::chrono::seconds m_connected;
@@ -707,8 +707,8 @@ public:
uint64_t nRecvBytes GUARDED_BY(cs_vRecv){0};
- std::atomic<std::chrono::seconds> m_last_send{0s};
- std::atomic<std::chrono::seconds> m_last_recv{0s};
+ std::atomic<NodeClock::time_point> m_last_send{NodeClock::epoch};
+ std::atomic<NodeClock::time_point> m_last_recv{NodeClock::epoch};
//! Unix epoch time at peer connection
const std::chrono::seconds m_connected;
// Address of this peer
@@ -1396,7 +1396,7 @@ public:
void WakeMessageHandler() EXCLUSIVE_LOCKS_REQUIRED(!mutexMsgProc);
/** Return true if we should disconnect the peer for failing an inactivity check. */
- bool ShouldRunInactivityChecks(const CNode& node, std::chrono::microseconds now) const;
+ bool ShouldRunInactivityChecks(const CNode& node, NodeClock::time_point now) const;
bool MultipleManualOrFullOutboundConns(Network net) const EXCLUSIVE_LOCKS_REQUIRED(m_nodes_mutex);
@@ -1447,7 +1447,7 @@ private:
void DisconnectNodes() EXCLUSIVE_LOCKS_REQUIRED(!m_reconnections_mutex, !m_nodes_mutex);
void NotifyNumConnectionsChanged();
/** Return true if the peer is inactive and should be disconnected. */
- bool InactivityCheck(const CNode& node, std::chrono::microseconds now) const;
+ bool InactivityCheck(const CNode& node, NodeClock::time_point now) const;
/**
* Generate a collection of sockets to check for IO readiness.
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 507be59c..6b352426 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -275,7 +275,7 @@ struct Peer {
/** The pong reply we're expecting, or 0 if no pong expected. */
std::atomic<uint64_t> m_ping_nonce_sent{0};
/** When the last ping was sent, or 0 if no ping was ever sent */
- std::atomic<std::chrono::microseconds> m_ping_start{0us};
+ std::atomic<NodeClock::time_point> m_ping_start{NodeClock::epoch};
/** Whether a ping has been requested by the user */
std::atomic<bool> m_ping_queued{false};
@@ -732,7 +732,7 @@ private:
* May mark the peer to be disconnected if a ping has timed out.
* We use mockable time for ping timeouts, so setmocktime may cause pings
* to time out. */
- void MaybeSendPing(CNode& node_to, Peer& peer, std::chrono::microseconds now);
+ void MaybeSendPing(CNode& node_to, Peer& peer, NodeClock::time_point now);
/** Send `addr` messages on a regular schedule. */
void MaybeSendAddr(CNode& node, Peer& peer, std::chrono::microseconds current_time) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
@@ -1809,9 +1809,9 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) c
// since pingtime does not update until the ping is complete, which might take a while.
// So, if a ping is taking an unusually long time in flight,
// the caller can immediately detect that this is happening.
- auto ping_wait{0us};
- if ((0 != peer->m_ping_nonce_sent) && (0 != peer->m_ping_start.load().count())) {
- ping_wait = GetTime<std::chrono::microseconds>() - peer->m_ping_start.load();
+ NodeClock::duration ping_wait{0us};
+ if ((0 != peer->m_ping_nonce_sent) && (peer->m_ping_start.load() > NodeClock::epoch)) {
+ ping_wait = NodeClock::now() - peer->m_ping_start.load();
}
if (auto tx_relay = peer->GetTxRelay(); tx_relay != nullptr) {
@@ -4158,7 +4158,7 @@ void PeerManagerImpl::ProcessMessage(Peer& peer, CNode& pfrom, const std::string
MakeAndPushMessage(pfrom, NetMsgType::TX, TX_WITH_WITNESS(*pushed_tx));
peer.m_ping_queued = true; // Ensure a ping will be sent: mimic a request via RPC.
- MaybeSendPing(pfrom, peer, GetTime<std::chrono::microseconds>());
+ MaybeSendPing(pfrom, peer, NodeClock::now());
} else {
LogDebug(BCLog::PRIVBROADCAST, "Disconnecting: got an unexpected GETDATA message, %s",
pfrom.LogPeer());
@@ -4900,7 +4900,7 @@ void PeerManagerImpl::ProcessMessage(Peer& peer, CNode& pfrom, const std::string
}
if (msg_type == NetMsgType::PONG) {
- const auto ping_end{time_received.time_since_epoch()};
+ const auto ping_end{time_received};
uint64_t nonce = 0;
size_t nAvail = vRecv.in_avail();
bool bPingFinished = false;
@@ -5396,7 +5396,7 @@ void PeerManagerImpl::CheckForStaleTipAndEvictPeers()
}
}
-void PeerManagerImpl::MaybeSendPing(CNode& node_to, Peer& peer, std::chrono::microseconds now)
+void PeerManagerImpl::MaybeSendPing(CNode& node_to, Peer& peer, NodeClock::time_point now)
{
if (m_connman.ShouldRunInactivityChecks(node_to, now) &&
peer.m_ping_nonce_sent &&
@@ -5733,6 +5733,7 @@ bool PeerManagerImpl::SendMessages(CNode& node)
if (!node.fSuccessfullyConnected || node.fDisconnect)
return true;
+ const auto now{NodeClock::now()};
const auto current_time{GetTime<std::chrono::microseconds>()};
// The logic below does not apply to private broadcast peers, so skip it.
@@ -5753,7 +5754,7 @@ bool PeerManagerImpl::SendMessages(CNode& node)
return true;
}
- MaybeSendPing(node, peer, current_time);
+ MaybeSendPing(node, peer, now);
// MaybeSendPing may have marked peer for disconnection
if (node.fDisconnect) return true;
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index d30a8acf..fef1f687 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -1165,12 +1165,13 @@ void RPCConsole::updateDetailWidget()
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 auto now{NodeClock::now()};
const auto time_now{GetTime<std::chrono::seconds>()};
ui->peerConnTime->setText(GUIUtil::formatDurationStr(time_now - stats->nodeStats.m_connected));
ui->peerLastBlock->setText(TimeDurationField(time_now, stats->nodeStats.m_last_block_time));
ui->peerLastTx->setText(TimeDurationField(time_now, stats->nodeStats.m_last_tx_time));
- ui->peerLastSend->setText(TimeDurationField(time_now, stats->nodeStats.m_last_send));
- ui->peerLastRecv->setText(TimeDurationField(time_now, stats->nodeStats.m_last_recv));
+ ui->peerLastSend->setText(TimeDurationField(now, stats->nodeStats.m_last_send));
+ ui->peerLastRecv->setText(TimeDurationField(now, stats->nodeStats.m_last_recv));
ui->peerBytesSent->setText(GUIUtil::formatBytes(stats->nodeStats.nSendBytes));
ui->peerBytesRecv->setText(GUIUtil::formatBytes(stats->nodeStats.nRecvBytes));
ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.m_last_ping_time));
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index bbd469cd..0c9f7040 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -182,7 +182,12 @@ private:
/** Update UI with latest network info from model. */
void updateNetworkState();
- /** Helper for the output of a time duration field. Inputs are UNIX epoch times. */
+ /// Format the duration between now and event as a string.
+ QString TimeDurationField(NodeClock::time_point now, NodeClock::time_point event) const
+ {
+ return event > NodeClock::epoch ? GUIUtil::formatDurationStr(now - event) : tr("Never");
+ }
+ /// DEPRECATED: Helper for the output of a time duration field. Inputs are UNIX epoch times.
QString TimeDurationField(std::chrono::seconds time_now, std::chrono::seconds time_at_event) const
{
return time_at_event.count() ? GUIUtil::formatDurationStr(time_now - time_at_event) : tr("Never");
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 9c71e4ee..ff2adf82 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -242,8 +242,8 @@ static RPCHelpMan getpeerinfo()
obj.pushKV("relaytxes", statestats.m_relay_txs);
obj.pushKV("last_inv_sequence", statestats.m_last_inv_seq);
obj.pushKV("inv_to_send", statestats.m_inv_to_send);
- obj.pushKV("lastsend", count_seconds(stats.m_last_send));
- obj.pushKV("lastrecv", count_seconds(stats.m_last_recv));
+ obj.pushKV("lastsend", TicksSinceEpoch<std::chrono::seconds>(stats.m_last_send));
+ obj.pushKV("lastrecv", TicksSinceEpoch<std::chrono::seconds>(stats.m_last_recv));
obj.pushKV("last_transaction", count_seconds(stats.m_last_tx_time));
obj.pushKV("last_block", count_seconds(stats.m_last_block_time));
obj.pushKV("bytessent", stats.nSendBytes);
Why this scored 14/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.