Skip to content

Commit 372dd8d

Browse files
committed
Merge #21110: util: remove Boost posix_time usage from GetTime*
9266f74 util: Use std::chrono for time getters (MarcoFalke) 3c2e16b time: add runtime sanity check (Cory Fields) Pull request description: I have a followup that should remove the last of our `boost:posix_time` usage in `ParseISO8601DateTime`, but that will likely need more cross-platform testing/discussion, so have just split them up as this change is straight forward. ACKs for top commit: practicalswift: Tested ACK 9266f74 laanwj: Code review ACK 9266f74 Tree-SHA512: 5471a60e65e9fa8ef48320743ef637f1d162724e717e0f5509118e1e5732fc0844656a9c09d3d1300eb657dcc7a1e1e67305d8c9ef959c63be67393607dd4ceb
2 parents 0433608 + 9266f74 commit 372dd8d

File tree

4 files changed

+63
-9
lines changed

4 files changed

+63
-9
lines changed

src/init.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,10 @@ static bool InitSanityCheck()
773773
return InitError(Untranslated("OS cryptographic RNG sanity check failure. Aborting."));
774774
}
775775

776+
if (!ChronoSanityCheck()) {
777+
return InitError(Untranslated("Clock epoch mismatch. Aborting."));
778+
}
779+
776780
return true;
777781
}
778782

src/test/sanity_tests.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <compat/sanity.h>
66
#include <key.h>
77
#include <test/util/setup_common.h>
8+
#include <util/time.h>
89

910
#include <boost/test/unit_test.hpp>
1011

@@ -15,6 +16,7 @@ BOOST_AUTO_TEST_CASE(basic_sanity)
1516
BOOST_CHECK_MESSAGE(glibc_sanity_test() == true, "libc sanity test");
1617
BOOST_CHECK_MESSAGE(glibcxx_sanity_test() == true, "stdlib sanity test");
1718
BOOST_CHECK_MESSAGE(ECC_InitSanityCheck() == true, "secp256k1 sanity test");
19+
BOOST_CHECK_MESSAGE(ChronoSanityCheck() == true, "chrono epoch test");
1820
}
1921

2022
BOOST_AUTO_TEST_SUITE_END()

src/util/time.cpp

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,49 @@ int64_t GetTime()
3333
return now;
3434
}
3535

36+
bool ChronoSanityCheck()
37+
{
38+
// std::chrono::system_clock.time_since_epoch and time_t(0) are not guaranteed
39+
// to use the Unix epoch timestamp, prior to C++20, but in practice they almost
40+
// certainly will. Any differing behavior will be assumed to be an error, unless
41+
// certain platforms prove to consistently deviate, at which point we'll cope
42+
// with it by adding offsets.
43+
44+
// Create a new clock from time_t(0) and make sure that it represents 0
45+
// seconds from the system_clock's time_since_epoch. Then convert that back
46+
// to a time_t and verify that it's the same as before.
47+
const time_t time_t_epoch{};
48+
auto clock = std::chrono::system_clock::from_time_t(time_t_epoch);
49+
if (std::chrono::duration_cast<std::chrono::seconds>(clock.time_since_epoch()).count() != 0) {
50+
return false;
51+
}
52+
53+
time_t time_val = std::chrono::system_clock::to_time_t(clock);
54+
if (time_val != time_t_epoch) {
55+
return false;
56+
}
57+
58+
// Check that the above zero time is actually equal to the known unix timestamp.
59+
struct tm epoch;
60+
#ifdef HAVE_GMTIME_R
61+
if (gmtime_r(&time_val, &epoch) == nullptr) {
62+
#else
63+
if (gmtime_s(&epoch, &time_val) != 0) {
64+
#endif
65+
return false;
66+
}
67+
68+
if ((epoch.tm_sec != 0) ||
69+
(epoch.tm_min != 0) ||
70+
(epoch.tm_hour != 0) ||
71+
(epoch.tm_mday != 1) ||
72+
(epoch.tm_mon != 0) ||
73+
(epoch.tm_year != 70)) {
74+
return false;
75+
}
76+
return true;
77+
}
78+
3679
template <typename T>
3780
T GetTime()
3881
{
@@ -47,6 +90,14 @@ template std::chrono::seconds GetTime();
4790
template std::chrono::milliseconds GetTime();
4891
template std::chrono::microseconds GetTime();
4992

93+
template <typename T>
94+
static T GetSystemTime()
95+
{
96+
const auto now = std::chrono::duration_cast<T>(std::chrono::system_clock::now().time_since_epoch());
97+
assert(now.count() > 0);
98+
return now;
99+
}
100+
50101
void SetMockTime(int64_t nMockTimeIn)
51102
{
52103
Assert(nMockTimeIn >= 0);
@@ -65,23 +116,17 @@ std::chrono::seconds GetMockTime()
65116

66117
int64_t GetTimeMillis()
67118
{
68-
int64_t now = (boost::posix_time::microsec_clock::universal_time() -
69-
boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_milliseconds();
70-
assert(now > 0);
71-
return now;
119+
return int64_t{GetSystemTime<std::chrono::milliseconds>().count()};
72120
}
73121

74122
int64_t GetTimeMicros()
75123
{
76-
int64_t now = (boost::posix_time::microsec_clock::universal_time() -
77-
boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_microseconds();
78-
assert(now > 0);
79-
return now;
124+
return int64_t{GetSystemTime<std::chrono::microseconds>().count()};
80125
}
81126

82127
int64_t GetSystemTimeInSeconds()
83128
{
84-
return GetTimeMicros()/1000000;
129+
return int64_t{GetSystemTime<std::chrono::seconds>().count()};
85130
}
86131

87132
std::string FormatISO8601DateTime(int64_t nTime) {

src/util/time.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,7 @@ struct timeval MillisToTimeval(int64_t nTimeout);
7979
*/
8080
struct timeval MillisToTimeval(std::chrono::milliseconds ms);
8181

82+
/** Sanity check epoch match normal Unix epoch */
83+
bool ChronoSanityCheck();
84+
8285
#endif // BITCOIN_UTIL_TIME_H

0 commit comments

Comments
 (0)