diff --git a/src/common/system.cpp b/src/common/system.cpp index 72e9de100be..98bc01471d5 100644 --- a/src/common/system.cpp +++ b/src/common/system.cpp @@ -37,9 +37,6 @@ using util::ReplaceAll; -// Application startup time (used for uptime calculation) -const int64_t nStartupTime = GetTime(); - #ifndef WIN32 std::string ShellEscape(const std::string& arg) { @@ -130,8 +127,8 @@ std::optional GetTotalRAM() return std::nullopt; } -// Obtain the application startup time (used for uptime calculation) -int64_t GetStartupTime() +SteadyClock::duration GetUptime() { - return nStartupTime; + static const auto g_startup_time{SteadyClock::now()}; + return SteadyClock::now() - g_startup_time; } diff --git a/src/common/system.h b/src/common/system.h index 2184f1d4c9e..a3100fecbc1 100644 --- a/src/common/system.h +++ b/src/common/system.h @@ -7,13 +7,15 @@ #define BITCOIN_COMMON_SYSTEM_H #include // IWYU pragma: keep +#include +#include #include #include #include -// Application startup time (used for uptime calculation) -int64_t GetStartupTime(); +/// Monotonic uptime (not affected by system time changes). +SteadyClock::duration GetUptime(); void SetupEnvironment(); [[nodiscard]] bool SetupNetworking(); diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index dda26fa059b..cd7d9e32587 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -210,7 +210,7 @@ bool ClientModel::isReleaseVersion() const QString ClientModel::formatClientStartupTime() const { - return QDateTime::fromSecsSinceEpoch(GetStartupTime()).toString(); + return QDateTime::currentDateTime().addSecs(-TicksSeconds(GetUptime())).toString(); } QString ClientModel::dataDir() const diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 5fd733d056b..01fb815a967 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -184,7 +184,7 @@ static RPCHelpMan uptime() }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - return GetTime() - GetStartupTime(); + return TicksSeconds(GetUptime()); } }; } diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index eb6057a6ffb..2b4e5330e9e 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -600,6 +600,15 @@ BOOST_AUTO_TEST_CASE(util_mocktime) SetMockTime(0s); } +BOOST_AUTO_TEST_CASE(util_ticksseconds) +{ + BOOST_CHECK_EQUAL(TicksSeconds(0s), 0); + BOOST_CHECK_EQUAL(TicksSeconds(1s), 1); + BOOST_CHECK_EQUAL(TicksSeconds(999ms), 0); + BOOST_CHECK_EQUAL(TicksSeconds(1000ms), 1); + BOOST_CHECK_EQUAL(TicksSeconds(1500ms), 1); +} + BOOST_AUTO_TEST_CASE(test_IsDigit) { BOOST_CHECK_EQUAL(IsDigit('0'), true); diff --git a/src/util/time.h b/src/util/time.h index 0f98726d224..59abc4d5a7f 100644 --- a/src/util/time.h +++ b/src/util/time.h @@ -74,6 +74,12 @@ constexpr auto Ticks(Dur2 d) { return std::chrono::duration_cast(d).count(); } + +template +constexpr int64_t TicksSeconds(Duration d) +{ + return int64_t{Ticks(d)}; +} template constexpr auto TicksSinceEpoch(Timepoint t) { diff --git a/test/functional/rpc_uptime.py b/test/functional/rpc_uptime.py index 74880417941..817ba2b4b53 100755 --- a/test/functional/rpc_uptime.py +++ b/test/functional/rpc_uptime.py @@ -26,9 +26,12 @@ class UptimeTest(BitcoinTestFramework): assert_raises_rpc_error(-8, "Mocktime must be in the range [0, 9223372036], not -1.", self.nodes[0].setmocktime, -1) def _test_uptime(self): - wait_time = 10 - self.nodes[0].setmocktime(int(time.time() + wait_time)) - assert self.nodes[0].uptime() >= wait_time + wait_time = 20_000 + uptime_before = self.nodes[0].uptime() + self.nodes[0].setmocktime(int(time.time()) + wait_time) + uptime_after = self.nodes[0].uptime() + self.nodes[0].setmocktime(0) + assert uptime_after - uptime_before < wait_time, "uptime should not jump with wall clock" if __name__ == '__main__':