Add TimeKeeper:

This class tracks Ripple network time and closing time.

Conflicts:
	src/ripple/ledger/ReadView.h
This commit is contained in:
Vinnie Falco
2015-07-15 10:08:47 -07:00
parent e82d774d32
commit b38a96ae82
19 changed files with 473 additions and 226 deletions

View File

@@ -2071,13 +2071,13 @@
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<ClCompile Include="..\..\src\ripple\core\impl\SNTPClient.cpp">
<ClCompile Include="..\..\src\ripple\core\impl\SNTPClock.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<ClInclude Include="..\..\src\ripple\core\impl\SNTPClient.h">
<ClInclude Include="..\..\src\ripple\core\impl\SNTPClock.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\core\impl\SociDB.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
@@ -2085,6 +2085,12 @@
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<ClCompile Include="..\..\src\ripple\core\impl\TimeKeeper.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<ClInclude Include="..\..\src\ripple\core\Job.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\core\JobQueue.h">
@@ -2121,6 +2127,8 @@
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">..\..\src\soci\src\core;..\..\src\sqlite;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<ClInclude Include="..\..\src\ripple\core\TimeKeeper.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\crypto\Base58.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\crypto\Base58Data.h">

View File

@@ -2805,15 +2805,18 @@
<ClCompile Include="..\..\src\ripple\core\impl\LoadMonitor.cpp">
<Filter>ripple\core\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\core\impl\SNTPClient.cpp">
<ClCompile Include="..\..\src\ripple\core\impl\SNTPClock.cpp">
<Filter>ripple\core\impl</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\core\impl\SNTPClient.h">
<ClInclude Include="..\..\src\ripple\core\impl\SNTPClock.h">
<Filter>ripple\core\impl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\core\impl\SociDB.cpp">
<Filter>ripple\core\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\core\impl\TimeKeeper.cpp">
<Filter>ripple\core\impl</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\core\Job.h">
<Filter>ripple\core</Filter>
</ClInclude>
@@ -2850,6 +2853,9 @@
<ClCompile Include="..\..\src\ripple\core\tests\SociDB.test.cpp">
<Filter>ripple\core\tests</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\core\TimeKeeper.h">
<Filter>ripple\core</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\crypto\Base58.h">
<Filter>ripple\crypto</Filter>
</ClInclude>

View File

@@ -39,6 +39,7 @@
#include <ripple/core/LoadFeeTrack.h>
#include <ripple/core/JobQueue.h>
#include <ripple/core/SociDB.h>
#include <ripple/core/TimeKeeper.h>
#include <ripple/json/to_string.h>
#include <ripple/nodestore/Database.h>
#include <ripple/protocol/digest.h>
@@ -223,7 +224,7 @@ Ledger::Ledger (open_ledger_t, Ledger const& prevLedger)
// VFALCO Remove this call to getApp
if (prevLedger.info_.closeTime == 0)
info_.closeTime = roundCloseTime (
getApp().getOPs ().getCloseTimeNC (), info_.closeTimeResolution);
getApp().timeKeeper().closeTime().time_since_epoch().count(), info_.closeTimeResolution);
else
info_.closeTime =
prevLedger.info_.closeTime + info_.closeTimeResolution;

View File

