Merge pull request #379 from svost/c++11
[novacoin.git] / src / timedata.cpp
diff --git a/src/timedata.cpp b/src/timedata.cpp
new file mode 100644 (file)
index 0000000..ae258e9
--- /dev/null
@@ -0,0 +1,107 @@
+#include "timedata.h"
+#include "netbase.h"
+#include "sync.h"
+#include "ui_interface.h"
+
+using namespace std;
+
+static CCriticalSection cs_nTimeOffset;
+static uint32_t NOVACOIN_TIMEDATA_MAX_SAMPLES = 200;
+
+// Trusted NTP offset or median of NTP samples.
+extern int64_t nNtpOffset;
+
+// Median of time samples given by other nodes.
+static int64_t nNodesOffset = numeric_limits<int64_t>::max();
+
+//
+// "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)
+//
+
+// Select time offset:
+int64_t GetTimeOffset()
+{
+    LOCK(cs_nTimeOffset);
+    // If NTP and system clock are in agreement within 40 minutes, then use NTP.
+    if (abs(nNtpOffset) < 40 * 60)
+        return nNtpOffset;
+
+    // If not, then choose between median peer time and system clock.
+    if (abs(nNodesOffset) < 70 * 60)
+        return nNodesOffset;
+
+    return 0;
+}
+
+int64_t GetNodesOffset()
+{
+    return nNodesOffset;
+}
+
+int64_t GetAdjustedTime()
+{
+    return GetTime() + GetTimeOffset();
+}
+
+void AddTimeData(const CNetAddr& ip, int64_t nTime)
+{
+    int64_t nOffsetSample = nTime - GetTime();
+
+    LOCK(cs_nTimeOffset);
+    // Ignore duplicates
+    static set<CNetAddr> setKnown;
+    if (setKnown.size() == NOVACOIN_TIMEDATA_MAX_SAMPLES)
+        return;
+    if (!setKnown.insert(ip).second)
+        return;
+
+    // Add data
+    static CMedianFilter<int64_t> vTimeOffsets(NOVACOIN_TIMEDATA_MAX_SAMPLES,0);
+    vTimeOffsets.input(nOffsetSample);
+    printf("Added time data, samples %d, offset %+" PRId64 " (%+" PRId64 " minutes)\n", vTimeOffsets.size(), nOffsetSample, nOffsetSample/60);
+    if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1)
+    {
+        auto nMedian = vTimeOffsets.median();
+        auto vSorted = vTimeOffsets.sorted();
+        // Only let other nodes change our time by so much
+        if (abs(nMedian) < 70 * 60)
+        {
+            nNodesOffset = nMedian;
+        }
+        else
+        {
+            nNodesOffset = numeric_limits<int64_t>::max();
+
+            static bool fDone;
+            if (!fDone)
+            {
+                bool fMatch = false;
+
+                // If nobody has a time different than ours but within 5 minutes of ours, give a warning
+                for(auto nOffset :  vSorted)
+                    if (nOffset != 0 && abs(nOffset) < 5 * 60)
+                        fMatch = true;
+
+                if (!fMatch)
+                {
+                    fDone = true;
+                    string strMessage("Warning: Please check that your computer's date and time are correct! If your clock is wrong NovaCoin will not work properly.");
+                    strMiscWarning = strMessage;
+                    printf("*** %s\n", strMessage.c_str());
+                    uiInterface.ThreadSafeMessageBox(strMessage+" ", string("NovaCoin"), CClientUIInterface::OK | CClientUIInterface::ICON_EXCLAMATION);
+                }
+            }
+        }
+        if (fDebug) {
+            for(int64_t n :  vSorted)
+                printf("%+" PRId64 "  ", n);
+            printf("|  ");
+        }
+        if (nNodesOffset != numeric_limits<int64_t>::max())
+            printf("nNodesOffset = %+" PRId64 "  (%+" PRId64 " minutes)\n", nNodesOffset, nNodesOffset/60);
+    }
+}