2023-04-25 13:51:26 +02:00
// Copyright (c) 2014-2020 The Bitcoin Core developers
2014-10-31 01:43:19 +01:00
// Distributed under the MIT software license, see the accompanying
2014-06-19 15:08:37 +02:00
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2016-02-04 13:41:58 +01:00
# if defined(HAVE_CONFIG_H)
2022-08-02 18:34:58 +02:00
# include <config/bitcoin-config.h>
2016-02-04 13:41:58 +01:00
# endif
2020-03-19 23:46:56 +01:00
# include <timedata.h>
2014-06-19 15:08:37 +02:00
2020-03-19 23:46:56 +01:00
# include <netaddress.h>
2020-07-01 15:18:55 +02:00
# include <node/ui_interface.h>
2020-03-19 23:46:56 +01:00
# include <sync.h>
2021-09-23 16:55:37 +02:00
# include <tinyformat.h>
2021-06-27 08:33:13 +02:00
# include <util/system.h>
2022-03-24 05:13:51 +01:00
# include <util/translation.h>
2020-03-19 23:46:56 +01:00
# include <warnings.h>
2014-06-19 15:08:37 +02:00
2020-06-08 13:03:36 +02:00
static Mutex g_timeoffset_mutex ;
static int64_t nTimeOffset GUARDED_BY ( g_timeoffset_mutex ) = 0 ;
2014-06-19 15:08:37 +02:00
2014-10-31 01:43:19 +01:00
/**
* " 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 )
*/
2014-06-19 15:08:37 +02:00
int64_t GetTimeOffset ( )
{
2020-06-08 13:03:36 +02:00
LOCK ( g_timeoffset_mutex ) ;
2014-06-19 15:08:37 +02:00
return nTimeOffset ;
}
int64_t GetAdjustedTime ( )
{
return GetTime ( ) + GetTimeOffset ( ) ;
}
2015-08-11 15:57:52 +02:00
# define BITCOIN_TIMEDATA_MAX_SAMPLES 200
2014-06-19 15:08:37 +02:00
2014-12-15 11:06:15 +01:00
void AddTimeData ( const CNetAddr & ip , int64_t nOffsetSample )
2014-06-19 15:08:37 +02:00
{
2020-06-08 13:03:36 +02:00
LOCK ( g_timeoffset_mutex ) ;
2014-06-19 15:08:37 +02:00
// Ignore duplicates
2017-01-30 13:13:07 +01:00
static std : : set < CNetAddr > setKnown ;
2015-08-11 15:57:52 +02:00
if ( setKnown . size ( ) = = BITCOIN_TIMEDATA_MAX_SAMPLES )
return ;
2014-06-19 15:08:37 +02:00
if ( ! setKnown . insert ( ip ) . second )
return ;
// Add data
2015-08-11 15:57:52 +02:00
static CMedianFilter < int64_t > vTimeOffsets ( BITCOIN_TIMEDATA_MAX_SAMPLES , 0 ) ;
2014-06-19 15:08:37 +02:00
vTimeOffsets . input ( nOffsetSample ) ;
2020-06-08 13:03:36 +02:00
LogPrint ( BCLog : : NET , " added time data, samples %d, offset %+d (%+d minutes) \n " , vTimeOffsets . size ( ) , nOffsetSample , nOffsetSample / 60 ) ;
2014-07-24 19:00:24 +02:00
// 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.
//
2020-06-08 13:03:36 +02:00
if ( vTimeOffsets . size ( ) > = 5 & & vTimeOffsets . size ( ) % 2 = = 1 ) {
2014-06-19 15:08:37 +02:00
int64_t nMedian = vTimeOffsets . median ( ) ;
std : : vector < int64_t > vSorted = vTimeOffsets . sorted ( ) ;
// Only let other nodes change our time by so much
2020-10-13 13:04:10 +02:00
int64_t max_adjustment = std : : max < int64_t > ( 0 , gArgs . GetArg ( " -maxtimeadjustment " , DEFAULT_MAX_TIME_ADJUSTMENT ) ) ;
if ( nMedian > = - max_adjustment & & nMedian < = max_adjustment ) {
2014-06-19 15:08:37 +02:00
nTimeOffset = nMedian ;
2020-06-08 13:03:36 +02:00
} else {
2014-06-19 15:08:37 +02:00
nTimeOffset = 0 ;
static bool fDone ;
2020-06-08 13:03:36 +02:00
if ( ! fDone ) {
2014-06-19 15:08:37 +02:00
// If nobody has a time different than ours but within 5 minutes of ours, give a warning
bool fMatch = false ;
2020-06-08 13:03:36 +02:00
for ( const int64_t nOffset : vSorted ) {
2020-10-13 13:04:10 +02:00
if ( nOffset ! = 0 & & nOffset > - 5 * 60 & & nOffset < 5 * 60 ) fMatch = true ;
2020-06-08 13:03:36 +02:00
}
2014-06-19 15:08:37 +02:00
2020-06-08 13:03:36 +02:00
if ( ! fMatch ) {
2014-06-19 15:08:37 +02:00
fDone = true ;
2020-05-08 18:17:47 +02:00
bilingual_str 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 ) ;
2020-06-10 15:50:50 +02:00
SetMiscWarning ( strMessage ) ;
2014-06-19 15:08:37 +02:00
uiInterface . ThreadSafeMessageBox ( strMessage , " " , CClientUIInterface : : MSG_WARNING ) ;
}
}
}
2019-05-22 23:51:39 +02:00
if ( LogAcceptCategory ( BCLog : : NET ) ) {
2021-09-23 16:55:37 +02:00
std : : string log_message { " time data samples: " } ;
2018-09-04 15:36:09 +02:00
for ( const int64_t n : vSorted ) {
2021-09-23 16:55:37 +02:00
log_message + = strprintf ( " %+d " , n ) ;
2019-05-22 23:51:39 +02:00
}
2021-09-23 16:55:37 +02:00
log_message + = strprintf ( " | median offset = %+d (%+d minutes) " , nTimeOffset , nTimeOffset / 60 ) ;
LogPrint ( BCLog : : NET , " %s \n " , log_message ) ;
2019-05-22 23:51:39 +02:00
}
2014-06-19 15:08:37 +02:00
}
}