timedata.cpp
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 int64_t GetTimeOffset() 31 { 32 LOCK(g_timeoffset_mutex); 33 return nTimeOffset; 34 } 35 36 #define BITCOIN_TIMEDATA_MAX_SAMPLES 200 37 38 static std::set<CNetAddr> g_sources; 39 static CMedianFilter<int64_t> g_time_offsets{BITCOIN_TIMEDATA_MAX_SAMPLES, 0}; 40 static bool g_warning_emitted; 41 42 void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample) 43 { 44 LOCK(g_timeoffset_mutex); 45 // Ignore duplicates 46 if (g_sources.size() == BITCOIN_TIMEDATA_MAX_SAMPLES) 47 return; 48 if (!g_sources.insert(ip).second) 49 return; 50 51 // Add data 52 g_time_offsets.input(nOffsetSample); 53 LogPrint(BCLog::NET, "added time data, samples %d, offset %+d (%+d minutes)\n", g_time_offsets.size(), nOffsetSample, nOffsetSample / 60); 54 55 // There is a known issue here (see issue #4521): 56 // 57 // - The structure g_time_offsets contains up to 200 elements, after which 58 // any new element added to it will not increase its size, replacing the 59 // oldest element. 60 // 61 // - The condition to update nTimeOffset includes checking whether the 62 // number of elements in g_time_offsets is odd, which will never happen after 63 // there are 200 elements. 64 // 65 // But in this case the 'bug' is protective against some attacks, and may 66 // actually explain why we've never seen attacks which manipulate the 67 // clock offset. 68 // 69 // So we should hold off on fixing this and clean it up as part of 70 // a timing cleanup that strengthens it in a number of other ways. 71 // 72 if (g_time_offsets.size() >= 5 && g_time_offsets.size() % 2 == 1) { 73 int64_t nMedian = g_time_offsets.median(); 74 std::vector<int64_t> vSorted = g_time_offsets.sorted(); 75 // Only let other nodes change our time by so much 76 int64_t max_adjustment = std::max<int64_t>(0, gArgs.GetIntArg("-maxtimeadjustment", DEFAULT_MAX_TIME_ADJUSTMENT)); 77 if (nMedian >= -max_adjustment && nMedian <= max_adjustment) { 78 nTimeOffset = nMedian; 79 } else { 80 nTimeOffset = 0; 81 82 if (!g_warning_emitted) { 83 // If nobody has a time different than ours but within 5 minutes of ours, give a warning 84 bool fMatch = false; 85 for (const int64_t nOffset : vSorted) { 86 if (nOffset != 0 && nOffset > -5 * 60 && nOffset < 5 * 60) fMatch = true; 87 } 88 89 if (!fMatch) { 90 g_warning_emitted = true; 91 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); 92 SetMiscWarning(strMessage); 93 uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING); 94 } 95 } 96 } 97 98 if (LogAcceptCategory(BCLog::NET, BCLog::Level::Debug)) { 99 std::string log_message{"time data samples: "}; 100 for (const int64_t n : vSorted) { 101 log_message += strprintf("%+d ", n); 102 } 103 log_message += strprintf("| median offset = %+d (%+d minutes)", nTimeOffset, nTimeOffset / 60); 104 LogPrint(BCLog::NET, "%s\n", log_message); 105 } 106 } 107 } 108 109 void TestOnlyResetTimeData() 110 { 111 LOCK(g_timeoffset_mutex); 112 nTimeOffset = 0; 113 g_sources.clear(); 114 g_time_offsets = CMedianFilter<int64_t>{BITCOIN_TIMEDATA_MAX_SAMPLES, 0}; 115 g_warning_emitted = false; 116 }