// Copyright (c) 2014 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
# include "timedata.h"
# include "netbase.h"
# include "sync.h"
# include "ui_interface.h"
# include "util.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 ( ) ;
}
void AddTimeData ( const CNetAddr & ip , int64_t nTime )
{
int64_t nOffsetSample = nTime - GetTime ( ) ;
LOCK ( cs_nTimeOffset ) ;
// Ignore duplicates
static set < CNetAddr > setKnown ;
if ( ! setKnown . insert ( ip ) . second )
return ;
// Add data
static CMedianFilter < int64_t > vTimeOffsets ( 200 , 0 ) ;
vTimeOffsets . input ( nOffsetSample ) ;
LogPrintf ( " 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 ) < 70 * 60 )
{
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 = _ ( " Warning: Please check that your computer's date and time are correct! If your clock is wrong Bitcoin Core will not work properly. " ) ;
strMiscWarning = strMessage ;
LogPrintf ( " *** %s \n " , strMessage ) ;
uiInterface . ThreadSafeMessageBox ( strMessage , " " , CClientUIInterface : : MSG_WARNING ) ;
}
}
}
if ( fDebug ) {
BOOST_FOREACH ( int64_t n , vSorted )
LogPrintf ( " %+d " , n ) ;
LogPrintf ( " | " ) ;
}
LogPrintf ( " nTimeOffset = %+d (%+d minutes) \n " , nTimeOffset , nTimeOffset / 60 ) ;
}
}