Branch data 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 : 0 : int64_t GetTimeOffset() 31 : : { 32 : 0 : LOCK(g_timeoffset_mutex); 33 : 0 : return nTimeOffset; 34 : 0 : } 35 : : 36 : 0 : NodeClock::time_point GetAdjustedTime() 37 : : { 38 : 0 : 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 : : // 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 : }