]>
Commit | Line | Data |
---|---|---|
f914f1a7 | 1 | // Copyright (c) 2014 The Bitcoin Core developers |
fa94b9d5 | 2 | // Distributed under the MIT software license, see the accompanying |
bc909a7a | 3 | // file COPYING or https://www.opensource.org/licenses/mit-license.php . |
14f888ca WL |
4 | |
5 | #include "timedata.h" | |
6 | ||
7 | #include "netbase.h" | |
8 | #include "sync.h" | |
9 | #include "ui_interface.h" | |
10 | #include "util.h" | |
ad49c256 | 11 | #include "utilstrencodings.h" |
14f888ca WL |
12 | |
13 | #include <boost/foreach.hpp> | |
14 | ||
15 | using namespace std; | |
16 | ||
17 | static CCriticalSection cs_nTimeOffset; | |
18 | static int64_t nTimeOffset = 0; | |
19 | ||
fa94b9d5 MF |
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 | */ | |
14f888ca WL |
27 | int64_t GetTimeOffset() |
28 | { | |
29 | LOCK(cs_nTimeOffset); | |
30 | return nTimeOffset; | |
31 | } | |
32 | ||
33 | int64_t GetAdjustedTime() | |
34 | { | |
35 | return GetTime() + GetTimeOffset(); | |
36 | } | |
37 | ||
ad49c256 WL |
38 | static int64_t abs64(int64_t n) |
39 | { | |
40 | return (n >= 0 ? n : -n); | |
41 | } | |
42 | ||
649f5d9c PJ |
43 | #define BITCOIN_TIMEDATA_MAX_SAMPLES 200 |
44 | ||
26a6bae7 | 45 | void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample) |
14f888ca | 46 | { |
14f888ca WL |
47 | LOCK(cs_nTimeOffset); |
48 | // Ignore duplicates | |
49 | static set<CNetAddr> setKnown; | |
649f5d9c PJ |
50 | if (setKnown.size() == BITCOIN_TIMEDATA_MAX_SAMPLES) |
51 | return; | |
14f888ca WL |
52 | if (!setKnown.insert(ip).second) |
53 | return; | |
54 | ||
55 | // Add data | |
649f5d9c | 56 | static CMedianFilter<int64_t> vTimeOffsets(BITCOIN_TIMEDATA_MAX_SAMPLES, 0); |
14f888ca WL |
57 | vTimeOffsets.input(nOffsetSample); |
58 | LogPrintf("Added time data, samples %d, offset %+d (%+d minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60); | |
93659379 WL |
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 | // | |
14f888ca WL |
77 | if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1) |
78 | { | |
79 | 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 | if (abs64(nMedian) < 70 * 60) | |
83 | { | |
84 | nTimeOffset = nMedian; | |
85 | } | |
86 | else | |
87 | { | |
88 | nTimeOffset = 0; | |
89 | ||
90 | static bool fDone; | |
91 | if (!fDone) | |
92 | { | |
93 | // If nobody has a time different than ours but within 5 minutes of ours, give a warning | |
94 | bool fMatch = false; | |
95 | BOOST_FOREACH(int64_t nOffset, vSorted) | |
96 | if (nOffset != 0 && abs64(nOffset) < 5 * 60) | |
97 | fMatch = true; | |
98 | ||
99 | if (!fMatch) | |
100 | { | |
101 | fDone = true; | |
8dafb9c6 | 102 | string strMessage = _("Warning: Please check that your computer's date and time are correct! If your clock is wrong Zcash will not work properly."); |
14f888ca WL |
103 | strMiscWarning = strMessage; |
104 | LogPrintf("*** %s\n", strMessage); | |
105 | uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING); | |
106 | } | |
107 | } | |
108 | } | |
109 | if (fDebug) { | |
110 | BOOST_FOREACH(int64_t n, vSorted) | |
111 | LogPrintf("%+d ", n); | |
112 | LogPrintf("| "); | |
113 | } | |
114 | LogPrintf("nTimeOffset = %+d (%+d minutes)\n", nTimeOffset, nTimeOffset/60); | |
115 | } | |
116 | } |