aboutsummaryrefslogtreecommitdiff
path: root/src/timedata.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/timedata.cpp')
-rw-r--r--src/timedata.cpp119
1 files changed, 119 insertions, 0 deletions
diff --git a/src/timedata.cpp b/src/timedata.cpp
new file mode 100644
index 000000000..25fc49412
--- /dev/null
+++ b/src/timedata.cpp
@@ -0,0 +1,119 @@
+// Copyright (c) 2014-2015 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#if defined(HAVE_CONFIG_H)
+#include "config/bitcoin-config.h"
+#endif
+
+#include "timedata.h"
+
+#include "netaddress.h"
+#include "sync.h"
+#include "ui_interface.h"
+#include "util.h"
+#include "utilstrencodings.h"
+
+#include <boost/foreach.hpp>
+
+using namespace std;
+
+static CCriticalSection cs_nTimeOffset;
+static int64_t nTimeOffset = 0;
+
+/**
+ * "Never go to sea with two chronometers; take one or three."
+ * Our three time sources are:
+ * - System clock
+ * - Median of other nodes clocks
+ * - The user (asking the user to fix the system clock if the first two disagree)
+ */
+int64_t GetTimeOffset()
+{
+ LOCK(cs_nTimeOffset);
+ return nTimeOffset;
+}
+
+int64_t GetAdjustedTime()
+{
+ return GetTime() + GetTimeOffset();
+}
+
+static int64_t abs64(int64_t n)
+{
+ return (n >= 0 ? n : -n);
+}
+
+#define BITCOIN_TIMEDATA_MAX_SAMPLES 200
+
+void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
+{
+ LOCK(cs_nTimeOffset);
+ // Ignore duplicates
+ static set<CNetAddr> setKnown;
+ if (setKnown.size() == BITCOIN_TIMEDATA_MAX_SAMPLES)
+ return;
+ if (!setKnown.insert(ip).second)
+ return;
+
+ // Add data
+ static CMedianFilter<int64_t> vTimeOffsets(BITCOIN_TIMEDATA_MAX_SAMPLES, 0);
+ vTimeOffsets.input(nOffsetSample);
+ LogPrint("net","added time data, samples %d, offset %+d (%+d minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60);
+
+ // There is a known issue here (see issue #4521):
+ //
+ // - The structure vTimeOffsets contains up to 200 elements, after which
+ // any new element added to it will not increase its size, replacing the
+ // oldest element.
+ //
+ // - The condition to update nTimeOffset includes checking whether the
+ // number of elements in vTimeOffsets is odd, which will never happen after
+ // there are 200 elements.
+ //
+ // But in this case the 'bug' is protective against some attacks, and may
+ // actually explain why we've never seen attacks which manipulate the
+ // clock offset.
+ //
+ // So we should hold off on fixing this and clean it up as part of
+ // a timing cleanup that strengthens it in a number of other ways.
+ //
+ if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1)
+ {
+ int64_t nMedian = vTimeOffsets.median();
+ std::vector<int64_t> vSorted = vTimeOffsets.sorted();
+ // Only let other nodes change our time by so much
+ if (abs64(nMedian) <= std::max<int64_t>(0, GetArg("-maxtimeadjustment", DEFAULT_MAX_TIME_ADJUSTMENT)))
+ {
+ nTimeOffset = nMedian;
+ }
+ else
+ {
+ nTimeOffset = 0;
+
+ static bool fDone;
+ if (!fDone)
+ {
+ // If nobody has a time different than ours but within 5 minutes of ours, give a warning
+ bool fMatch = false;
+ BOOST_FOREACH(int64_t nOffset, vSorted)
+ if (nOffset != 0 && abs64(nOffset) < 5 * 60)
+ fMatch = true;
+
+ if (!fMatch)
+ {
+ fDone = true;
+ string 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));
+ strMiscWarning = strMessage;
+ uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING);
+ }
+ }
+ }
+
+ BOOST_FOREACH(int64_t n, vSorted)
+ LogPrint("net", "%+d ", n);
+ LogPrint("net", "| ");
+
+ LogPrint("net", "nTimeOffset = %+d (%+d minutes)\n", nTimeOffset, nTimeOffset/60);
+ }
+}