LCOV - code coverage report
Current view: top level - src - timedata.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 50 51 98.0 %
Date: 2026-06-25 07:23:43 Functions: 6 6 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2014-2021 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             : #if defined(HAVE_CONFIG_H)
       6             : #include <config/bitcoin-config.h>
       7             : #endif
       8             : 
       9             : #include <timedata.h>
      10             : 
      11             : #include <netaddress.h>
      12             : #include <node/interface_ui.h>
      13             : #include <sync.h>
      14             : #include <tinyformat.h>
      15             : #include <util/system.h>
      16             : #include <util/translation.h>
      17             : #include <warnings.h>
      18             : 
      19             : static GlobalMutex g_timeoffset_mutex;
      20             : static int64_t nTimeOffset GUARDED_BY(g_timeoffset_mutex) = 0;
      21             : 
      22             : /**
      23             :  * "Never go to sea with two chronometers; take one or three."
      24             :  * Our three time sources are:
      25             :  *  - System clock
      26             :  *  - Median of other nodes clocks
      27             :  *  - The user (asking the user to fix the system clock if the first two disagree)
      28             :  */
      29     4951219 : int64_t GetTimeOffset()
      30             : {
      31     4951219 :     LOCK(g_timeoffset_mutex);
      32     4951219 :     return nTimeOffset;
      33     4951219 : }
      34             : 
      35     4947029 : int64_t GetAdjustedTime()
      36             : {
      37     4947029 :     return GetTime() + GetTimeOffset();
      38             : }
      39             : 
      40             : #define BITCOIN_TIMEDATA_MAX_SAMPLES 200
      41             : 
      42        3308 : static std::set<CNetAddr> g_sources;
      43        3308 : static CMedianFilter<int64_t> g_time_offsets{BITCOIN_TIMEDATA_MAX_SAMPLES, 0};
      44             : static bool g_warning_emitted;
      45             : 
      46        4232 : void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
      47             : {
      48        4232 :     LOCK(g_timeoffset_mutex);
      49             :     // Ignore duplicates
      50        4232 :     if (g_sources.size() == BITCOIN_TIMEDATA_MAX_SAMPLES)
      51           0 :         return;
      52        4232 :     if (!g_sources.insert(ip).second)
      53        2673 :         return;
      54             : 
      55             :     // Add data
      56        1559 :     g_time_offsets.input(nOffsetSample);
      57        1559 :     LogPrint(BCLog::NET, "added time data, samples %d, offset %+d (%+d minutes)\n", g_time_offsets.size(), nOffsetSample, nOffsetSample / 60);
      58             : 
      59             :     // There is a known issue here (see issue #4521):
      60             :     //
      61             :     // - The structure g_time_offsets contains up to 200 elements, after which
      62             :     // any new element added to it will not increase its size, replacing the
      63             :     // oldest element.
      64             :     //
      65             :     // - The condition to update nTimeOffset includes checking whether the
      66             :     // number of elements in g_time_offsets is odd, which will never happen after
      67             :     // there are 200 elements.
      68             :     //
      69             :     // But in this case the 'bug' is protective against some attacks, and may
      70             :     // actually explain why we've never seen attacks which manipulate the
      71             :     // clock offset.
      72             :     //
      73             :     // So we should hold off on fixing this and clean it up as part of
      74             :     // a timing cleanup that strengthens it in a number of other ways.
      75             :     //
      76        1559 :     if (g_time_offsets.size() >= 5 && g_time_offsets.size() % 2 == 1) {
      77          98 :         int64_t nMedian = g_time_offsets.median();
      78          98 :         std::vector<int64_t> vSorted = g_time_offsets.sorted();
      79             :         // Only let other nodes change our time by so much
      80          98 :         int64_t max_adjustment = std::max<int64_t>(0, gArgs.GetIntArg("-maxtimeadjustment", DEFAULT_MAX_TIME_ADJUSTMENT));
      81          98 :         if (nMedian >= -max_adjustment && nMedian <= max_adjustment) {
      82          96 :             nTimeOffset = nMedian;
      83          96 :         } else {
      84           2 :             nTimeOffset = 0;
      85             : 
      86           2 :             if (!g_warning_emitted) {
      87             :                 // If nobody has a time different than ours but within 5 minutes of ours, give a warning
      88           1 :                 bool fMatch = false;
      89           6 :                 for (const int64_t nOffset : vSorted) {
      90           5 :                     if (nOffset != 0 && nOffset > -5 * 60 && nOffset < 5 * 60) fMatch = true;
      91             :                 }
      92             : 
      93           1 :                 if (!fMatch) {
      94           1 :                     g_warning_emitted = true;
      95           1 :                     bilingual_str strMessage = strprintf(_("Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly."), PACKAGE_NAME);
      96           1 :                     SetMiscWarning(strMessage);
      97           1 :                     uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING);
      98           1 :                 }
      99           1 :             }
     100             :         }
     101             : 
     102          98 :         if (LogAcceptCategory(BCLog::NET, BCLog::Level::Debug)) {
     103          98 :             std::string log_message{"time data samples: "};
     104       10094 :             for (const int64_t n : vSorted) {
     105        9996 :                 log_message += strprintf("%+d  ", n);
     106             :             }
     107          98 :             log_message += strprintf("|  median offset = %+d  (%+d minutes)", nTimeOffset, nTimeOffset / 60);
     108          98 :             LogPrint(BCLog::NET, "%s\n", log_message);
     109          98 :         }
     110          98 :     }
     111        4232 : }
     112             : 
     113           3 : void TestOnlyResetTimeData()
     114             : {
     115           3 :     LOCK(g_timeoffset_mutex);
     116           3 :     nTimeOffset = 0;
     117           3 :     g_sources.clear();
     118           3 :     g_time_offsets = CMedianFilter<int64_t>{BITCOIN_TIMEDATA_MAX_SAMPLES, 0};
     119           3 :     g_warning_emitted = false;
     120           3 : }

Generated by: LCOV version 1.16