@@ -38,6 +38,7 @@
#include <ripple/core/Config.h>
#include <ripple/core/JobQueue.h>
#include <ripple/core/LoadFeeTrack.h>
#include <ripple/core/TimeKeeper.h>
#include <ripple/json/to_string.h>
#include <ripple/overlay/Overlay.h>
#include <ripple/overlay/predicates.h>
@@ -721,7 +722,7 @@ void LedgerConsensusImp::statePreClose ()
if (mHaveCorrectLCL && getCloseAgree(mPreviousLedger->info()))
{
// we can use consensus timing
sinceClose = 1000 * (getApp().getOPs ().getCloseTimeNC ()
sinceClose = 1000 * (getApp().timeKeeper().closeTime().time_since_epoch().count()
- mPreviousLedger->info().closeTime);
idleInterval = 2 * mPreviousLedger->info().closeTimeResolution;
@@ -731,7 +732,7 @@ void LedgerConsensusImp::statePreClose ()
else
{
// Use the time we saw the last ledger close
sinceClose = 1000 * (getApp().getOPs ().getCloseTimeNC ()
sinceClose = 1000 * (getApp().timeKeeper().closeTime().time_since_epoch().count()
- consensus_.getLastCloseTime ());
idleInterval = LEDGER_IDLE_INTERVAL;
}
@@ -1041,7 +1042,7 @@ void LedgerConsensusImp::accept (std::shared_ptr<SHAMap> set)
{
// Build validation
auto v = std::make_shared<STValidation> (newLCLHash,
consensus_.validationTimestamp (getApp().getOPs ().getNetworkTimeNC ()),
consensus_.validationTimestamp (getApp().timeKeeper().now().time_since_epoch().count()),
mValPublic, mProposing);
v->setFieldU32 (sfLedgerSequence, newLCL->info().seq);
addLoad(v); // Our network load
@@ -1195,7 +1196,8 @@ void LedgerConsensusImp::accept (std::shared_ptr<SHAMap> set)
WriteLog (lsINFO, LedgerConsensus)
<< "Our close offset is estimated at "
<< offset << " (" << closeCount << ")";
getApp().getOPs ().closeTimeOffset (offset);
getApp().timeKeeper().adjustCloseTime(
std::chrono::seconds(offset));
}
}
@@ -1280,7 +1282,7 @@ void LedgerConsensusImp::addDisputedTransaction (
protocol::TMTransaction msg;
msg.set_rawtransaction (& (tx.front ()), tx.size ());
msg.set_status (protocol::tsNEW);
msg.set_receivetimestamp (getApp().getOPs ().getNetworkTimeNC ());
msg.set_receivetimestamp (getApp().timeKeeper().now().time_since_epoch().count());
getApp ().overlay ().foreach (send_always (
std::make_shared<Message> (
msg, protocol::mtTRANSACTION)));
@@ -1355,7 +1357,7 @@ void LedgerConsensusImp::statusChange (protocol::NodeEvent event, Ledger& ledger
s.set_newevent (event);
s.set_ledgerseq (ledger.info().seq);
s.set_networktime (getApp().getOPs ().getNetworkTimeNC ());
s.set_networktime (getApp().timeKeeper().now().time_since_epoch().count());
s.set_ledgerhashprevious(ledger.info().parentHash.begin (),
std::decay_t<decltype(ledger.info().parentHash)>::bytes);
s.set_ledgerhash (ledger.getHash ().begin (),
@@ -1650,7 +1652,7 @@ void LedgerConsensusImp::closeLedger ()
checkOurValidation ();
state_ = State::establish;
mConsensusStartTime = std::chrono::steady_clock::now ();
mCloseTime = getApp().getOPs ().getCloseTimeNC ();
mCloseTime = getApp().timeKeeper().closeTime().time_since_epoch().count();
consensus_.setLastCloseTime (mCloseTime);
statusChange (protocol::neCLOSING_LEDGER, *mPreviousLedger);
ledgerMaster_.applyHeldTransactions ();
@@ -1682,7 +1684,7 @@ void LedgerConsensusImp::checkOurValidation ()
}
auto v = std::make_shared<STValidation> (mPreviousLedger->getHash (),
consensus_.validationTimestamp (getApp().getOPs ().getNetworkTimeNC ()),
consensus_.validationTimestamp (getApp().timeKeeper().now().time_since_epoch().count()),
mValPublic, false);
addLoad(v);
v->setTrusted ();

View File

@@ -39,6 +39,7 @@
#include <ripple/basics/TaggedCache.h>
#include <ripple/basics/UptimeTimer.h>
#include <ripple/core/LoadFeeTrack.h>
#include <ripple/core/TimeKeeper.h>
#include <ripple/overlay/Overlay.h>
#include <ripple/overlay/Peer.h>
#include <ripple/protocol/digest.h>
@@ -172,7 +173,8 @@ public:
return 999999;
}
std::int64_t ret = getApp().getOPs().getCloseTimeNC();
// VFALCO int widening?
std::int64_t ret = getApp().timeKeeper().closeTime().time_since_epoch().count();
ret -= static_cast<std::int64_t> (pubClose);
ret = (ret > 0) ? ret : 0;
@@ -189,7 +191,7 @@ public:
return 999999;
}
std::int64_t ret = getApp().getOPs().getCloseTimeNC();
std::int64_t ret = getApp().timeKeeper().closeTime().time_since_epoch().count();
ret -= static_cast<std::int64_t> (valClose);
ret = (ret > 0) ? ret : 0;
@@ -1170,7 +1172,7 @@ public:
if (!standalone_)
{ // don't pathfind with a ledger that's more than 60 seconds old
std::int64_t age = getApp().getOPs().getCloseTimeNC();
std::int64_t age = getApp().timeKeeper().closeTime().time_since_epoch().count();
age -= static_cast<std::int64_t> (lastLedger->info().closeTime);
if (age > 60)
{

View File

@@ -50,10 +50,10 @@
#include <ripple/basics/chrono.h>
#include <ripple/json/json_reader.h>
#include <ripple/json/to_string.h>
#include <ripple/core/LoadFeeTrack.h>
#include <ripple/core/ConfigSections.h>
#include <ripple/core/LoadFeeTrack.h>
#include <ripple/core/TimeKeeper.h>
#include <ripple/ledger/CachedSLEs.h>
#include <ripple/core/impl/SNTPClient.h>
#include <ripple/nodestore/Database.h>
#include <ripple/nodestore/DummyScheduler.h>
#include <ripple/nodestore/Manager.h>
@@ -259,6 +259,8 @@ public:
beast::Journal m_journal;
Application::MutexType m_masterMutex;
std::unique_ptr<TimeKeeper> timeKeeper_;
// Required by the SHAMapStore
TransactionMaster m_txMaster;
@@ -289,7 +291,6 @@ public:
std::unique_ptr <NetworkOPs> m_networkOPs;
std::unique_ptr <UniqueNodeList> m_deprecatedUNL;
std::unique_ptr <ServerHandler> serverHandler_;
std::unique_ptr <SNTPClient> m_sntpClient;
std::unique_ptr <Validators::Manager> m_validators;
std::unique_ptr <AmendmentTable> m_amendmentTable;
std::unique_ptr <LoadFeeTrack> mFeeTrack;
@@ -333,6 +334,9 @@ public:
, m_journal (m_logs.journal("Application"))
, timeKeeper_ (make_TimeKeeper(
deprecatedLogs().journal("TimeKeeper")))
, m_nodeStoreScheduler (*this)
, m_shaMapStore (make_SHAMapStore (setup_SHAMapStore (
@@ -403,9 +407,6 @@ public:
, serverHandler_ (make_ServerHandler (*m_networkOPs, get_io_service (),
*m_jobQueue, *m_networkOPs, *m_resourceManager, *m_collectorManager))
, m_sntpClient (make_SNTPClient(
deprecatedLogs().journal("SNTPClient")))
, m_validators (Validators::make_Manager(*this, get_io_service(),
m_logs.journal("UVL"), getConfig ()))
@@ -470,6 +471,12 @@ public:
return family_;
}
TimeKeeper&
timeKeeper()
{
return *timeKeeper_;
}
JobQueue& getJobQueue ()
{
return *m_jobQueue;
@@ -625,10 +632,6 @@ public:
{
return mTxnDB != nullptr;
}
bool getSystemTimeOffset (int& offset)
{
return m_sntpClient->getOffset (offset);
}
DatabaseCon& getTxnDB ()
{
@@ -724,7 +727,7 @@ public:
}
if (!getConfig ().RUN_STANDALONE)
m_sntpClient->init (getConfig ().SNTP_SERVERS);
timeKeeper_->run(getConfig ().SNTP_SERVERS);
if (!initSqliteDbs ())
{
@@ -1139,7 +1142,7 @@ bool ApplicationImp::loadOldLedger (
std::uint32_t seq = 1;
std::uint32_t closeTime = getApp().getOPs().getCloseTimeNC ();
auto closeTime = getApp().timeKeeper().closeTime().time_since_epoch().count();
std::uint32_t closeTimeResolution = 30;
bool closeTimeEstimated = false;
std::uint64_t totalDrops = 0;

View File

@@ -60,6 +60,7 @@ class PathRequests;
class PendingSaves;
class AccountIDCache;
class STLedgerEntry;
class TimeKeeper;
class TransactionMaster;
class Validations;
@@ -93,6 +94,7 @@ public:
virtual boost::asio::io_service& getIOService () = 0;
virtual CollectorManager& getCollectorManager () = 0;
virtual shamap::Family& family() = 0;
virtual TimeKeeper& timeKeeper() = 0;
virtual JobQueue& getJobQueue () = 0;
virtual NodeCache& getTempNodeCache () = 0;
virtual CachedSLEs& cachedSLEs() = 0;
@@ -132,7 +134,6 @@ public:
//
virtual DatabaseCon& getWalletDB () = 0;
virtual bool getSystemTimeOffset (int& offset) = 0;
virtual bool isShutdown () = 0;
virtual bool running () = 0;
virtual void setup () = 0;

View File

@@ -48,6 +48,7 @@
#include <ripple/protocol/JsonFields.h>
#include <ripple/core/Config.h>
#include <ripple/core/LoadFeeTrack.h>
#include <ripple/core/TimeKeeper.h>
#include <ripple/crypto/RandomNumbers.h>
#include <ripple/crypto/RFC1751.h>
#include <ripple/json/to_string.h>
@@ -128,7 +129,6 @@ public:
, m_clusterTimer (this)
, mConsensus (make_Consensus ())
, m_ledgerMaster (ledgerMaster)
, mCloseTimeOffset (0)
, mLastLoadBase (256)
, mLastLoadFactor (256)
, m_job_queue (job_queue)
@@ -139,21 +139,7 @@ public:
~NetworkOPsImp() override = default;
// Network information.
// Our best estimate of wall time in seconds from 1/1/2000.
std::uint32_t getNetworkTimeNC () const override;
// Our best estimate of current ledger close time.
std::uint32_t getCloseTimeNC () const override;
private:
std::uint32_t getCloseTimeNC (int& offset) const;
public:
void closeTimeOffset (int) override;
/** On return the offset param holds the System time offset in seconds.
*/
boost::posix_time::ptime getNetworkTimePT(int& offset) const;
OperatingMode getOperatingMode () const override
{
return mMode;
@@ -461,8 +447,6 @@ private:
LedgerMaster& m_ledgerMaster;
InboundLedger::pointer mAcquiringLedger;
int mCloseTimeOffset;
SubInfoMapType mSubAccount;
SubInfoMapType mSubRTAccount;
@@ -592,7 +576,7 @@ void NetworkOPsImp::processClusterTimer ()
{
bool synced = (m_ledgerMaster.getValidatedLedgerAge() <= 240);
ClusterNodeStatus us("", synced ? getApp().getFeeTrack().getLocalFee() : 0,
getNetworkTimeNC());
getApp().timeKeeper().now().time_since_epoch().count());
auto& unl = getApp().getUNL();
if (!unl.nodeUpdate(getApp().getLocalCredentials().getNodePublic(), us))
{
@@ -652,56 +636,6 @@ std::string NetworkOPsImp::strOperatingMode () const
return paStatusToken[mMode];
}
boost::posix_time::ptime NetworkOPsImp::getNetworkTimePT (int& offset) const
{
offset = 0;
getApp().getSystemTimeOffset (offset);
if (std::abs (offset) >= 60)
m_journal.warning << "Large system time offset (" << offset << ").";
// VFALCO TODO Replace this with a beast call
return boost::posix_time::microsec_clock::universal_time () +
boost::posix_time::seconds (offset);
}
std::uint32_t NetworkOPsImp::getNetworkTimeNC () const
{
int offset;
return iToSeconds (getNetworkTimePT (offset));
}
std::uint32_t NetworkOPsImp::getCloseTimeNC () const
{
int offset;
return getCloseTimeNC (offset);
}
std::uint32_t NetworkOPsImp::getCloseTimeNC (int& offset) const
{
return iToSeconds (getNetworkTimePT (offset) +
boost::posix_time::seconds (mCloseTimeOffset));
}
void NetworkOPsImp::closeTimeOffset (int offset)
{
// take large offsets, ignore small offsets, push towards our wall time
if (offset > 1)
mCloseTimeOffset += (offset + 3) / 4;
else if (offset < -1)
mCloseTimeOffset += (offset - 3) / 4;
else
mCloseTimeOffset = (mCloseTimeOffset * 3) / 4;
if (mCloseTimeOffset != 0)
{
m_journal.info << "Close time offset now " << mCloseTimeOffset;
if (std::abs (mCloseTimeOffset) >= 60)
m_journal.warning << "Large close time offset (" << mCloseTimeOffset << ").";
}
}
void NetworkOPsImp::submitTransaction (Job&, STTx::pointer iTrans)
{
if (isNeedNetworkLedger ())
@@ -1044,7 +978,7 @@ void NetworkOPsImp::apply (std::unique_lock<std::mutex>& batchLock)
e.transaction->getSTransaction()->add (s);
tx.set_rawtransaction (&s.getData().front(), s.getLength());
tx.set_status (protocol::tsCURRENT);
tx.set_receivetimestamp (getApp().getOPs().getNetworkTimeNC());
tx.set_receivetimestamp (getApp().timeKeeper().now().time_since_epoch().count());
// FIXME: This should be when we received it
getApp().overlay().foreach (send_if_not (
std::make_shared<Message> (tx, protocol::mtTRANSACTION),
@@ -1198,7 +1132,7 @@ void NetworkOPsImp::tryStartConsensus ()
// check if the ledger is good enough to go to omFULL
// Note: Do not go to omFULL if we don't have the previous ledger
// check if the ledger is bad enough to go to omCONNECTED -- TODO
if (getApp().getOPs ().getNetworkTimeNC () <
if (getApp().timeKeeper().now().time_since_epoch().count() <
m_ledgerMaster.getCurrentLedger ()->info().closeTime)
{
setMode (omFULL);
@@ -1401,7 +1335,7 @@ void NetworkOPsImp::switchLastClosedLedger (
protocol::TMStatusChange s;
s.set_newevent (protocol::neSWITCHED_LEDGER);
s.set_ledgerseq (newLCL->info().seq);
s.set_networktime (getApp().getOPs ().getNetworkTimeNC ());
s.set_networktime (getApp().timeKeeper().now().time_since_epoch().count());
uint256 hash = newLCL->info().parentHash;
s.set_ledgerhashprevious (hash.begin (), hash.size ());
hash = newLCL->getHash ();
@@ -2063,22 +1997,24 @@ Json::Value NetworkOPsImp::getServerInfo (bool human, bool admin)
lpClosed->getReserveInc () * baseFee / baseRef))
/ SYSTEM_CURRENCY_PARTS;
int offset;
std::uint32_t closeTime (getCloseTimeNC (offset));
if (std::abs (offset) >= 60)
l[jss::system_time_offset] = offset;
auto const nowOffset = getApp().timeKeeper().nowOffset();
if (std::abs (nowOffset.count()) >= 60)
l[jss::system_time_offset] = nowOffset.count();
auto const closeOffset = getApp().timeKeeper().closeOffset();
if (std::abs (closeOffset.count()) >= 60)
l[jss::close_time_offset] = closeOffset.count();
// VFALCO How do we fix this?
/*
std::uint32_t lCloseTime (lpClosed->info().closeTime);
if (std::abs (mCloseTimeOffset) >= 60)
l[jss::close_time_offset] = mCloseTimeOffset;
if (lCloseTime <= closeTime)
{
std::uint32_t age = closeTime - lCloseTime;
if (age < 1000000)
l[jss::age] = Json::UInt (age);
}
*/
}
if (valid)

View File

@@ -101,12 +101,6 @@ public:
// Network information
//
// Our best estimate of wall time in seconds from 1/1/2000
virtual std::uint32_t getNetworkTimeNC () const = 0;
// Our best estimate of current ledger close time
virtual std::uint32_t getCloseTimeNC () const = 0;
virtual void closeTimeOffset (int) = 0;
virtual OperatingMode getOperatingMode () const = 0;
virtual std::string strOperatingMode () const = 0;

View File

@@ -31,6 +31,7 @@
#include <ripple/basics/Time.h>
#include <ripple/core/Config.h>
#include <ripple/core/LoadFeeTrack.h>
#include <ripple/core/TimeKeeper.h>
#include <ripple/crypto/Base58.h>
#include <ripple/net/HTTPClient.h>
#include <ripple/protocol/JsonFields.h>
@@ -695,7 +696,7 @@ UniqueNodeListImp::getClusterStatus()
std::uint32_t UniqueNodeListImp::getClusterFee()
{
int thresh = getApp().getOPs().getNetworkTimeNC() - 90;
auto const thresh = getApp().timeKeeper().now().time_since_epoch().count() - 90;
std::vector<std::uint32_t> fees;
{
@@ -723,7 +724,7 @@ void UniqueNodeListImp::addClusterStatus (Json::Value& obj)
ScopedUNLLockType sl (mUNLLock);
if (m_clusterNodes.size() > 1) // nodes other than us
{
int now = getApp().getOPs().getNetworkTimeNC();
auto const now = getApp().timeKeeper().now().time_since_epoch().count();
std::uint32_t ref = getApp().getFeeTrack().getLoadBase();
Json::Value& nodes = (obj[jss::cluster] = Json::objectValue);

View File

@@ -29,6 +29,7 @@
#include <ripple/basics/StringUtilities.h>
#include <ripple/basics/chrono.h>
#include <ripple/core/JobQueue.h>
#include <ripple/core/TimeKeeper.h>
#include <beast/cxx14/memory.h> // <memory>
#include <mutex>
#include <thread>
@@ -86,7 +87,7 @@ private:
if (!val->isTrusted() && getApp().getUNL().nodeInUNL (signer))
val->setTrusted();
std::uint32_t now = getApp().getOPs().getCloseTimeNC();
auto const now = getApp().timeKeeper().closeTime().time_since_epoch().count();
std::uint32_t valClose = val->getSignTime();
if ((now > (valClose - LEDGER_EARLY_INTERVAL)) && (now < (valClose + LEDGER_VAL_INTERVAL)))
@@ -176,7 +177,8 @@ private:
if (set)
{
std::uint32_t now = getApp().getOPs ().getNetworkTimeNC ();
auto const now =
getApp().timeKeeper().now().time_since_epoch().count();
for (auto& it: *set)
{
bool isTrusted = it.second->isTrusted ();
@@ -304,7 +306,8 @@ private:
std::list<STValidation::pointer> getCurrentTrustedValidations ()
{
std::uint32_t cutoff = getApp().getOPs ().getNetworkTimeNC () - LEDGER_VAL_INTERVAL;
// VFALCO LEDGER_VAL_INTERVAL should be a NetClock::duration
auto const cutoff = getApp().timeKeeper().now().time_since_epoch().count() - LEDGER_VAL_INTERVAL;
std::list<STValidation::pointer> ret;
@@ -339,7 +342,7 @@ private:
LedgerToValidationCounter getCurrentValidations (
uint256 currentLedger, uint256 priorLedger)
{
std::uint32_t cutoff = getApp().getOPs ().getNetworkTimeNC () - LEDGER_VAL_INTERVAL;
auto const cutoff = getApp().timeKeeper().now().time_since_epoch().count() - LEDGER_VAL_INTERVAL;
bool valCurrentLedger = currentLedger.isNonZero ();
bool valPriorLedger = priorLedger.isNonZero ();

View File

@@ -0,0 +1,100 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_CORE_TIMEKEEPER_H_INCLUDED
#define RIPPLE_CORE_TIMEKEEPER_H_INCLUDED
#include <beast/chrono/abstract_clock.h>
#include <ripple/basics/chrono.h>
#include <string>
#include <vector>
namespace ripple {
/** Manages various times used by the server. */
class TimeKeeper
: public beast::abstract_clock<NetClock>
{
public:
virtual ~TimeKeeper() = default;
/** Launch the internal thread.
The internal thread synchronizes local network time
using the provided list of SNTP servers.
*/
virtual
void
run (std::vector<std::string> const& servers) = 0;
/** Returns the estimate of wall time, in network time.
The network time is wall time adjusted for the Ripple
epoch, the beginning of January 1st, 2000. Each server
can compute a different value for network time. Other
servers value for network time is not directly observable,
but good guesses can be made by looking at validators'
positions on close times.
Servers compute network time by adjusting a local wall
clock using SNTP and then adjusting for the epoch.
*/
virtual
time_point
now() const = 0;
/** Returns the close time, in network time.
Close time is the time the network would agree that
a ledger closed, if a ledger closed right now.
The close time represents the notional "center"
of the network. Each server assumes its clock
is correct, and tries to pull the close time towards
its measure of network time.
*/
virtual
time_point
closeTime() const = 0;
/** Adjust the close time.
This is called in response to received validations.
*/
virtual
void
adjustCloseTime (duration amount) = 0;
virtual
duration
nowOffset() const = 0;
virtual
duration
closeOffset() const = 0;
};
extern
std::unique_ptr<TimeKeeper>
make_TimeKeeper(beast::Journal j);
} // ripple
#endif

View File

@@ -21,11 +21,12 @@
#include <ripple/basics/Log.h>
#include <ripple/basics/ThreadName.h>
#include <ripple/crypto/RandomNumbers.h>
#include <ripple/core/impl/SNTPClient.h>
#include <ripple/core/impl/SNTPClock.h>
#include <beast/asio/placeholders.h>
#include <beast/threads/Thread.h>
#include <boost/asio.hpp>
#include <boost/optional.hpp>
#include <cmath>
#include <deque>
#include <map>
#include <beast/cxx14/memory.h> // <memory>
@@ -69,7 +70,7 @@ static uint8_t SNTPQueryData[48] =
#define NTP_OFF_XMITTS_FRAC 11
class SNTPClientImp
: public SNTPClient
: public SNTPClock
{
private:
struct Query
@@ -78,7 +79,7 @@ private:
time_t sent; // VFALCO time_t, really?
std::uint32_t nonce;
Query (time_t j = (time_t) -1)
Query (time_t j = time_t(-1))
: replied (false)
, sent (j)
{
@@ -86,7 +87,7 @@ private:
};
beast::Journal j_;
std::mutex mutex_;
std::mutex mutable mutex_;
std::thread thread_;
boost::asio::io_service io_service_;
boost::optional<
@@ -114,9 +115,45 @@ public:
, timer_ (io_service_)
, resolver_ (io_service_)
, offset_ (0)
, lastUpdate_ ((time_t) -1)
, lastUpdate_ (time_t(-1))
, buf_ (256)
{
}
~SNTPClientImp ()
{
if (thread_.joinable())
{
error_code ec;
timer_.cancel(ec);
socket_.cancel(ec);
work_ = boost::none;
thread_.join();
}
}
//--------------------------------------------------------------------------
void
run (const std::vector<std::string>& servers) override
{
std::vector<std::string>::const_iterator it = servers.begin ();
if (it == servers.end ())
{
JLOG(j_.info) <<
"SNTP: no server specified";
return;
}
{
std::lock_guard<std::mutex> lock (mutex_);
for (auto const& item : servers)
servers_.emplace_back(
item, time_t(-1));
}
queryAll();
using namespace boost::asio;
socket_.open (ip::udp::v4 ());
socket_.async_receive_from (buffer (buf_, 256),
@@ -129,25 +166,38 @@ public:
timer_.async_wait(std::bind(
&SNTPClientImp::onTimer, this,
beast::asio::placeholders::error));
thread_ = std::thread(&SNTPClientImp::run, this);
// VFALCO Is it correct to launch the thread
// here after queuing I/O?
//
thread_ = std::thread(&SNTPClientImp::doRun, this);
}
~SNTPClientImp ()
time_point
now() const override
{
error_code ec;
timer_.cancel(ec);
socket_.cancel(ec);
work_ = boost::none;
thread_.join();
std::lock_guard<std::mutex> lock (mutex_);
auto const when = clock_type::now();
if ((lastUpdate_ == (time_t)-1) ||
((lastUpdate_ + NTP_TIMESTAMP_VALID) < time(nullptr)))
return when;
return when + std::chrono::seconds(offset_);
}
void run ()
duration
offset() const override
{
setCallingThreadName("SNTPClient");
io_service_.run();
std::lock_guard<std::mutex> lock (mutex_);
return std::chrono::seconds(offset_);
}
//--------------------------------------------------------------------------
void doRun ()
{
setCallingThreadName("SNTPClock");
io_service_.run();
}
void
onTimer (error_code const& ec)
@@ -158,7 +208,7 @@ public:
if (ec)
{
JLOG(j_.error) <<
"SNTPClient::onTimer: " << ec.message();
"SNTPClock::onTimer: " << ec.message();
return;
}
@@ -190,29 +240,41 @@ public:
std::lock_guard<std::mutex> lock (mutex_);
auto const query = queries_.find (ep_);
if (query == queries_.end ())
{
JLOG(j_.debug) <<
"SNTP: Reply from " << ep_ << " found without matching query";
}
else if (query->second.replied)
{
JLOG(j_.debug) <<
"SNTP: Duplicate response from " << ep_;
}
else
{
query->second.replied = true;
if (time (nullptr) > (query->second.sent + 1))
{
JLOG(j_.warning) <<
"SNTP: Late response from " << ep_;
}
else if (bytes_xferd < 48)
{
JLOG(j_.warning) <<
"SNTP: Short reply from " << ep_ <<
" (" << bytes_xferd << ") " << buf_.size ();
}
else if (reinterpret_cast<std::uint32_t*>(
&buf_[0])[NTP_OFF_ORGTS_FRAC] !=
query->second.nonce)
{
JLOG(j_.warning) <<
"SNTP: Reply from " << ep_ << "had wrong nonce";
}
else
{
processReply ();
}
}
}
@@ -224,26 +286,10 @@ public:
//--------------------------------------------------------------------------
void init (const std::vector<std::string>& servers)
{
std::vector<std::string>::const_iterator it = servers.begin ();
if (it == servers.end ())
{
JLOG(j_.info) <<
"SNTP: no server specified";
return;
}
for (auto const& it : servers)
addServer (it);
queryAll ();
}
void addServer (std::string const& server)
{
std::lock_guard<std::mutex> lock (mutex_);
servers_.push_back (std::make_pair (server, (time_t) - 1));
servers_.push_back (std::make_pair (server, time_t(-1)));
}
void queryAll ()
@@ -253,27 +299,15 @@ public:
}
}
bool getOffset (int& offset)
{
std::lock_guard<std::mutex> lock (mutex_);
if ((lastUpdate_ == (time_t) - 1) || ((lastUpdate_ + NTP_TIMESTAMP_VALID) < time (nullptr)))
return false;
offset = offset_;
return true;
}
bool doQuery ()
{
std::lock_guard<std::mutex> lock (mutex_);
std::vector< std::pair<std::string, time_t> >::iterator best = servers_.end ();
for (std::vector< std::pair<std::string, time_t> >::iterator it = servers_.begin (), end = best;
it != end; ++it)
if ((best == end) || (it->second == (time_t) - 1) || (it->second < best->second))
best = it;
for (auto iter = servers_.begin (), end = best;
iter != end; ++iter)
if ((best == end) || (iter->second == time_t(-1)) || (iter->second < best->second))
best = iter;
if (best == servers_.end ())
{
@@ -284,7 +318,7 @@ public:
time_t now = time (nullptr);
if ((best->second != (time_t) - 1) && ((best->second + NTP_MIN_QUERY) >= now))
if ((best->second != time_t(-1)) && ((best->second + NTP_MIN_QUERY) >= now))
{
JLOG(j_.trace) <<
"SNTP: All servers recently queried";
@@ -300,44 +334,53 @@ public:
beast::asio::placeholders::error,
beast::asio::placeholders::iterator));
JLOG(j_.trace) <<
"SNTP: Resolve pending for " << best->first;
"SNTPClock: Resolve pending for " << best->first;
return true;
}
void resolveComplete (const error_code& error, boost::asio::ip::udp::resolver::iterator it)
void resolveComplete (error_code const& ec,
boost::asio::ip::udp::resolver::iterator it)
{
if (!error)
using namespace boost::asio;
if (ec == error::operation_aborted)
return;
if (ec)
{
boost::asio::ip::udp::resolver::iterator sel = it;
int i = 1;
JLOG(j_.trace) <<
"SNTPClock::resolveComplete: " << ec.message();
return;
}
while (++it != boost::asio::ip::udp::resolver::iterator ())
if ((rand () % ++i) == 0)
sel = it;
ip::udp::resolver::iterator sel = it;
int i = 1;
if (sel != boost::asio::ip::udp::resolver::iterator ())
while (++it != ip::udp::resolver::iterator())
if ((rand () % ++i) == 0)
sel = it;
if (sel != ip::udp::resolver::iterator ())
{
std::lock_guard<std::mutex> lock (mutex_);
Query& query = queries_[*sel];
time_t now = time (nullptr);
if ((query.sent == now) || ((query.sent + 1) == now))
{
std::lock_guard<std::mutex> lock (mutex_);
Query& query = queries_[*sel];
time_t now = time (nullptr);
if ((query.sent == now) || ((query.sent + 1) == now))
{
// This can happen if the same IP address is reached through multiple names
JLOG(j_.trace) <<
"SNTP: Redundant query suppressed";
return;
}
query.replied = false;
query.sent = now;
random_fill (&query.nonce);
reinterpret_cast<std::uint32_t*> (SNTPQueryData)[NTP_OFF_XMITTS_INT] = static_cast<std::uint32_t> (time (nullptr)) + NTP_UNIX_OFFSET;
reinterpret_cast<std::uint32_t*> (SNTPQueryData)[NTP_OFF_XMITTS_FRAC] = query.nonce;
socket_.async_send_to (boost::asio::buffer (SNTPQueryData, 48), *sel,
std::bind (&SNTPClientImp::onSend, this,
beast::asio::placeholders::error, beast::asio::placeholders::bytes_transferred));
// This can happen if the same IP address is reached through multiple names
JLOG(j_.trace) <<
"SNTP: Redundant query suppressed";
return;
}
query.replied = false;
query.sent = now;
random_fill (&query.nonce);
reinterpret_cast<std::uint32_t*> (SNTPQueryData)[NTP_OFF_XMITTS_INT] = static_cast<std::uint32_t> (time (nullptr)) + NTP_UNIX_OFFSET;
reinterpret_cast<std::uint32_t*> (SNTPQueryData)[NTP_OFF_XMITTS_FRAC] = query.nonce;
socket_.async_send_to(buffer(SNTPQueryData, 48),
*sel, std::bind (&SNTPClientImp::onSend, this,
beast::asio::placeholders::error,
beast::asio::placeholders::bytes_transferred));
}
}
@@ -349,7 +392,7 @@ public:
if (ec)
{
JLOG(j_.warning) <<
"SNTPClient::onSend: " << ec.message();
"SNTPClock::onSend: " << ec.message();
return;
}
}
@@ -414,8 +457,8 @@ public:
//------------------------------------------------------------------------------
std::unique_ptr<SNTPClient>
make_SNTPClient (beast::Journal j)
std::unique_ptr<SNTPClock>
make_SNTPClock (beast::Journal j)
{
return std::make_unique<SNTPClientImp>(j);
}

View File

@@ -17,29 +17,36 @@
*/
//==============================================================================
#ifndef RIPPLE_NET_SNTPCLIENT_H_INCLUDED
#define RIPPLE_NET_SNTPCLIENT_H_INCLUDED
#ifndef RIPPLE_NET_SNTPCLOCK_H_INCLUDED
#define RIPPLE_NET_SNTPCLOCK_H_INCLUDED
#include <beast/chrono/abstract_clock.h>
#include <beast/utility/Journal.h>
#include <chrono>
#include <memory>
#include <string>
#include <vector>
namespace ripple {
class SNTPClient
/** A clock based on system_clock and adjusted for SNTP. */
class SNTPClock
: public beast::abstract_clock<
std::chrono::system_clock>
{
public:
virtual ~SNTPClient() = default;
virtual void init (std::vector <std::string> const& servers) = 0;
virtual void addServer (std::string const& mServer) = 0;
virtual void queryAll () = 0;
virtual bool getOffset (int& offset) = 0;
virtual
void
run (std::vector <std::string> const& servers) = 0;
virtual
duration
offset() const = 0;
};
extern
std::unique_ptr<SNTPClient>
make_SNTPClient (beast::Journal);
std::unique_ptr<SNTPClock>
make_SNTPClock (beast::Journal);
} // ripple

View File

@@ -0,0 +1,133 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <BeastConfig.h>
#include <ripple/basics/Log.h>
#include <ripple/core/TimeKeeper.h>
#include <ripple/core/impl/SNTPClock.h>
#include <beast/cxx14/memory.h> // <memory>
#include <mutex>
namespace ripple {
class TimeKeeperImpl : public TimeKeeper
{
private:
beast::Journal j_;
std::mutex mutable mutex_;
duration closeOffset_;
std::unique_ptr<SNTPClock> clock_;
// Adjust system_clock::time_point for NetClock epoch
static
time_point
adjust (std::chrono::system_clock::time_point when)
{
return time_point(
std::chrono::duration_cast<duration>(
when.time_since_epoch() -
days(10957)));
}
public:
explicit
TimeKeeperImpl (beast::Journal j)
: j_ (j)
, clock_ (make_SNTPClock(j))
{
}
void
run (std::vector<
std::string> const& servers) override
{
clock_->run(servers);
}
time_point
now() const override
{
std::lock_guard<std::mutex> lock(mutex_);
return adjust(clock_->now());
}
time_point
closeTime() const override
{
std::lock_guard<std::mutex> lock(mutex_);
return adjust(clock_->now()) + closeOffset_;
}
void
adjustCloseTime(
NetClock::duration amount) override
{
using namespace std::chrono;
auto const s = amount.count();
std::lock_guard<std::mutex> lock(mutex_);
// Take large offsets, ignore small offsets,
// push the close time towards our wall time.
if (s > 1)
closeOffset_ += seconds((s + 3) / 4);
else if (s < -1)
closeOffset_ += seconds((s - 3) / 4);
else
closeOffset_ = (closeOffset_ * 3) / 4;
if (closeOffset_.count() != 0)
{
if (std::abs (closeOffset_.count()) < 60)
{
JLOG(j_.info) <<
"TimeKeeper: Close time offset now " <<
closeOffset_.count();
}
else
{
JLOG(j_.warning) <<
"TimeKeeper: Large close time offset = " <<
closeOffset_.count();
}
}
}
duration
nowOffset() const override
{
std::lock_guard<std::mutex> lock(mutex_);
return std::chrono::duration_cast<duration>(
clock_->offset());
}
duration
closeOffset() const override
{
std::lock_guard<std::mutex> lock(mutex_);
return closeOffset_;
}
};
//------------------------------------------------------------------------------
std::unique_ptr<TimeKeeper>
make_TimeKeeper (beast::Journal j)
{
return std::make_unique<TimeKeeperImpl>(j);
}
} // ripple

View File

@@ -66,25 +66,30 @@ struct Fees
/** Information about the notional ledger backing the view. */
struct LedgerInfo
{
// Fields for all ledgers
//
// For all ledgers
//
bool open = true;
LedgerIndex seq = 0;
std::uint32_t parentCloseTime = 0;
// Fields for closed ledgers
//
// For closed ledgers
//
// Closed means "tx set already determined"
uint256 hash = zero;
uint256 txHash = zero;
uint256 accountHash = zero;
uint256 parentHash = zero;
//uint256 stateHash;
std::uint64_t drops = 0;
// If validated is false, it means "not yet validated."
// Once validated is true, it will never be set false at a later time.
mutable
bool validated = false;
// VFALCO TODO Make this not mutable
bool mutable validated = false;
bool accepted = false;
// flags indicating how this ledger close took place
int closeFlags = 0;
@@ -103,7 +108,6 @@ struct LedgerInfo
static
std::uint32_t const sLCF_NoConsensusTime = 1;
inline
bool getCloseAgree (LedgerInfo const& info)
{

View File

@@ -32,6 +32,7 @@
#include <ripple/basics/StringUtilities.h>
#include <ripple/basics/UptimeTimer.h>
#include <ripple/core/JobQueue.h>
#include <ripple/core/TimeKeeper.h>
#include <ripple/json/json_reader.h>
#include <ripple/resource/Fees.h>
#include <ripple/server/ServerHandler.h>
@@ -1144,7 +1145,7 @@ PeerImp::onMessage (std::shared_ptr <protocol::TMProposeSet> const& m)
set.set_hops(set.hops() + 1);
// VFALCO Magic numbers are bad
if ((set.closetime() + 180) < getApp().getOPs().getCloseTimeNC())
if ((set.closetime() + 180) < getApp().timeKeeper().closeTime().time_since_epoch().count())
return;
auto const type = publicKeyType(
@@ -1231,7 +1232,7 @@ PeerImp::onMessage (std::shared_ptr <protocol::TMStatusChange> const& m)
p_journal_.trace << "Status: Change";
if (!m->has_networktime ())
m->set_networktime (getApp().getOPs ().getNetworkTimeNC ());
m->set_networktime (getApp().timeKeeper().now().time_since_epoch().count());
if (!last_status_.has_newstatus () || m->has_newstatus ())
last_status_ = *m;
@@ -1410,7 +1411,7 @@ void
PeerImp::onMessage (std::shared_ptr <protocol::TMValidation> const& m)
{
error_code ec;
std::uint32_t closeTime = getApp().getOPs().getCloseTimeNC();
auto const closeTime = getApp().timeKeeper().closeTime().time_since_epoch().count();
if (m->has_hops() && ! slot_->cluster())
m->set_hops(m->hops() + 1);

View File

@@ -22,6 +22,7 @@
#include <ripple/app/main/Application.h>
#include <ripple/app/main/LocalCredentials.h>
#include <ripple/app/misc/NetworkOPs.h>
#include <ripple/core/TimeKeeper.h>
#include <ripple/protocol/digest.h>
#include <ripple/protocol/BuildInfo.h>
#include <ripple/overlay/impl/TMHello.h>
@@ -116,7 +117,7 @@ buildHello (uint256 const& sharedValue, Application& app)
h.set_protoversion (to_packed (BuildInfo::getCurrentProtocol()));
h.set_protoversionmin (to_packed (BuildInfo::getMinimumProtocol()));
h.set_fullversion (BuildInfo::getFullVersionString ());
h.set_nettime (app.getOPs ().getNetworkTimeNC ());
h.set_nettime (app.timeKeeper().now().time_since_epoch().count());
h.set_nodepublic (app.getLocalCredentials ().getNodePublic (
).humanNodePublic ());
h.set_nodeproof (&vchSig[0], vchSig.size ());
@@ -300,7 +301,7 @@ verifyHello (protocol::TMHello const& h, uint256 const& sharedValue,
beast::Journal journal, Application& app)
{
std::pair<RippleAddress, bool> result = { {}, false };
std::uint32_t const ourTime = app.getOPs().getNetworkTimeNC();
auto const ourTime = app.timeKeeper().now().time_since_epoch().count();
std::uint32_t const minTime = ourTime - clockToleranceDeltaSeconds;
std::uint32_t const maxTime = ourTime + clockToleranceDeltaSeconds;

View File

@@ -26,7 +26,8 @@
#include <ripple/core/impl/LoadMonitor.cpp>
#include <ripple/core/impl/Job.cpp>
#include <ripple/core/impl/JobQueue.cpp>
#include <ripple/core/impl/SNTPClient.cpp>
#include <ripple/core/impl/SNTPClock.cpp>
#include <ripple/core/impl/TimeKeeper.cpp>
#include <ripple/core/tests/LoadFeeTrack.test.cpp>
#include <ripple/core/tests/Config.test.cpp>