Merge #21110: util: remove Boost posix_time usage from GetTime*

9266f7497f util: Use std::chrono for time getters (MarcoFalke)
3c2e16be22 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 9266f7497f
  laanwj:
    Code review ACK 9266f7497f

Tree-SHA512: 5471a60e65e9fa8ef48320743ef637f1d162724e717e0f5509118e1e5732fc0844656a9c09d3d1300eb657dcc7a1e1e67305d8c9ef959c63be67393607dd4ceb
This commit is contained in:
Wladimir J. van der Laan 2021-02-17 20:23:25 +01:00
commit 372dd8da24
No known key found for this signature in database
GPG Key ID: 1E4AED62986CD25D
4 changed files with 63 additions and 9 deletions

View File

@ -773,6 +773,10 @@ static bool InitSanityCheck()
return InitError(Untranslated("OS cryptographic RNG sanity check failure. Aborting."));
}
if (!ChronoSanityCheck()) {
return InitError(Untranslated("Clock epoch mismatch. Aborting."));
}
return true;
}

View File

@ -5,6 +5,7 @@
#include <compat/sanity.h>
#include <key.h>
#include <test/util/setup_common.h>
#include <util/time.h>
#include <boost/test/unit_test.hpp>
@ -15,6 +16,7 @@ BOOST_AUTO_TEST_CASE(basic_sanity)
BOOST_CHECK_MESSAGE(glibc_sanity_test() == true, "libc sanity test");
BOOST_CHECK_MESSAGE(glibcxx_sanity_test() == true, "stdlib sanity test");
BOOST_CHECK_MESSAGE(ECC_InitSanityCheck() == true, "secp256k1 sanity test");
BOOST_CHECK_MESSAGE(ChronoSanityCheck() == true, "chrono epoch test");
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -33,6 +33,49 @@ int64_t GetTime()
return now;
}
bool ChronoSanityCheck()
{
// std::chrono::system_clock.time_since_epoch and time_t(0) are not guaranteed
// to use the Unix epoch timestamp, prior to C++20, but in practice they almost
// certainly will. Any differing behavior will be assumed to be an error, unless
// certain platforms prove to consistently deviate, at which point we'll cope
// with it by adding offsets.
// Create a new clock from time_t(0) and make sure that it represents 0
// seconds from the system_clock's time_since_epoch. Then convert that back
// to a time_t and verify that it's the same as before.
const time_t time_t_epoch{};
auto clock = std::chrono::system_clock::from_time_t(time_t_epoch);
if (std::chrono::duration_cast<std::chrono::seconds>(clock.time_since_epoch()).count() != 0) {
return false;
}
time_t time_val = std::chrono::system_clock::to_time_t(clock);
if (time_val != time_t_epoch) {
return false;
}
// Check that the above zero time is actually equal to the known unix timestamp.
struct tm epoch;
#ifdef HAVE_GMTIME_R
if (gmtime_r(&time_val, &epoch) == nullptr) {
#else
if (gmtime_s(&epoch, &time_val) != 0) {
#endif
return false;
}
if ((epoch.tm_sec != 0) ||
(epoch.tm_min != 0) ||
(epoch.tm_hour != 0) ||
(epoch.tm_mday != 1) ||
(epoch.tm_mon != 0) ||
(epoch.tm_year != 70)) {
return false;
}
return true;
}
template <typename T>
T GetTime()
{
@ -47,6 +90,14 @@ template std::chrono::seconds GetTime();
template std::chrono::milliseconds GetTime();
template std::chrono::microseconds GetTime();
template <typename T>
static T GetSystemTime()
{
const auto now = std::chrono::duration_cast<T>(std::chrono::system_clock::now().time_since_epoch());
assert(now.count() > 0);
return now;
}
void SetMockTime(int64_t nMockTimeIn)
{
Assert(nMockTimeIn >= 0);
@ -65,23 +116,17 @@ std::chrono::seconds GetMockTime()
int64_t GetTimeMillis()
{
int64_t now = (boost::posix_time::microsec_clock::universal_time() -
boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_milliseconds();
assert(now > 0);
return now;
return int64_t{GetSystemTime<std::chrono::milliseconds>().count()};
}
int64_t GetTimeMicros()
{
int64_t now = (boost::posix_time::microsec_clock::universal_time() -
boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_microseconds();
assert(now > 0);
return now;
return int64_t{GetSystemTime<std::chrono::microseconds>().count()};
}
int64_t GetSystemTimeInSeconds()
{
return GetTimeMicros()/1000000;
return int64_t{GetSystemTime<std::chrono::seconds>().count()};
}
std::string FormatISO8601DateTime(int64_t nTime) {

View File

@ -79,4 +79,7 @@ struct timeval MillisToTimeval(int64_t nTimeout);
*/
struct timeval MillisToTimeval(std::chrono::milliseconds ms);
/** Sanity check epoch match normal Unix epoch */
bool ChronoSanityCheck();
#endif // BITCOIN_UTIL_TIME_H