From 86e098c5f7fecc0033e3818bac5a33c7c5c23c80 Mon Sep 17 00:00:00 2001 From: Nik Bougalis Date: Tue, 18 Jul 2023 15:06:45 -0700 Subject: [PATCH] Eliminate the built-in SNTP support (fixes #4207): The codebase includes a simple SNTP client that is enabled by default and used to adjust an application-level clock in case the operating system's clock is not already synchronized. A recent audit of the code conducted by Bishop-Fox identified a potential attack vector, which could allow an attacker with the capability to manipulate the UDP traffic flow of a server by redirecting queries or by injecting crafted SNTP responses to manipulate the correction factor, skewing the clock. Although the impact of such an attack, if mounted, is minimal and can be completely eliminated by editing the configuration file to disable the built-in SNTP client, this commit instead entirely eliminates the "crufty" 10+ year-old code, removes a mutex and greatly simplifies the `TimeKeeper`. **Note to server operators:** It is highly recommended to use time synchronization software to ensure that the system clock is accurate. For more details see https://en.wikipedia.org/wiki/Ntpd#Implementations. --- Builds/CMake/RippledCore.cmake | 3 - Builds/levelization/results/ordering.txt | 1 - cfg/rippled-example.cfg | 19 - cfg/rippled-reporting.cfg | 19 - src/ripple/app/main/Application.cpp | 3 - src/ripple/app/main/Main.cpp | 4 +- src/ripple/app/misc/NetworkOPs.cpp | 18 +- src/ripple/beast/clock/abstract_clock.h | 2 +- src/ripple/core/Config.h | 8 +- src/ripple/core/TimeKeeper.h | 120 +++--- src/ripple/core/impl/Config.cpp | 32 +- src/ripple/core/impl/SNTPClock.cpp | 491 ----------------------- src/ripple/core/impl/SNTPClock.h | 47 --- src/ripple/core/impl/TimeKeeper.cpp | 124 ------ src/test/jtx/ManualTimeKeeper.h | 41 +- src/test/jtx/impl/ManualTimeKeeper.cpp | 95 ----- src/test/overlay/compression_test.cpp | 21 +- 17 files changed, 124 insertions(+), 924 deletions(-) delete mode 100644 src/ripple/core/impl/SNTPClock.cpp delete mode 100644 src/ripple/core/impl/SNTPClock.h delete mode 100644 src/ripple/core/impl/TimeKeeper.cpp delete mode 100644 src/test/jtx/impl/ManualTimeKeeper.cpp diff --git a/Builds/CMake/RippledCore.cmake b/Builds/CMake/RippledCore.cmake index 87d6484288f..8d2ff6cbaef 100644 --- a/Builds/CMake/RippledCore.cmake +++ b/Builds/CMake/RippledCore.cmake @@ -568,9 +568,7 @@ target_sources (rippled PRIVATE src/ripple/core/impl/JobQueue.cpp src/ripple/core/impl/LoadEvent.cpp src/ripple/core/impl/LoadMonitor.cpp - src/ripple/core/impl/SNTPClock.cpp src/ripple/core/impl/SociDB.cpp - src/ripple/core/impl/TimeKeeper.cpp src/ripple/core/impl/Workers.cpp src/ripple/core/Pg.cpp #[===============================[ @@ -929,7 +927,6 @@ if (tests) src/test/jtx/impl/AMMTest.cpp src/test/jtx/impl/Env.cpp src/test/jtx/impl/JSONRPCClient.cpp - src/test/jtx/impl/ManualTimeKeeper.cpp src/test/jtx/impl/TestHelpers.cpp src/test/jtx/impl/WSClient.cpp src/test/jtx/impl/acctdelete.cpp diff --git a/Builds/levelization/results/ordering.txt b/Builds/levelization/results/ordering.txt index 7a4f4404321..832b548e5de 100644 --- a/Builds/levelization/results/ordering.txt +++ b/Builds/levelization/results/ordering.txt @@ -173,7 +173,6 @@ test.nodestore > test.unit_test test.overlay > ripple.app test.overlay > ripple.basics test.overlay > ripple.beast -test.overlay > ripple.core test.overlay > ripple.overlay test.overlay > ripple.peerfinder test.overlay > ripple.protocol diff --git a/cfg/rippled-example.cfg b/cfg/rippled-example.cfg index 6e1d553a970..e21197eed5a 100644 --- a/cfg/rippled-example.cfg +++ b/cfg/rippled-example.cfg @@ -463,19 +463,6 @@ # # # -# [sntp_servers] -# -# IP address or domain of NTP servers to use for time synchronization. -# -# These NTP servers are suitable for rippled servers located in the United -# States: -# time.windows.com -# time.apple.com -# time.nist.gov -# pool.ntp.org -# -# -# # [max_transactions] # # Configure the maximum number of transactions to have in the job queue @@ -1704,12 +1691,6 @@ advisory_delete=0 [debug_logfile] /var/log/rippled/debug.log -[sntp_servers] -time.windows.com -time.apple.com -time.nist.gov -pool.ntp.org - # To use the XRP test network # (see https://xrpl.org/connect-your-rippled-to-the-xrp-test-net.html), # use the following [ips] section: diff --git a/cfg/rippled-reporting.cfg b/cfg/rippled-reporting.cfg index f09c17ae637..632a8a7800e 100644 --- a/cfg/rippled-reporting.cfg +++ b/cfg/rippled-reporting.cfg @@ -450,19 +450,6 @@ # # # -# [sntp_servers] -# -# IP address or domain of NTP servers to use for time synchronization. -# -# These NTP servers are suitable for rippled servers located in the United -# States: -# time.windows.com -# time.apple.com -# time.nist.gov -# pool.ntp.org -# -# -# # [max_transactions] # # Configure the maximum number of transactions to have in the job queue @@ -1662,12 +1649,6 @@ advisory_delete=0 [debug_logfile] /var/log/rippled-reporting/debug.log -[sntp_servers] -time.windows.com -time.apple.com -time.nist.gov -pool.ntp.org - # To use the XRP test network # (see https://xrpl.org/connect-your-rippled-to-the-xrp-test-net.html), # use the following [ips] section: diff --git a/src/ripple/app/main/Application.cpp b/src/ripple/app/main/Application.cpp index 14b816e4564..8fcbb8a971c 100644 --- a/src/ripple/app/main/Application.cpp +++ b/src/ripple/app/main/Application.cpp @@ -1175,9 +1175,6 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) // Optionally turn off logging to console. logs_->silent(config_->silent()); - if (!config_->standalone()) - timeKeeper_->run(config_->SNTP_SERVERS); - if (!initRelationalDatabase() || !initNodeStore()) return false; diff --git a/src/ripple/app/main/Main.cpp b/src/ripple/app/main/Main.cpp index 80ac40132d5..a998640c01c 100644 --- a/src/ripple/app/main/Main.cpp +++ b/src/ripple/app/main/Main.cpp @@ -803,10 +803,8 @@ run(int argc, char** argv) if (vm.count("debug")) setDebugLogSink(logs->makeSink("Debug", beast::severities::kTrace)); - auto timeKeeper = make_TimeKeeper(logs->journal("TimeKeeper")); - auto app = make_Application( - std::move(config), std::move(logs), std::move(timeKeeper)); + std::move(config), std::move(logs), std::make_unique()); if (!app->setup(vm)) return -1; diff --git a/src/ripple/app/misc/NetworkOPs.cpp b/src/ripple/app/misc/NetworkOPs.cpp index 4e91a9d32f6..cd7fe3861ec 100644 --- a/src/ripple/app/misc/NetworkOPs.cpp +++ b/src/ripple/app/misc/NetworkOPs.cpp @@ -739,11 +739,10 @@ class NetworkOPsImp final : public NetworkOPs sPeerStatus, // Peer status changes. sConsensusPhase, // Consensus phase sBookChanges, // Per-ledger order book changes - - sLastEntry = sBookChanges // as this name implies, any new entry - // must be ADDED ABOVE this one + sLastEntry // Any new entry must be ADDED ABOVE this one }; - std::array mStreamMaps; + + std::array mStreamMaps; ServerFeeSummary mLastFeeSummary; @@ -2614,13 +2613,10 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) lpClosed->fees().accountReserve(0).decimalXRP(); l[jss::reserve_inc_xrp] = lpClosed->fees().increment.decimalXRP(); - auto const nowOffset = app_.timeKeeper().nowOffset(); - if (std::abs(nowOffset.count()) >= 60) - l[jss::system_time_offset] = nowOffset.count(); - - auto const closeOffset = app_.timeKeeper().closeOffset(); - if (std::abs(closeOffset.count()) >= 60) - l[jss::close_time_offset] = closeOffset.count(); + if (auto const closeOffset = app_.timeKeeper().closeOffset(); + std::abs(closeOffset.count()) >= 60) + l[jss::close_time_offset] = + static_cast(closeOffset.count()); #if RIPPLED_REPORTING std::int64_t const dbAge = diff --git a/src/ripple/beast/clock/abstract_clock.h b/src/ripple/beast/clock/abstract_clock.h index e543d6d7bb4..128ab82b4bd 100644 --- a/src/ripple/beast/clock/abstract_clock.h +++ b/src/ripple/beast/clock/abstract_clock.h @@ -70,7 +70,7 @@ class abstract_clock abstract_clock(abstract_clock const&) = default; /** Returns the current time. */ - virtual time_point + [[nodiscard]] virtual time_point now() const = 0; }; diff --git a/src/ripple/core/Config.h b/src/ripple/core/Config.h index 48bc9681e46..a9acd4c6b2b 100644 --- a/src/ripple/core/Config.h +++ b/src/ripple/core/Config.h @@ -149,9 +149,11 @@ class Config : public BasicConfig bool nodeToShard = false; bool ELB_SUPPORT = false; - std::vector IPS; // Peer IPs from rippled.cfg. - std::vector IPS_FIXED; // Fixed Peer IPs from rippled.cfg. - std::vector SNTP_SERVERS; // SNTP servers from rippled.cfg. + // Entries from [ips] config stanza + std::vector IPS; + + // Entries from [ips_fixed] config stanza + std::vector IPS_FIXED; enum StartUpType { FRESH, NORMAL, LOAD, LOAD_FILE, REPLAY, NETWORK }; StartUpType START_UP = NORMAL; diff --git a/src/ripple/core/TimeKeeper.h b/src/ripple/core/TimeKeeper.h index ebc6c1f1ab0..55970ec8227 100644 --- a/src/ripple/core/TimeKeeper.h +++ b/src/ripple/core/TimeKeeper.h @@ -22,73 +22,99 @@ #include #include -#include -#include -#include +#include namespace ripple { /** Manages various times used by the server. */ class TimeKeeper : public beast::abstract_clock { -public: - virtual ~TimeKeeper() = default; - - /** Launch the internal thread. +private: + std::atomic closeOffset_{}; - The internal thread synchronizes local network time - using the provided list of SNTP servers. - */ - virtual void - run(std::vector const& servers) = 0; - - /** Returns the estimate of wall time, in network time. + // Adjust system_clock::time_point for NetClock epoch + static constexpr time_point + adjust(std::chrono::system_clock::time_point when) + { + return time_point(std::chrono::duration_cast( + when.time_since_epoch() - days(10957))); + } - The network time is wall time adjusted for the Ripple - epoch, the beginning of January 1st, 2000 UTC. 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. +public: + virtual ~TimeKeeper() = default; - Servers compute network time by adjusting a local wall - clock using SNTP and then adjusting for the epoch. - */ - virtual time_point - now() const override = 0; + /** Returns the current time, using the server's clock. - /** Returns the close time, in network time. + It's possible for servers to have a different value for network + time, especially if they do not use some external mechanism for + time synchronization (e.g. NTP or SNTP). This is fine. - Close time is the time the network would agree that - a ledger closed, if a ledger closed right now. + This estimate is not directly visible to other servers over the + protocol, but it is possible for them to make an educated guess + if this server publishes proposals or validations. - 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. + @note The network time is adjusted for the "Ripple epoch" which + was arbitrarily defined as 2000-01-01T00:00:00Z by Arthur + Britto and David Schwartz during early development of the + code. No rationale has been provided for this curious and + annoying, but otherwise unimportant, choice. */ - virtual time_point - closeTime() const = 0; + [[nodiscard]] time_point + now() const override + { + return adjust(std::chrono::system_clock::now()); + } - /** Adjust the close time. + /** Returns the predicted close time, in network time. - This is called in response to received validations. + The predicted close time represents the notional "center" of the + network. Each server assumes that its clock is correct and tries + to pull the close time towards its measure of network time. */ - virtual void - adjustCloseTime(std::chrono::duration amount) = 0; + [[nodiscard]] time_point + closeTime() const + { + return now() + closeOffset_.load(); + } // This may return a negative value - virtual std::chrono::duration - nowOffset() const = 0; - - // This may return a negative value - virtual std::chrono::duration - closeOffset() const = 0; + [[nodiscard]] std::chrono::seconds + closeOffset() const + { + return closeOffset_.load(); + } + + /** Adjust the close time, based on the network's view of time. */ + std::chrono::seconds + adjustCloseTime(std::chrono::seconds by) + { + using namespace std::chrono_literals; + + auto offset = closeOffset_.load(); + + if (by == 0s && offset == 0s) + return offset; + + // The close time adjustment is serialized externally to this + // code. The compare/exchange only serves as a weak check and + // should not fail. Even if it does, it's safe to simply just + // skip the adjustment. + closeOffset_.compare_exchange_strong(offset, [by, offset]() { + // Ignore small offsets and push the close time + // towards our wall time. + if (by > 1s) + return offset + ((by + 3s) / 4); + + if (by < -1s) + return offset + ((by - 3s) / 4); + + return (offset * 3) / 4; + }()); + + return closeOffset_.load(); + } }; -extern std::unique_ptr -make_TimeKeeper(beast::Journal j); - } // namespace ripple #endif diff --git a/src/ripple/core/impl/Config.cpp b/src/ripple/core/impl/Config.cpp index f835ca8df04..2f26b8ba525 100644 --- a/src/ripple/core/impl/Config.cpp +++ b/src/ripple/core/impl/Config.cpp @@ -206,14 +206,10 @@ parseIniFile(std::string const& strInput, const bool bTrim) IniFileSections::mapped_type* getIniFileSection(IniFileSections& secSource, std::string const& strSection) { - IniFileSections::iterator it; - IniFileSections::mapped_type* smtResult; - it = secSource.find(strSection); - if (it == secSource.end()) - smtResult = nullptr; - else - smtResult = &(it->second); - return smtResult; + if (auto it = secSource.find(strSection); it != secSource.end()) + return &(it->second); + + return nullptr; } bool @@ -223,22 +219,21 @@ getSingleSection( std::string& strValue, beast::Journal j) { - IniFileSections::mapped_type* pmtEntries = - getIniFileSection(secSource, strSection); - bool bSingle = pmtEntries && 1 == pmtEntries->size(); + auto const pmtEntries = getIniFileSection(secSource, strSection); - if (bSingle) + if (pmtEntries && pmtEntries->size() == 1) { strValue = (*pmtEntries)[0]; + return true; } - else if (pmtEntries) + + if (pmtEntries) { - JLOG(j.warn()) << boost::str( - boost::format("Section [%s]: requires 1 line not %d lines.") % - strSection % pmtEntries->size()); + JLOG(j.warn()) << "Section '" << strSection << "': requires 1 line not " + << pmtEntries->size() << " lines."; } - return bSingle; + return false; } //------------------------------------------------------------------------------ @@ -461,9 +456,6 @@ Config::loadFromString(std::string const& fileContents) if (auto s = getIniFileSection(secConfig, SECTION_IPS_FIXED)) IPS_FIXED = *s; - if (auto s = getIniFileSection(secConfig, SECTION_SNTP)) - SNTP_SERVERS = *s; - // if the user has specified ip:port then replace : with a space. { auto replaceColons = [](std::vector& strVec) { diff --git a/src/ripple/core/impl/SNTPClock.cpp b/src/ripple/core/impl/SNTPClock.cpp deleted file mode 100644 index 8651dcbe5a4..00000000000 --- a/src/ripple/core/impl/SNTPClock.cpp +++ /dev/null @@ -1,491 +0,0 @@ -//------------------------------------------------------------------------------ -/* - 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ripple { - -static uint8_t SNTPQueryData[48] = { - 0x1B, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - -using namespace std::chrono_literals; -// NTP query frequency - 4 minutes -auto constexpr NTP_QUERY_FREQUENCY = 4min; - -// NTP minimum interval to query same servers - 3 minutes -auto constexpr NTP_MIN_QUERY = 3min; - -// NTP sample window (should be odd) -#define NTP_SAMPLE_WINDOW 9 - -// NTP timestamp constant -auto constexpr NTP_UNIX_OFFSET = 0x83AA7E80s; - -// NTP timestamp validity -auto constexpr NTP_TIMESTAMP_VALID = (NTP_QUERY_FREQUENCY + NTP_MIN_QUERY) * 2; - -// SNTP packet offsets -#define NTP_OFF_INFO 0 -#define NTP_OFF_ROOTDELAY 1 -#define NTP_OFF_ROOTDISP 2 -#define NTP_OFF_REFERENCEID 3 -#define NTP_OFF_REFTS_INT 4 -#define NTP_OFF_REFTS_FRAC 5 -#define NTP_OFF_ORGTS_INT 6 -#define NTP_OFF_ORGTS_FRAC 7 -#define NTP_OFF_RECVTS_INT 8 -#define NTP_OFF_RECVTS_FRAC 9 -#define NTP_OFF_XMITTS_INT 10 -#define NTP_OFF_XMITTS_FRAC 11 - -class SNTPClientImp : public SNTPClock -{ -private: - template - using sys_time = std::chrono::time_point; - - using sys_seconds = sys_time; - - struct Query - { - bool replied; - sys_seconds sent; - std::uint32_t nonce; - - explicit Query(sys_seconds j = sys_seconds::max()) - : replied(false), sent(j) - { - } - }; - - beast::Journal const j_; - std::mutex mutable mutex_; - std::thread thread_; - boost::asio::io_service io_service_; - std::optional work_; - - std::map queries_; - boost::asio::ip::udp::socket socket_; - boost::asio::basic_waitable_timer timer_; - boost::asio::ip::udp::resolver resolver_; - std::vector> servers_; - std::chrono::seconds offset_; - sys_seconds lastUpdate_; - std::deque offsets_; - std::vector buf_; - boost::asio::ip::udp::endpoint ep_; - -public: - using error_code = boost::system::error_code; - - explicit SNTPClientImp(beast::Journal j) - : j_(j) - , work_(io_service_) - , socket_(io_service_) - , timer_(io_service_) - , resolver_(io_service_) - , offset_(0) - , lastUpdate_(sys_seconds::max()) - , buf_(256) - { - } - - ~SNTPClientImp() override - { - if (thread_.joinable()) - { - error_code ec; - timer_.cancel(ec); - socket_.cancel(ec); - work_ = std::nullopt; - thread_.join(); - } - } - - //-------------------------------------------------------------------------- - - void - run(const std::vector& servers) override - { - std::vector::const_iterator it = servers.begin(); - - if (it == servers.end()) - { - JLOG(j_.info()) << "SNTP: no server specified"; - return; - } - - { - std::lock_guard lock(mutex_); - for (auto const& item : servers) - servers_.emplace_back(item, sys_seconds::max()); - } - queryAll(); - - using namespace boost::asio; - socket_.open(ip::udp::v4()); - socket_.bind(ep_); - socket_.async_receive_from( - buffer(buf_, 256), - ep_, - std::bind( - &SNTPClientImp::onRead, - this, - std::placeholders::_1, - std::placeholders::_2)); - timer_.expires_from_now(NTP_QUERY_FREQUENCY); - timer_.async_wait( - std::bind(&SNTPClientImp::onTimer, this, std::placeholders::_1)); - - thread_ = std::thread(&SNTPClientImp::doRun, this); - } - - time_point - now() const override - { - std::lock_guard lock(mutex_); - using namespace std::chrono; - auto const when = time_point_cast(clock_type::now()); - if ((lastUpdate_ == sys_seconds::max()) || - ((lastUpdate_ + NTP_TIMESTAMP_VALID) < - time_point_cast(clock_type::now()))) - return when; - return when + offset_; - } - - duration - offset() const override - { - std::lock_guard lock(mutex_); - return offset_; - } - - //-------------------------------------------------------------------------- - - void - doRun() - { - beast::setCurrentThreadName("rippled: SNTPClock"); - io_service_.run(); - } - - void - onTimer(error_code const& ec) - { - using namespace boost::asio; - if (ec == error::operation_aborted) - return; - if (ec) - { - JLOG(j_.error()) << "SNTPClock::onTimer: " << ec.message(); - return; - } - - doQuery(); - timer_.expires_from_now(NTP_QUERY_FREQUENCY); - timer_.async_wait( - std::bind(&SNTPClientImp::onTimer, this, std::placeholders::_1)); - } - - void - onRead(error_code const& ec, std::size_t bytes_xferd) - { - using namespace boost::asio; - using namespace std::chrono; - if (ec == error::operation_aborted) - return; - - // VFALCO Should we return on any error? - /* - if (ec) - return; - */ - - if (!ec) - { - JLOG(j_.trace()) << "SNTP: Packet from " << ep_; - std::lock_guard 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_point_cast(clock_type::now()) > - (query->second.sent + 1s)) - { - JLOG(j_.warn()) << "SNTP: Late response from " << ep_; - } - else if (bytes_xferd < 48) - { - JLOG(j_.warn()) << "SNTP: Short reply from " << ep_ << " (" - << bytes_xferd << ") " << buf_.size(); - } - else if ( - reinterpret_cast( - &buf_[0])[NTP_OFF_ORGTS_FRAC] != query->second.nonce) - { - JLOG(j_.warn()) - << "SNTP: Reply from " << ep_ << "had wrong nonce"; - } - else - { - processReply(); - } - } - } - - socket_.async_receive_from( - buffer(buf_, 256), - ep_, - std::bind( - &SNTPClientImp::onRead, - this, - std::placeholders::_1, - std::placeholders::_2)); - } - - //-------------------------------------------------------------------------- - - void - addServer(std::string const& server) - { - std::lock_guard lock(mutex_); - servers_.push_back(std::make_pair(server, sys_seconds::max())); - } - - void - queryAll() - { - while (doQuery()) - { - } - } - - bool - doQuery() - { - std::lock_guard lock(mutex_); - auto best = servers_.end(); - - for (auto iter = servers_.begin(), end = best; iter != end; ++iter) - if ((best == end) || (iter->second == sys_seconds::max()) || - (iter->second < best->second)) - best = iter; - - if (best == servers_.end()) - { - JLOG(j_.trace()) << "SNTP: No server to query"; - return false; - } - - using namespace std::chrono; - auto now = time_point_cast(clock_type::now()); - - if ((best->second != sys_seconds::max()) && - ((best->second + NTP_MIN_QUERY) >= now)) - { - JLOG(j_.trace()) << "SNTP: All servers recently queried"; - return false; - } - - best->second = now; - - boost::asio::ip::udp::resolver::query query( - boost::asio::ip::udp::v4(), best->first, "ntp"); - resolver_.async_resolve( - query, - std::bind( - &SNTPClientImp::resolveComplete, - this, - std::placeholders::_1, - std::placeholders::_2)); - JLOG(j_.trace()) << "SNTPClock: Resolve pending for " << best->first; - return true; - } - - void - resolveComplete( - error_code const& ec, - boost::asio::ip::udp::resolver::iterator it) - { - using namespace boost::asio; - if (ec == error::operation_aborted) - return; - if (ec) - { - JLOG(j_.trace()) << "SNTPClock::resolveComplete: " << ec.message(); - return; - } - - assert(it != ip::udp::resolver::iterator()); - - auto sel = it; - int i = 1; - - while (++it != ip::udp::resolver::iterator()) - { - if (rand_int(i++) == 0) - sel = it; - } - - if (sel != ip::udp::resolver::iterator()) - { - std::lock_guard lock(mutex_); - Query& query = queries_[*sel]; - using namespace std::chrono; - auto now = time_point_cast(clock_type::now()); - - if ((query.sent == now) || ((query.sent + 1s) == 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; - query.nonce = rand_int(); - // The following line of code will overflow at 2036-02-07 06:28:16 - // UTC - // due to the 32 bit cast. - reinterpret_cast( - SNTPQueryData)[NTP_OFF_XMITTS_INT] = - static_cast( - (time_point_cast(clock_type::now()) + - NTP_UNIX_OFFSET) - .time_since_epoch() - .count()); - reinterpret_cast( - SNTPQueryData)[NTP_OFF_XMITTS_FRAC] = query.nonce; - socket_.async_send_to( - buffer(SNTPQueryData, 48), - *sel, - std::bind( - &SNTPClientImp::onSend, - this, - std::placeholders::_1, - std::placeholders::_2)); - } - } - - void - onSend(error_code const& ec, std::size_t) - { - if (ec == boost::asio::error::operation_aborted) - return; - - if (ec) - { - JLOG(j_.warn()) << "SNTPClock::onSend: " << ec.message(); - return; - } - } - - void - processReply() - { - using namespace std::chrono; - assert(buf_.size() >= 48); - std::uint32_t* recvBuffer = - reinterpret_cast(&buf_.front()); - - unsigned info = ntohl(recvBuffer[NTP_OFF_INFO]); - auto timev = seconds{ntohl(recvBuffer[NTP_OFF_RECVTS_INT])}; - unsigned stratum = (info >> 16) & 0xff; - - if ((info >> 30) == 3) - { - JLOG(j_.info()) << "SNTP: Alarm condition " << ep_; - return; - } - - if ((stratum == 0) || (stratum > 14)) - { - JLOG(j_.info()) << "SNTP: Unreasonable stratum (" << stratum - << ") from " << ep_; - return; - } - - using namespace std::chrono; - auto now = time_point_cast(clock_type::now()); - timev -= now.time_since_epoch(); - timev -= NTP_UNIX_OFFSET; - - // add offset to list, replacing oldest one if appropriate - offsets_.push_back(timev); - - if (offsets_.size() >= NTP_SAMPLE_WINDOW) - offsets_.pop_front(); - - lastUpdate_ = now; - - // select median time - auto offsetList = offsets_; - std::sort(offsetList.begin(), offsetList.end()); - auto j = offsetList.size(); - auto it = std::next(offsetList.begin(), j / 2); - offset_ = *it; - - if ((j % 2) == 0) - offset_ = (offset_ + (*--it)) / 2; - - // debounce: small corrections likely - // do more harm than good - if ((offset_ == -1s) || (offset_ == 1s)) - offset_ = 0s; - - if (timev != 0s || offset_ != 0s) - { - JLOG(j_.trace()) << "SNTP: Offset is " << timev.count() - << ", new system offset is " << offset_.count(); - } - } -}; - -//------------------------------------------------------------------------------ - -std::unique_ptr -make_SNTPClock(beast::Journal j) -{ - return std::make_unique(j); -} - -} // namespace ripple diff --git a/src/ripple/core/impl/SNTPClock.h b/src/ripple/core/impl/SNTPClock.h deleted file mode 100644 index b63cac53da5..00000000000 --- a/src/ripple/core/impl/SNTPClock.h +++ /dev/null @@ -1,47 +0,0 @@ -//------------------------------------------------------------------------------ -/* - 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_NET_SNTPCLOCK_H_INCLUDED -#define RIPPLE_NET_SNTPCLOCK_H_INCLUDED - -#include -#include -#include -#include -#include -#include - -namespace ripple { - -/** A clock based on system_clock and adjusted for SNTP. */ -class SNTPClock : public beast::abstract_clock -{ -public: - virtual void - run(std::vector const& servers) = 0; - - virtual duration - offset() const = 0; -}; - -extern std::unique_ptr make_SNTPClock(beast::Journal); - -} // namespace ripple - -#endif diff --git a/src/ripple/core/impl/TimeKeeper.cpp b/src/ripple/core/impl/TimeKeeper.cpp deleted file mode 100644 index d1b07f53f44..00000000000 --- a/src/ripple/core/impl/TimeKeeper.cpp +++ /dev/null @@ -1,124 +0,0 @@ -//------------------------------------------------------------------------------ -/* - 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 -#include -#include -#include -#include - -namespace ripple { - -class TimeKeeperImpl : public TimeKeeper -{ -private: - beast::Journal const j_; - std::mutex mutable mutex_; - std::chrono::duration closeOffset_; - std::unique_ptr 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( - when.time_since_epoch() - days(10957))); - } - -public: - explicit TimeKeeperImpl(beast::Journal j) - : j_(j), closeOffset_{}, clock_(make_SNTPClock(j)) - { - } - - void - run(std::vector const& servers) override - { - clock_->run(servers); - } - - time_point - now() const override - { - std::lock_guard lock(mutex_); - return adjust(clock_->now()); - } - - time_point - closeTime() const override - { - std::lock_guard lock(mutex_); - return adjust(clock_->now()) + closeOffset_; - } - - void - adjustCloseTime(std::chrono::duration amount) override - { - using namespace std::chrono; - auto const s = amount.count(); - std::lock_guard 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_.warn()) << "TimeKeeper: Large close time offset = " - << closeOffset_.count(); - } - } - } - - std::chrono::duration - nowOffset() const override - { - using namespace std::chrono; - using namespace std; - lock_guard lock(mutex_); - return duration_cast>(clock_->offset()); - } - - std::chrono::duration - closeOffset() const override - { - std::lock_guard lock(mutex_); - return closeOffset_; - } -}; - -//------------------------------------------------------------------------------ - -std::unique_ptr -make_TimeKeeper(beast::Journal j) -{ - return std::make_unique(j); -} - -} // namespace ripple diff --git a/src/test/jtx/ManualTimeKeeper.h b/src/test/jtx/ManualTimeKeeper.h index 838f2c1398f..f3adb29b5f0 100644 --- a/src/test/jtx/ManualTimeKeeper.h +++ b/src/test/jtx/ManualTimeKeeper.h @@ -21,45 +21,30 @@ #define RIPPLE_TEST_MANUALTIMEKEEPER_H_INCLUDED #include -#include +#include namespace ripple { namespace test { class ManualTimeKeeper : public TimeKeeper { -public: - ManualTimeKeeper(); - - void - run(std::vector const& servers) override; +private: + std::atomic now_{}; - time_point - now() const override; +public: + ManualTimeKeeper() = default; time_point - closeTime() const override; + now() const override + { + return now_.load(); + } void - adjustCloseTime(std::chrono::duration amount) override; - - std::chrono::duration - nowOffset() const override; - - std::chrono::duration - closeOffset() const override; - - void - set(time_point now); - -private: - // Adjust system_clock::time_point for NetClock epoch - static time_point - adjust(std::chrono::system_clock::time_point when); - - std::mutex mutable mutex_; - std::chrono::duration closeOffset_; - time_point now_; + set(time_point now) + { + now_.store(now); + } }; } // namespace test diff --git a/src/test/jtx/impl/ManualTimeKeeper.cpp b/src/test/jtx/impl/ManualTimeKeeper.cpp deleted file mode 100644 index 72ceaa30bc0..00000000000 --- a/src/test/jtx/impl/ManualTimeKeeper.cpp +++ /dev/null @@ -1,95 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012-2015 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 - -namespace ripple { -namespace test { - -using namespace std::chrono_literals; - -ManualTimeKeeper::ManualTimeKeeper() : closeOffset_{}, now_(0s) -{ -} - -void -ManualTimeKeeper::run(std::vector const& servers) -{ -} - -auto -ManualTimeKeeper::now() const -> time_point -{ - std::lock_guard lock(mutex_); - return now_; -} - -auto -ManualTimeKeeper::closeTime() const -> time_point -{ - std::lock_guard lock(mutex_); - return now_ + closeOffset_; -} - -void -ManualTimeKeeper::adjustCloseTime(std::chrono::duration amount) -{ - // Copied from TimeKeeper::adjustCloseTime - using namespace std::chrono; - auto const s = amount.count(); - std::lock_guard 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; -} - -std::chrono::duration -ManualTimeKeeper::nowOffset() const -{ - return {}; -} - -std::chrono::duration -ManualTimeKeeper::closeOffset() const -{ - std::lock_guard lock(mutex_); - return closeOffset_; -} - -void -ManualTimeKeeper::set(time_point now) -{ - std::lock_guard lock(mutex_); - now_ = now; -} - -auto -ManualTimeKeeper::adjust(std::chrono::system_clock::time_point when) - -> time_point -{ - return time_point(std::chrono::duration_cast( - when.time_since_epoch() - days(10957))); -} -} // namespace test -} // namespace ripple diff --git a/src/test/overlay/compression_test.cpp b/src/test/overlay/compression_test.cpp index 3b61b2b3a09..81f21258e30 100644 --- a/src/test/overlay/compression_test.cpp +++ b/src/test/overlay/compression_test.cpp @@ -20,9 +20,9 @@ #include #include #include +#include #include #include -#include #include #include #include @@ -227,8 +227,7 @@ class compression_test : public beast::unit_test::suite auto transaction = std::make_shared(); transaction->set_rawtransaction(usdTxBlob); transaction->set_status(protocol::tsNEW); - auto tk = make_TimeKeeper(logs.journal("TimeKeeper")); - transaction->set_receivetimestamp(tk->now().time_since_epoch().count()); + transaction->set_receivetimestamp(rand_int()); transaction->set_deferred(true); return transaction; @@ -263,19 +262,23 @@ class compression_test : public beast::unit_test::suite ledgerData->set_error(protocol::TMReplyError::reNO_LEDGER); ledgerData->mutable_nodes()->Reserve(n); uint256 parentHash(0); + + NetClock::duration const resolution{10}; + NetClock::time_point ct{resolution}; + for (int i = 0; i < n; i++) { LedgerInfo info; - auto tk = make_TimeKeeper(logs.journal("TimeKeeper")); info.seq = i; - info.parentCloseTime = tk->now(); + info.parentCloseTime = ct; info.hash = ripple::sha512Half(i); info.txHash = ripple::sha512Half(i + 1); info.accountHash = ripple::sha512Half(i + 2); info.parentHash = parentHash; info.drops = XRPAmount(10); - info.closeTimeResolution = tk->now().time_since_epoch(); - info.closeTime = tk->now(); + info.closeTimeResolution = resolution; + info.closeTime = ct; + ct += resolution; parentHash = ledgerHash(info); Serializer nData; ripple::addRaw(info, nData); @@ -341,7 +344,7 @@ class compression_test : public beast::unit_test::suite Serializer s1; st.add(s1); list->set_signature(s1.data(), s1.size()); - list->set_blob(strHex(s.getString())); + list->set_blob(strHex(s.slice())); return list; } @@ -375,7 +378,7 @@ class compression_test : public beast::unit_test::suite st.add(s1); auto& blob = *list->add_blobs(); blob.set_signature(s1.data(), s1.size()); - blob.set_blob(strHex(s.getString())); + blob.set_blob(strHex(s.slice())); return list; }