Line data Source code
1 : // Copyright (c) 2014 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 "timedata.h"
6 :
7 : #include "netbase.h"
8 : #include "sync.h"
9 : #include "ui_interface.h"
10 : #include "util.h"
11 : #include "utilstrencodings.h"
12 :
13 : #include <boost/foreach.hpp>
14 :
15 : using namespace std;
16 :
17 96 : static CCriticalSection cs_nTimeOffset;
18 : static int64_t nTimeOffset = 0;
19 :
20 : /**
21 : * "Never go to sea with two chronometers; take one or three."
22 : * Our three time sources are:
23 : * - System clock
24 : * - Median of other nodes clocks
25 : * - The user (asking the user to fix the system clock if the first two disagree)
26 : */
27 99888 : int64_t GetTimeOffset()
28 : {
29 99888 : LOCK(cs_nTimeOffset);
30 199776 : return nTimeOffset;
31 : }
32 :
33 99884 : int64_t GetAdjustedTime()
34 : {
35 99884 : return GetTime() + GetTimeOffset();
36 : }
37 :
38 : static int64_t abs64(int64_t n)
39 : {
40 0 : return (n >= 0 ? n : -n);
41 : }
42 :
43 : #define BITCOIN_TIMEDATA_MAX_SAMPLES 200
44 :
45 250 : void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
46 : {
47 250 : LOCK(cs_nTimeOffset);
48 : // Ignore duplicates
49 335 : static set<CNetAddr> setKnown;
50 250 : if (setKnown.size() == BITCOIN_TIMEDATA_MAX_SAMPLES)
51 : return;
52 250 : if (!setKnown.insert(ip).second)
53 : return;
54 :
55 : // Add data
56 85 : static CMedianFilter<int64_t> vTimeOffsets(BITCOIN_TIMEDATA_MAX_SAMPLES, 0);
57 85 : vTimeOffsets.input(nOffsetSample);
58 170 : LogPrintf("Added time data, samples %d, offset %+d (%+d minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60);
59 :
60 : // There is a known issue here (see issue #4521):
61 : //
62 : // - The structure vTimeOffsets 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 vTimeOffsets 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 85 : if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1)
78 : {
79 0 : int64_t nMedian = vTimeOffsets.median();
80 : std::vector<int64_t> vSorted = vTimeOffsets.sorted();
81 : // Only let other nodes change our time by so much
82 0 : if (abs64(nMedian) < 70 * 60)
83 : {
84 0 : nTimeOffset = nMedian;
85 : }
86 : else
87 : {
88 0 : nTimeOffset = 0;
89 :
90 : static bool fDone;
91 0 : if (!fDone)
92 : {
93 : // If nobody has a time different than ours but within 5 minutes of ours, give a warning
94 0 : bool fMatch = false;
95 0 : BOOST_FOREACH(int64_t nOffset, vSorted)
96 0 : if (nOffset != 0 && abs64(nOffset) < 5 * 60)
97 : fMatch = true;
98 :
99 0 : if (!fMatch)
100 : {
101 0 : fDone = true;
102 0 : string strMessage = _("Please check that your computer's date and time are correct! If your clock is wrong Bitcoin Core will not work properly.");
103 : strMiscWarning = strMessage;
104 0 : uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING);
105 : }
106 : }
107 : }
108 0 : if (fDebug) {
109 0 : BOOST_FOREACH(int64_t n, vSorted)
110 0 : LogPrintf("%+d ", n);
111 0 : LogPrintf("| ");
112 : }
113 0 : LogPrintf("nTimeOffset = %+d (%+d minutes)\n", nTimeOffset, nTimeOffset/60);
114 : }
115 288 : }
|