timeoffsets.cpp raw

   1  // Copyright (c) 2024-present The Bitcoin Core developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #include <node/timeoffsets.h>
   6  #include <node/warnings.h>
   7  #include <sync.h>
   8  #include <tinyformat.h>
   9  #include <util/log.h>
  10  #include <util/time.h>
  11  #include <util/translation.h>
  12  
  13  #include <algorithm>
  14  #include <chrono>
  15  #include <cstdint>
  16  #include <deque>
  17  #include <limits>
  18  #include <optional>
  19  
  20  using namespace std::chrono_literals;
  21  
  22  void TimeOffsets::Add(std::chrono::seconds offset)
  23  {
  24      LOCK(m_mutex);
  25  
  26      if (m_offsets.size() >= MAX_SIZE) {
  27          m_offsets.pop_front();
  28      }
  29      m_offsets.push_back(offset);
  30      LogDebug(BCLog::NET, "Added time offset %+ds, total samples %d\n",
  31               Ticks<std::chrono::seconds>(offset), m_offsets.size());
  32  }
  33  
  34  std::chrono::seconds TimeOffsets::Median() const
  35  {
  36      LOCK(m_mutex);
  37  
  38      // Only calculate the median if we have 5 or more offsets
  39      if (m_offsets.size() < 5) return 0s;
  40  
  41      auto sorted_copy = m_offsets;
  42      std::sort(sorted_copy.begin(), sorted_copy.end());
  43      return sorted_copy[sorted_copy.size() / 2];  // approximate median is good enough, keep it simple
  44  }
  45  
  46  bool TimeOffsets::WarnIfOutOfSync() const
  47  {
  48      // when median == std::numeric_limits<int64_t>::min(), calling std::chrono::abs is UB
  49      auto median{std::max(Median(), std::chrono::seconds(std::numeric_limits<int64_t>::min() + 1))};
  50      if (std::chrono::abs(median) <= WARN_THRESHOLD) {
  51          m_warnings.Unset(node::Warning::CLOCK_OUT_OF_SYNC);
  52          return false;
  53      }
  54  
  55      bilingual_str msg{strprintf(_(
  56          "Your computer's date and time appear to be more than %d minutes out of sync with the network, "
  57          "this may lead to consensus failure. After you've confirmed your computer's clock, this message "
  58          "should no longer appear when you restart your node. Without a restart, it should stop showing "
  59          "automatically after you've connected to a sufficient number of new outbound peers, which may "
  60          "take some time. You can inspect the `timeoffset` field of the `getpeerinfo` and `getnetworkinfo` "
  61          "RPC methods to get more info."
  62      ), Ticks<std::chrono::minutes>(WARN_THRESHOLD))};
  63      LogWarning("%s\n", msg.original);
  64      m_warnings.Set(node::Warning::CLOCK_OUT_OF_SYNC, msg);
  65      return true;
  66  }
  67