LCOV - code coverage report
Current view: top level - src - timedata.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 9 52 17.3 %
Date: 2023-09-26 12:08:55 Functions: 5 7 71.4 %

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

Generated by: LCOV version 1.14