diff --git a/src/addrdb.cpp b/src/addrdb.cpp index 74f249d2ec..301b61aa16 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -188,11 +188,11 @@ std::optional LoadAddrman(const NetGroupManager& netgroupman, con auto check_addrman = std::clamp(args.GetArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000); addrman = std::make_unique(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman); - int64_t nStart = GetTimeMillis(); + const auto start{SteadyClock::now()}; const auto path_addr{gArgs.GetDataDirNet() / "peers.dat"}; try { DeserializeFileDB(path_addr, *addrman, CLIENT_VERSION); - LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman->Size(), GetTimeMillis() - nStart); + LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman->Size(), Ticks(SteadyClock::now() - start)); } catch (const DbNotFoundError&) { // Addrman can be in an inconsistent state after failure, reset it addrman = std::make_unique(netgroupman, /*deterministic=*/false, /*consistency_check_ratio=*/check_addrman); diff --git a/src/addrman.cpp b/src/addrman.cpp index 52d36cd145..3a54ebbf70 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -14,10 +14,10 @@ #include #include #include -#include #include #include #include +#include #include #include @@ -29,19 +29,19 @@ static constexpr uint32_t ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP{64}; /** Maximum number of times an address can occur in the new table */ static constexpr int32_t ADDRMAN_NEW_BUCKETS_PER_ADDRESS{8}; /** How old addresses can maximally be */ -static constexpr int64_t ADDRMAN_HORIZON_DAYS{30}; +static constexpr auto ADDRMAN_HORIZON{30 * 24h}; /** After how many failed attempts we give up on a new node */ static constexpr int32_t ADDRMAN_RETRIES{3}; /** How many successive failures are allowed ... */ static constexpr int32_t ADDRMAN_MAX_FAILURES{10}; -/** ... in at least this many days */ -static constexpr int64_t ADDRMAN_MIN_FAIL_DAYS{7}; +/** ... in at least this duration */ +static constexpr auto ADDRMAN_MIN_FAIL{7 * 24h}; /** How recent a successful connection should be before we allow an address to be evicted from tried */ -static constexpr int64_t ADDRMAN_REPLACEMENT_HOURS{4}; +static constexpr auto ADDRMAN_REPLACEMENT{4h}; /** The maximum number of tried addr collisions to store */ static constexpr size_t ADDRMAN_SET_TRIED_COLLISION_SIZE{10}; -/** The maximum time we'll spend trying to resolve a tried table collision, in seconds */ -static constexpr int64_t ADDRMAN_TEST_WINDOW{40*60}; // 40 minutes +/** The maximum time we'll spend trying to resolve a tried table collision */ +static constexpr auto ADDRMAN_TEST_WINDOW{40min}; int AddrInfo::GetTriedBucket(const uint256& nKey, const NetGroupManager& netgroupman) const { @@ -64,36 +64,39 @@ int AddrInfo::GetBucketPosition(const uint256& nKey, bool fNew, int bucket) cons return hash1 % ADDRMAN_BUCKET_SIZE; } -bool AddrInfo::IsTerrible(int64_t nNow) const +bool AddrInfo::IsTerrible(NodeSeconds now) const { - if (nNow - nLastTry <= 60) { // never remove things tried in the last minute + if (now - m_last_try <= 1min) { // never remove things tried in the last minute return false; } - if (nTime > nNow + 10 * 60) // came in a flying DeLorean - return true; - - if (nNow - nTime > ADDRMAN_HORIZON_DAYS * 24 * 60 * 60) { // not seen in recent history + if (nTime > now + 10min) { // came in a flying DeLorean return true; } - if (nLastSuccess == 0 && nAttempts >= ADDRMAN_RETRIES) // tried N times and never a success + if (now - nTime > ADDRMAN_HORIZON) { // not seen in recent history return true; + } - if (nNow - nLastSuccess > ADDRMAN_MIN_FAIL_DAYS * 24 * 60 * 60 && nAttempts >= ADDRMAN_MAX_FAILURES) // N successive failures in the last week + if (TicksSinceEpoch(m_last_success) == 0 && nAttempts >= ADDRMAN_RETRIES) { // tried N times and never a success return true; + } + + if (now - m_last_success > ADDRMAN_MIN_FAIL && nAttempts >= ADDRMAN_MAX_FAILURES) { // N successive failures in the last week + return true; + } return false; } -double AddrInfo::GetChance(int64_t nNow) const +double AddrInfo::GetChance(NodeSeconds now) const { double fChance = 1.0; - int64_t nSinceLastTry = std::max(nNow - nLastTry, 0); // deprioritize very recent attempts away - if (nSinceLastTry < 60 * 10) + if (now - m_last_try < 10min) { fChance *= 0.01; + } // deprioritize 66% after each failed attempt, but at most 1/28th to avoid the search taking forever or overly penalizing outages. fChance *= pow(0.66, std::min(nAttempts, 8)); @@ -547,7 +550,7 @@ void AddrManImpl::MakeTried(AddrInfo& info, int nId) m_network_counts[info.GetNetwork()].n_tried++; } -bool AddrManImpl::AddSingle(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty) +bool AddrManImpl::AddSingle(const CAddress& addr, const CNetAddr& source, std::chrono::seconds time_penalty) { AssertLockHeld(cs); @@ -559,15 +562,15 @@ bool AddrManImpl::AddSingle(const CAddress& addr, const CNetAddr& source, int64_ // Do not set a penalty for a source's self-announcement if (addr == source) { - nTimePenalty = 0; + time_penalty = 0s; } if (pinfo) { // periodically update nTime - bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60); - int64_t nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60); - if (pinfo->nTime < addr.nTime - nUpdateInterval - nTimePenalty) { - pinfo->nTime = std::max((int64_t)0, addr.nTime - nTimePenalty); + const bool currently_online{NodeClock::now() - addr.nTime < 24h}; + const auto update_interval{currently_online ? 1h : 24h}; + if (pinfo->nTime < addr.nTime - update_interval - time_penalty) { + pinfo->nTime = std::max(NodeSeconds{0s}, addr.nTime - time_penalty); } // add services @@ -593,7 +596,7 @@ bool AddrManImpl::AddSingle(const CAddress& addr, const CNetAddr& source, int64_ } } else { pinfo = Create(addr, source, &nId); - pinfo->nTime = std::max((int64_t)0, (int64_t)pinfo->nTime - nTimePenalty); + pinfo->nTime = std::max(NodeSeconds{0s}, pinfo->nTime - time_penalty); } int nUBucket = pinfo->GetNewBucket(nKey, source, m_netgroupman); @@ -622,13 +625,13 @@ bool AddrManImpl::AddSingle(const CAddress& addr, const CNetAddr& source, int64_ return fInsert; } -bool AddrManImpl::Good_(const CService& addr, bool test_before_evict, int64_t nTime) +bool AddrManImpl::Good_(const CService& addr, bool test_before_evict, NodeSeconds time) { AssertLockHeld(cs); int nId; - nLastGood = nTime; + m_last_good = time; AddrInfo* pinfo = Find(addr, &nId); @@ -638,8 +641,8 @@ bool AddrManImpl::Good_(const CService& addr, bool test_before_evict, int64_t nT AddrInfo& info = *pinfo; // update info - info.nLastSuccess = nTime; - info.nLastTry = nTime; + info.m_last_success = time; + info.m_last_try = time; info.nAttempts = 0; // nTime is not updated here, to avoid leaking information about // currently-connected peers. @@ -680,11 +683,11 @@ bool AddrManImpl::Good_(const CService& addr, bool test_before_evict, int64_t nT } } -bool AddrManImpl::Add_(const std::vector &vAddr, const CNetAddr& source, int64_t nTimePenalty) +bool AddrManImpl::Add_(const std::vector& vAddr, const CNetAddr& source, std::chrono::seconds time_penalty) { int added{0}; for (std::vector::const_iterator it = vAddr.begin(); it != vAddr.end(); it++) { - added += AddSingle(*it, source, nTimePenalty) ? 1 : 0; + added += AddSingle(*it, source, time_penalty) ? 1 : 0; } if (added > 0) { LogPrint(BCLog::ADDRMAN, "Added %i addresses (of %i) from %s: %i tried, %i new\n", added, vAddr.size(), source.ToStringAddr(), nTried, nNew); @@ -692,7 +695,7 @@ bool AddrManImpl::Add_(const std::vector &vAddr, const CNetAddr& sourc return added > 0; } -void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime) +void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, NodeSeconds time) { AssertLockHeld(cs); @@ -705,14 +708,14 @@ void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, int64_t nTi AddrInfo& info = *pinfo; // update info - info.nLastTry = nTime; - if (fCountFailure && info.nLastCountAttempt < nLastGood) { - info.nLastCountAttempt = nTime; + info.m_last_try = time; + if (fCountFailure && info.m_last_count_attempt < m_last_good) { + info.m_last_count_attempt = time; info.nAttempts++; } } -std::pair AddrManImpl::Select_(bool new_only, std::optional network) const +std::pair AddrManImpl::Select_(bool new_only, std::optional network) const { AssertLockHeld(cs); @@ -780,7 +783,7 @@ std::pair AddrManImpl::Select_(bool new_only, std::optional AddrManImpl::GetAddr_(size_t max_addresses, size_t max_pct } // gather a list of random nodes, skipping those of low quality - const int64_t now{GetAdjustedTime()}; + const auto now{Now()}; std::vector addresses; for (unsigned int n = 0; n < vRandom.size(); n++) { if (addresses.size() >= nNodes) @@ -843,7 +846,7 @@ std::vector AddrManImpl::GetAddr_(size_t max_addresses, size_t max_pct return addresses; } -void AddrManImpl::Connected_(const CService& addr, int64_t nTime) +void AddrManImpl::Connected_(const CService& addr, NodeSeconds time) { AssertLockHeld(cs); @@ -856,9 +859,10 @@ void AddrManImpl::Connected_(const CService& addr, int64_t nTime) AddrInfo& info = *pinfo; // update info - int64_t nUpdateInterval = 20 * 60; - if (nTime - info.nTime > nUpdateInterval) - info.nTime = nTime; + const auto update_interval{20min}; + if (time - info.nTime > update_interval) { + info.nTime = time; + } } void AddrManImpl::SetServices_(const CService& addr, ServiceFlags nServices) @@ -914,22 +918,22 @@ void AddrManImpl::ResolveCollisions_() int id_old = vvTried[tried_bucket][tried_bucket_pos]; AddrInfo& info_old = mapInfo[id_old]; - const auto current_time{GetAdjustedTime()}; + const auto current_time{Now()}; // Has successfully connected in last X hours - if (current_time - info_old.nLastSuccess < ADDRMAN_REPLACEMENT_HOURS*(60*60)) { + if (current_time - info_old.m_last_success < ADDRMAN_REPLACEMENT) { erase_collision = true; - } else if (current_time - info_old.nLastTry < ADDRMAN_REPLACEMENT_HOURS*(60*60)) { // attempted to connect and failed in last X hours + } else if (current_time - info_old.m_last_try < ADDRMAN_REPLACEMENT) { // attempted to connect and failed in last X hours // Give address at least 60 seconds to successfully connect - if (current_time - info_old.nLastTry > 60) { + if (current_time - info_old.m_last_try > 60s) { LogPrint(BCLog::ADDRMAN, "Replacing %s with %s in tried table\n", info_old.ToStringAddrPort(), info_new.ToStringAddrPort()); // Replaces an existing address already in the tried table with the new address Good_(info_new, false, current_time); erase_collision = true; } - } else if (current_time - info_new.nLastSuccess > ADDRMAN_TEST_WINDOW) { + } else if (current_time - info_new.m_last_success > ADDRMAN_TEST_WINDOW) { // If the collision hasn't resolved in some reasonable amount of time, // just evict the old entry -- we must not be able to // connect to it for some reason. @@ -938,7 +942,7 @@ void AddrManImpl::ResolveCollisions_() erase_collision = true; } } else { // Collision is not actually a collision anymore - Good_(info_new, false, GetAdjustedTime()); + Good_(info_new, false, Now()); erase_collision = true; } } @@ -951,7 +955,7 @@ void AddrManImpl::ResolveCollisions_() } } -std::pair AddrManImpl::SelectTriedCollision_() +std::pair AddrManImpl::SelectTriedCollision_() { AssertLockHeld(cs); @@ -976,7 +980,7 @@ std::pair AddrManImpl::SelectTriedCollision_() int tried_bucket_pos = newInfo.GetBucketPosition(nKey, false, tried_bucket); const AddrInfo& info_old = mapInfo[vvTried[tried_bucket][tried_bucket_pos]]; - return {info_old, info_old.nLastTry}; + return {info_old, info_old.m_last_try}; } std::optional AddrManImpl::FindAddressEntry_(const CAddress& addr) @@ -1057,8 +1061,9 @@ int AddrManImpl::CheckAddrman() const int n = entry.first; const AddrInfo& info = entry.second; if (info.fInTried) { - if (!info.nLastSuccess) + if (!TicksSinceEpoch(info.m_last_success)) { return -1; + } if (info.nRefCount) return -2; setTried.insert(n); @@ -1077,10 +1082,12 @@ int AddrManImpl::CheckAddrman() const } if (info.nRandomPos < 0 || (size_t)info.nRandomPos >= vRandom.size() || vRandom[info.nRandomPos] != n) return -14; - if (info.nLastTry < 0) + if (info.m_last_try < NodeSeconds{0s}) { return -6; - if (info.nLastSuccess < 0) + } + if (info.m_last_success < NodeSeconds{0s}) { return -8; + } } if (setTried.size() != (size_t)nTried) @@ -1150,29 +1157,29 @@ size_t AddrManImpl::Size(std::optional net, std::optional in_new) return ret; } -bool AddrManImpl::Add(const std::vector& vAddr, const CNetAddr& source, int64_t nTimePenalty) +bool AddrManImpl::Add(const std::vector& vAddr, const CNetAddr& source, std::chrono::seconds time_penalty) { LOCK(cs); Check(); - auto ret = Add_(vAddr, source, nTimePenalty); + auto ret = Add_(vAddr, source, time_penalty); Check(); return ret; } -bool AddrManImpl::Good(const CService& addr, int64_t nTime) +bool AddrManImpl::Good(const CService& addr, NodeSeconds time) { LOCK(cs); Check(); - auto ret = Good_(addr, /*test_before_evict=*/true, nTime); + auto ret = Good_(addr, /*test_before_evict=*/true, time); Check(); return ret; } -void AddrManImpl::Attempt(const CService& addr, bool fCountFailure, int64_t nTime) +void AddrManImpl::Attempt(const CService& addr, bool fCountFailure, NodeSeconds time) { LOCK(cs); Check(); - Attempt_(addr, fCountFailure, nTime); + Attempt_(addr, fCountFailure, time); Check(); } @@ -1184,7 +1191,7 @@ void AddrManImpl::ResolveCollisions() Check(); } -std::pair AddrManImpl::SelectTriedCollision() +std::pair AddrManImpl::SelectTriedCollision() { LOCK(cs); Check(); @@ -1193,7 +1200,7 @@ std::pair AddrManImpl::SelectTriedCollision() return ret; } -std::pair AddrManImpl::Select(bool new_only, std::optional network) const +std::pair AddrManImpl::Select(bool new_only, std::optional network) const { LOCK(cs); Check(); @@ -1211,11 +1218,11 @@ std::vector AddrManImpl::GetAddr(size_t max_addresses, size_t max_pct, return addresses; } -void AddrManImpl::Connected(const CService& addr, int64_t nTime) +void AddrManImpl::Connected(const CService& addr, NodeSeconds time) { LOCK(cs); Check(); - Connected_(addr, nTime); + Connected_(addr, time); Check(); } @@ -1278,19 +1285,19 @@ size_t AddrMan::Size(std::optional net, std::optional in_new) con return m_impl->Size(net, in_new); } -bool AddrMan::Add(const std::vector& vAddr, const CNetAddr& source, int64_t nTimePenalty) +bool AddrMan::Add(const std::vector& vAddr, const CNetAddr& source, std::chrono::seconds time_penalty) { - return m_impl->Add(vAddr, source, nTimePenalty); + return m_impl->Add(vAddr, source, time_penalty); } -bool AddrMan::Good(const CService& addr, int64_t nTime) +bool AddrMan::Good(const CService& addr, NodeSeconds time) { - return m_impl->Good(addr, nTime); + return m_impl->Good(addr, time); } -void AddrMan::Attempt(const CService& addr, bool fCountFailure, int64_t nTime) +void AddrMan::Attempt(const CService& addr, bool fCountFailure, NodeSeconds time) { - m_impl->Attempt(addr, fCountFailure, nTime); + m_impl->Attempt(addr, fCountFailure, time); } void AddrMan::ResolveCollisions() @@ -1298,12 +1305,12 @@ void AddrMan::ResolveCollisions() m_impl->ResolveCollisions(); } -std::pair AddrMan::SelectTriedCollision() +std::pair AddrMan::SelectTriedCollision() { return m_impl->SelectTriedCollision(); } -std::pair AddrMan::Select(bool new_only, std::optional network) const +std::pair AddrMan::Select(bool new_only, std::optional network) const { return m_impl->Select(new_only, network); } @@ -1313,9 +1320,9 @@ std::vector AddrMan::GetAddr(size_t max_addresses, size_t max_pct, std return m_impl->GetAddr(max_addresses, max_pct, network); } -void AddrMan::Connected(const CService& addr, int64_t nTime) +void AddrMan::Connected(const CService& addr, NodeSeconds time) { - m_impl->Connected(addr, nTime); + m_impl->Connected(addr, time); } void AddrMan::SetServices(const CService& addr, ServiceFlags nServices) diff --git a/src/addrman.h b/src/addrman.h index b432e6afb4..d927232b2b 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include @@ -124,23 +124,23 @@ public: * * @param[in] vAddr Address records to attempt to add. * @param[in] source The address of the node that sent us these addr records. - * @param[in] nTimePenalty A "time penalty" to apply to the address record's nTime. If a peer + * @param[in] time_penalty A "time penalty" to apply to the address record's nTime. If a peer * sends us an address record with nTime=n, then we'll add it to our - * addrman with nTime=(n - nTimePenalty). + * addrman with nTime=(n - time_penalty). * @return true if at least one address is successfully added. */ - bool Add(const std::vector& vAddr, const CNetAddr& source, int64_t nTimePenalty = 0); + bool Add(const std::vector& vAddr, const CNetAddr& source, std::chrono::seconds time_penalty = 0s); /** * Mark an address record as accessible and attempt to move it to addrman's tried table. * * @param[in] addr Address record to attempt to move to tried table. - * @param[in] nTime The time that we were last connected to this peer. + * @param[in] time The time that we were last connected to this peer. * @return true if the address is successfully moved from the new table to the tried table. */ - bool Good(const CService& addr, int64_t nTime = GetAdjustedTime()); + bool Good(const CService& addr, NodeSeconds time = Now()); //! Mark an entry as connection attempted to. - void Attempt(const CService& addr, bool fCountFailure, int64_t nTime = GetAdjustedTime()); + void Attempt(const CService& addr, bool fCountFailure, NodeSeconds time = Now()); //! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions. void ResolveCollisions(); @@ -150,9 +150,9 @@ public: * attempting to evict. * * @return CAddress The record for the selected tried peer. - * int64_t The last time we attempted to connect to that peer. + * seconds The last time we attempted to connect to that peer. */ - std::pair SelectTriedCollision(); + std::pair SelectTriedCollision(); /** * Choose an address to connect to. @@ -164,9 +164,9 @@ public: * @param[in] network Select only addresses of this network (nullopt = all). Passing a network may * slow down the search. * @return CAddress The record for the selected peer. - * int64_t The last time we attempted to connect to that peer. + * seconds The last time we attempted to connect to that peer. */ - std::pair Select(bool new_only = false, std::optional network = std::nullopt) const; + std::pair Select(bool new_only = false, std::optional network = std::nullopt) const; /** * Return all or many randomly selected addresses, optionally by network. @@ -188,9 +188,9 @@ public: * not leak information about currently connected peers. * * @param[in] addr The address of the peer we were connected to - * @param[in] nTime The time that we were last connected to this peer + * @param[in] time The time that we were last connected to this peer */ - void Connected(const CService& addr, int64_t nTime = GetAdjustedTime()); + void Connected(const CService& addr, NodeSeconds time = Now()); //! Update an entry's service bits. void SetServices(const CService& addr, ServiceFlags nServices); diff --git a/src/addrman_impl.h b/src/addrman_impl.h index a1bf2991f9..08c597e667 100644 --- a/src/addrman_impl.h +++ b/src/addrman_impl.h @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include #include @@ -38,16 +40,16 @@ class AddrInfo : public CAddress { public: //! last try whatsoever by us (memory only) - int64_t nLastTry{0}; + NodeSeconds m_last_try{0s}; //! last counted attempt (memory only) - int64_t nLastCountAttempt{0}; + NodeSeconds m_last_count_attempt{0s}; //! where knowledge about this address first came from CNetAddr source; //! last successful connection by us - int64_t nLastSuccess{0}; + NodeSeconds m_last_success{0s}; //! connection attempts since last successful attempt int nAttempts{0}; @@ -64,7 +66,7 @@ public: SERIALIZE_METHODS(AddrInfo, obj) { READWRITEAS(CAddress, obj); - READWRITE(obj.source, obj.nLastSuccess, obj.nAttempts); + READWRITE(obj.source, Using>(obj.m_last_success), obj.nAttempts); } AddrInfo(const CAddress &addrIn, const CNetAddr &addrSource) : CAddress(addrIn), source(addrSource) @@ -91,10 +93,10 @@ public: int GetBucketPosition(const uint256 &nKey, bool fNew, int bucket) const; //! Determine whether the statistics about this entry are bad enough so that it can just be deleted - bool IsTerrible(int64_t nNow = GetAdjustedTime()) const; + bool IsTerrible(NodeSeconds now = Now()) const; //! Calculate the relative chance this entry should be given when selecting nodes to connect to - double GetChance(int64_t nNow = GetAdjustedTime()) const; + double GetChance(NodeSeconds now = Now()) const; }; class AddrManImpl @@ -112,26 +114,26 @@ public: size_t Size(std::optional net, std::optional in_new) const EXCLUSIVE_LOCKS_REQUIRED(!cs); - bool Add(const std::vector& vAddr, const CNetAddr& source, int64_t nTimePenalty) + bool Add(const std::vector& vAddr, const CNetAddr& source, std::chrono::seconds time_penalty) EXCLUSIVE_LOCKS_REQUIRED(!cs); - bool Good(const CService& addr, int64_t nTime) + bool Good(const CService& addr, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(!cs); - void Attempt(const CService& addr, bool fCountFailure, int64_t nTime) + void Attempt(const CService& addr, bool fCountFailure, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(!cs); void ResolveCollisions() EXCLUSIVE_LOCKS_REQUIRED(!cs); - std::pair SelectTriedCollision() EXCLUSIVE_LOCKS_REQUIRED(!cs); + std::pair SelectTriedCollision() EXCLUSIVE_LOCKS_REQUIRED(!cs); - std::pair Select(bool new_only, std::optional network) const + std::pair Select(bool new_only, std::optional network) const EXCLUSIVE_LOCKS_REQUIRED(!cs); std::vector GetAddr(size_t max_addresses, size_t max_pct, std::optional network) const EXCLUSIVE_LOCKS_REQUIRED(!cs); - void Connected(const CService& addr, int64_t nTime) + void Connected(const CService& addr, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(!cs); void SetServices(const CService& addr, ServiceFlags nServices) @@ -205,7 +207,7 @@ private: int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE] GUARDED_BY(cs); //! last time Good was called (memory only). Initially set to 1 so that "never" is strictly worse. - int64_t nLastGood GUARDED_BY(cs){1}; + NodeSeconds m_last_good GUARDED_BY(cs){1s}; //! Holds addrs inserted into tried table that collide with existing entries. Test-before-evict discipline used to resolve these collisions. std::set m_tried_collisions; @@ -244,15 +246,15 @@ private: /** Attempt to add a single address to addrman's new table. * @see AddrMan::Add() for parameters. */ - bool AddSingle(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs); + bool AddSingle(const CAddress& addr, const CNetAddr& source, std::chrono::seconds time_penalty) EXCLUSIVE_LOCKS_REQUIRED(cs); - bool Good_(const CService& addr, bool test_before_evict, int64_t time) EXCLUSIVE_LOCKS_REQUIRED(cs); + bool Good_(const CService& addr, bool test_before_evict, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(cs); - bool Add_(const std::vector &vAddr, const CNetAddr& source, int64_t nTimePenalty) EXCLUSIVE_LOCKS_REQUIRED(cs); + bool Add_(const std::vector& vAddr, const CNetAddr& source, std::chrono::seconds time_penalty) EXCLUSIVE_LOCKS_REQUIRED(cs); - void Attempt_(const CService& addr, bool fCountFailure, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs); + void Attempt_(const CService& addr, bool fCountFailure, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(cs); - std::pair Select_(bool new_only, std::optional network) const EXCLUSIVE_LOCKS_REQUIRED(cs); + std::pair Select_(bool new_only, std::optional network) const EXCLUSIVE_LOCKS_REQUIRED(cs); /** Helper to generalize looking up an addrman entry from either table. * @@ -262,7 +264,7 @@ private: std::vector GetAddr_(size_t max_addresses, size_t max_pct, std::optional network) const EXCLUSIVE_LOCKS_REQUIRED(cs); - void Connected_(const CService& addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs); + void Connected_(const CService& addr, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(cs); void SetServices_(const CService& addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs); @@ -270,7 +272,7 @@ private: void ResolveCollisions_() EXCLUSIVE_LOCKS_REQUIRED(cs); - std::pair SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs); + std::pair SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs); std::optional FindAddressEntry_(const CAddress& addr) EXCLUSIVE_LOCKS_REQUIRED(cs); diff --git a/src/banman.cpp b/src/banman.cpp index 2bf3949c27..c162eb08fd 100644 --- a/src/banman.cpp +++ b/src/banman.cpp @@ -31,12 +31,12 @@ void BanMan::LoadBanlist() if (m_client_interface) m_client_interface->InitMessage(_("Loading banlist…").translated); - int64_t n_start = GetTimeMillis(); + const auto start{SteadyClock::now()}; if (m_ban_db.Read(m_banned)) { SweepBanned(); // sweep out unused entries LogPrint(BCLog::NET, "Loaded %d banned node addresses/subnets %dms\n", m_banned.size(), - GetTimeMillis() - n_start); + Ticks(SteadyClock::now() - start)); } else { LogPrintf("Recreating the banlist database\n"); m_banned = {}; @@ -58,13 +58,13 @@ void BanMan::DumpBanlist() SetBannedSetDirty(false); } - int64_t n_start = GetTimeMillis(); + const auto start{SteadyClock::now()}; if (!m_ban_db.Write(banmap)) { SetBannedSetDirty(true); } LogPrint(BCLog::NET, "Flushed %d banned node addresses/subnets to disk %dms\n", banmap.size(), - GetTimeMillis() - n_start); + Ticks(SteadyClock::now() - start)); } void BanMan::ClearBanned() diff --git a/src/bench/addrman.cpp b/src/bench/addrman.cpp index aaaf671014..575f90766a 100644 --- a/src/bench/addrman.cpp +++ b/src/bench/addrman.cpp @@ -42,7 +42,7 @@ static void CreateAddresses() CAddress ret(CService(addr, port), NODE_NETWORK); - ret.nTime = GetAdjustedTime(); + ret.nTime = Now(); return ret; }; @@ -118,7 +118,7 @@ static void AddrManSelectByNetwork(benchmark::Bench& bench) CService i2p_service; i2p_service.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p"); CAddress i2p_address(i2p_service, NODE_NONE); - i2p_address.nTime = GetAdjustedTime(); + i2p_address.nTime = Now(); const CNetAddr source{LookupHost("252.2.2.2", false).value()}; addrman.Add({i2p_address}, source); diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 29c4c7b9be..045cf0ae76 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -18,17 +19,19 @@ #include #include #include +#include #include #include #include #include #include +#include #include +#include #include #include #include -#include #include #include @@ -40,8 +43,10 @@ #include #include -#include -#include +// The server returns time values from a mockable system clock, but it is not +// trivial to get the mocked time from the server, nor is it needed for now, so +// just use a plain system_clock. +using CliClock = std::chrono::system_clock; const std::function G_TRANSLATION_FUN = nullptr; UrlDecodeFn* const URL_DECODE = urlDecode; @@ -454,7 +459,6 @@ private: if (conn_type == "addr-fetch") return "addr"; return ""; } - const int64_t m_time_now{GetTimeSeconds()}; public: static constexpr int ID_PEERINFO = 0; @@ -486,6 +490,7 @@ public: if (networkinfo["version"].get_int() < 200000) { throw std::runtime_error("-netinfo requires dashd server to be running v20.0 and up"); } + const int64_t time_now{TicksSinceEpoch(CliClock::now())}; // Count peer connection totals, and if DetailsRequested(), store peer data in a vector of structs. for (const UniValue& peer : batch[ID_PEERINFO]["result"].getValues()) { @@ -516,7 +521,7 @@ public: const double min_ping{peer["minping"].isNull() ? -1 : peer["minping"].get_real()}; const double ping{peer["pingtime"].isNull() ? -1 : peer["pingtime"].get_real()}; const std::string addr{peer["addr"].get_str()}; - const std::string age{conn_time == 0 ? "" : ToString((m_time_now - conn_time) / 60)}; + const std::string age{conn_time == 0 ? "" : ToString((time_now - conn_time) / 60)}; const std::string sub_version{peer["subver"].get_str()}; const std::string transport{peer["transport_protocol_type"].isNull() ? "v1" : peer["transport_protocol_type"].get_str()}; const bool is_addr_relay_enabled{peer["addr_relay_enabled"].isNull() ? false : peer["addr_relay_enabled"].get_bool()}; @@ -554,10 +559,10 @@ public: peer.transport_protocol_type.rfind('v', 0) == 0 ? peer.transport_protocol_type[1] : ' ', PingTimeToString(peer.min_ping), PingTimeToString(peer.ping), - peer.last_send ? ToString(m_time_now - peer.last_send) : "", - peer.last_recv ? ToString(m_time_now - peer.last_recv) : "", - peer.last_trxn ? ToString((m_time_now - peer.last_trxn) / 60) : peer.is_block_relay ? "*" : "", - peer.last_blck ? ToString((m_time_now - peer.last_blck) / 60) : "", + peer.last_send ? ToString(time_now - peer.last_send) : "", + peer.last_recv ? ToString(time_now - peer.last_recv) : "", + peer.last_trxn ? ToString((time_now - peer.last_trxn) / 60) : peer.is_block_relay ? "*" : "", + peer.last_blck ? ToString((time_now - peer.last_blck) / 60) : "", strprintf("%s%s", peer.is_bip152_hb_to ? "." : " ", peer.is_bip152_hb_from ? "*" : " "), m_max_addr_processed_length, // variable spacing peer.addr_processed ? ToString(peer.addr_processed) : peer.is_addr_relay_enabled ? "" : ".", @@ -869,7 +874,7 @@ static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& str // Execute and handle connection failures with -rpcwait. const bool fWait = gArgs.GetBoolArg("-rpcwait", false); const int timeout = gArgs.GetArg("-rpcwaittimeout", DEFAULT_WAIT_CLIENT_TIMEOUT); - const auto deadline{GetTime() + 1s * timeout}; + const auto deadline{std::chrono::steady_clock::now() + 1s * timeout}; do { try { @@ -882,8 +887,7 @@ static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& str } break; // Connection succeeded, no need to retry. } catch (const CConnectionFailed& e) { - const auto now{GetTime()}; - if (fWait && (timeout <= 0 || now < deadline)) { + if (fWait && (timeout <= 0 || std::chrono::steady_clock::now() < deadline)) { UninterruptibleSleep(1s); } else { throw CConnectionFailed(strprintf("timeout on transient error: %s", e.what())); diff --git a/src/flat-database.h b/src/flat-database.h index db9dad1939..7e921f8b96 100644 --- a/src/flat-database.h +++ b/src/flat-database.h @@ -39,7 +39,7 @@ private: { // LOCK(objToSave.cs); - int64_t nStart = GetTimeMillis(); + const auto start{SteadyClock::now()}; // serialize, checksum data up to that point, then append checksum CDataStream ssObj(SER_DISK, CLIENT_VERSION); @@ -65,7 +65,7 @@ private: } fileout.fclose(); - LogPrintf("Written info to %s %dms\n", strFilename, GetTimeMillis() - nStart); + LogPrintf("Written info to %s %dms\n", strFilename, Ticks(SteadyClock::now() - start)); LogPrintf(" %s\n", objToSave.ToString()); return true; @@ -75,7 +75,7 @@ private: { //LOCK(objToLoad.cs); - int64_t nStart = GetTimeMillis(); + const auto start{SteadyClock::now()}; // open input file, and associate with CAutoFile FILE *file = fsbridge::fopen(pathDB, "rb"); CAutoFile filein(file, SER_DISK, CLIENT_VERSION); @@ -149,7 +149,7 @@ private: return ReadResult::IncorrectFormat; } - LogPrintf("Loaded info from %s %dms\n", strFilename, GetTimeMillis() - nStart); + LogPrintf("Loaded info from %s %dms\n", strFilename, Ticks(SteadyClock::now() - start)); LogPrintf(" %s\n", objToLoad.ToString()); return ReadResult::Ok; @@ -193,11 +193,11 @@ public: T tmpObjToLoad; if (!Read(tmpObjToLoad)) return false; - int64_t nStart = GetTimeMillis(); + const auto start{SteadyClock::now()}; LogPrintf("Writing info to %s...\n", strFilename); const bool ret = CoreWrite(objToSave); - LogPrintf("%s dump finished %dms\n", strFilename, GetTimeMillis() - nStart); + LogPrintf("%s dump finished %dms\n", strFilename, Ticks(SteadyClock::now() - start)); return ret; } diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index deea85ce26..99465fa24e 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -1432,11 +1432,12 @@ void CGovernanceManager::AddCachedTriggers() void CGovernanceManager::InitOnLoad() { LOCK(cs); - int64_t nStart = GetTimeMillis(); + const auto start{SteadyClock::now()}; LogPrintf("Preparing masternode indexes and governance triggers...\n"); RebuildIndexes(); AddCachedTriggers(); - LogPrintf("Masternode indexes and governance triggers prepared %dms\n", GetTimeMillis() - nStart); + LogPrintf("Masternode indexes and governance triggers prepared %dms\n", + Ticks(SteadyClock::now() - start)); LogPrintf(" %s\n", ToString()); } diff --git a/src/init.cpp b/src/init.cpp index ef6fb6a82e..147f845859 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1870,7 +1870,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) do { bool failed_verification = false; - const int64_t load_block_index_start_time = GetTimeMillis(); + const auto load_block_index_start_time{SteadyClock::now()}; try { LOCK(cs_main); @@ -2131,7 +2131,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) if (!failed_verification) { fLoaded = true; - LogPrintf(" block index %15dms\n", GetTimeMillis() - load_block_index_start_time); + LogPrintf(" block index %15dms\n", Ticks(SteadyClock::now() - load_block_index_start_time)); } } while(false); diff --git a/src/net.cpp b/src/net.cpp index 91eb45fc83..db234f4876 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -232,7 +232,7 @@ static std::vector ConvertSeeds(const std::vector &vSeedsIn) // it'll get a pile of addresses with newer timestamps. // Seed nodes are given a random 'last seen time' of between one and two // weeks ago. - const int64_t nOneWeek = 7*24*60*60; + const auto one_week{7 * 24h}; std::vector vSeedsOut; FastRandomContext rng; CDataStream s(vSeedsIn, SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT); @@ -240,7 +240,7 @@ static std::vector ConvertSeeds(const std::vector &vSeedsIn) CService endpoint; s >> endpoint; CAddress addr{endpoint, GetDesirableServiceFlags(NODE_NONE)}; - addr.nTime = GetTime() - rng.randrange(nOneWeek) - nOneWeek; + addr.nTime = rng.rand_uniform_delay(Now() - one_week, -one_week); LogPrint(BCLog::NET, "Added hardcoded seed: %s\n", addr.ToStringAddrPort()); vSeedsOut.push_back(addr); } @@ -512,11 +512,11 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo LogPrintLevel(BCLog::NET, BCLog::Level::Debug, "trying %s connection %s lastseen=%.1fhrs\n", use_v2transport ? "v2" : "v1", pszDest ? pszDest : addrConnect.ToStringAddrPort(), - pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); + Ticks(pszDest ? 0h : Now() - addrConnect.nTime)); } else { LogPrintLevel(BCLog::NET, BCLog::Level::Debug, "trying %s connection lastseen=%.1fhrs\n", use_v2transport ? "v2" : "v1", - pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); + Ticks(pszDest ? 0h : Now() - addrConnect.nTime)); } // Resolve @@ -1901,7 +1901,7 @@ bool CConnman::AttemptToEvictConnection() // follow when the incoming connection is from another masternode. When a message other than MNAUTH // is received after VERSION/VERACK, the protection is lifted immediately. bool isProtected = GetTime() - node->m_connected < INBOUND_EVICTION_PROTECTION_TIME; - if (node->nTimeFirstMessageReceived != 0 && !node->fFirstMessageIsMNAUTH) { + if (node->nTimeFirstMessageReceived.load() != 0s && !node->fFirstMessageIsMNAUTH) { isProtected = false; } // if MNAUTH was valid, the node is always protected (and at the same time not accounted when @@ -3080,9 +3080,8 @@ void CConnman::ThreadDNSAddressSeed() const auto addresses{LookupHost(host, nMaxIPs, true)}; if (!addresses.empty()) { for (const CNetAddr& ip : addresses) { - int nOneDay = 24*3600; CAddress addr = CAddress(CService(ip, Params().GetDefaultPort()), requiredServiceBits); - addr.nTime = GetTime() - 3*nOneDay - rng.randrange(4*nOneDay); // use a random age between 3 and 7 days old + addr.nTime = rng.rand_uniform_delay(Now() - 3 * 24h, -4 * 24h); // use a random age between 3 and 7 days old vAdd.push_back(addr); found++; } @@ -3101,12 +3100,12 @@ void CConnman::ThreadDNSAddressSeed() void CConnman::DumpAddresses() { - int64_t nStart = GetTimeMillis(); + const auto start{SteadyClock::now()}; DumpPeerAddresses(::gArgs, addrman); LogPrint(BCLog::NET, "Flushed %d addresses to peers.dat %dms\n", - addrman.Size(), GetTimeMillis() - nStart); + addrman.Size(), Ticks(SteadyClock::now() - start)); } void CConnman::ProcessAddrFetch() @@ -3451,7 +3450,7 @@ void CConnman::ThreadOpenConnections(const std::vector connect, CDe auto mnList = dmnman.GetListAtChainTip(); - int64_t nANow = GetAdjustedTime(); + const auto current_time{NodeClock::now()}; int nTries = 0; while (!interruptNet) { @@ -3474,7 +3473,7 @@ void CConnman::ThreadOpenConnections(const std::vector connect, CDe break; CAddress addr; - int64_t addr_last_try{0}; + NodeSeconds addr_last_try{0s}; if (fFeeler) { // First, try to get a tried table collision address. This returns @@ -3530,8 +3529,9 @@ void CConnman::ThreadOpenConnections(const std::vector connect, CDe if (onion_only && !addr.IsTor()) continue; // only consider very recently tried nodes after 30 failed attempts - if (nANow - addr_last_try < 600 && nTries < 30) + if (current_time - addr_last_try < 10min && nTries < 30) { continue; + } // for non-feelers, require all the services we'll want, // for feelers, only require they be a full node (only because most @@ -3754,7 +3754,7 @@ void CConnman::ThreadOpenMasternodeConnections(CDeterministicMNManager& dmnman, // we probably connected to it before it became a masternode // or maybe we are still waiting for mnauth (void)ForNode(addr2, [&](CNode* pnode) { - if (pnode->nTimeFirstMessageReceived != 0 && GetTimeSeconds() - pnode->nTimeFirstMessageReceived > 5) { + if (pnode->nTimeFirstMessageReceived.load() != 0s && GetTime() - pnode->nTimeFirstMessageReceived.load() > 5s) { // clearly not expecting mnauth to take that long even if it wasn't the first message // we received (as it should normally), disconnect LogPrint(BCLog::NET_NETCONN, "CConnman::%s -- dropping non-mnauth connection to %s, service=%s\n", _func_, proRegTxHash.ToString(), addr2.ToStringAddrPort()); diff --git a/src/net.h b/src/net.h index 9085771b53..cf0469ff3f 100644 --- a/src/net.h +++ b/src/net.h @@ -766,7 +766,7 @@ public: const std::chrono::seconds m_connected; std::atomic nTimeOffset{0}; std::atomic nLastWarningTime{0}; - std::atomic nTimeFirstMessageReceived{0}; + std::atomic nTimeFirstMessageReceived{0s}; std::atomic fFirstMessageIsMNAUTH{false}; // Address of this peer const CAddress addr; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 6b8f7f875d..da550cc066 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -156,6 +157,8 @@ static const unsigned int NODE_NETWORK_LIMITED_MIN_BLOCKS = 288; static constexpr auto AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL{24h}; /** Average delay between peer address broadcasts */ static constexpr auto AVG_ADDRESS_BROADCAST_INTERVAL{30s}; +/** Delay between rotating the peers we relay a particular address to */ +static constexpr auto ROTATE_ADDR_RELAY_DEST_INTERVAL{24h}; /** Average delay between trickled inventory transmissions for inbound peers. * Blocks and peers with NetPermissionFlags::NoBan permission bypass this. */ static constexpr auto INBOUND_INVENTORY_BROADCAST_INTERVAL{5s}; @@ -2462,7 +2465,10 @@ void PeerManagerImpl::RelayAddress(NodeId originator, // Use deterministic randomness to send to the same nodes for 24 hours // at a time so the m_addr_knowns of the chosen nodes prevent repeats const uint64_t hashAddr{addr.GetHash()}; - const CSipHasher hasher{m_connman.GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY).Write(hashAddr).Write((GetTime() + hashAddr) / (24 * 60 * 60))}; + const auto current_time{GetTime()}; + // Adding address hash makes exact rotation time different per address, while preserving periodicity. + const uint64_t time_addr{(static_cast(count_seconds(current_time)) + hashAddr) / count_seconds(ROTATE_ADDR_RELAY_DEST_INTERVAL)}; + const CSipHasher hasher{m_connman.GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY).Write(hashAddr).Write(time_addr)}; FastRandomContext insecure_rand; // Relay reachable addresses to 2 peers. Unreachable addresses are relayed randomly to 1 or 2 peers. @@ -3690,7 +3696,7 @@ void PeerManagerImpl::ProcessMessage( // indicate to the peer that we will participate in addr relay. if (fListen && !m_chainman.ActiveChainstate().IsInitialBlockDownload()) { - CAddress addr{GetLocalAddress(pfrom), peer->m_our_services, (uint32_t)GetAdjustedTime()}; + CAddress addr{GetLocalAddress(pfrom), peer->m_our_services, Now()}; FastRandomContext insecure_rand; if (addr.IsRoutable()) { @@ -3931,9 +3937,9 @@ void PeerManagerImpl::ProcessMessage( return; } - if (pfrom.nTimeFirstMessageReceived == 0) { + if (pfrom.nTimeFirstMessageReceived.load() == 0s) { // First message after VERSION/VERACK - pfrom.nTimeFirstMessageReceived = GetTimeSeconds(); + pfrom.nTimeFirstMessageReceived = GetTime(); pfrom.fFirstMessageIsMNAUTH = msg_type == NetMsgType::MNAUTH; // Note: do not break the flow here @@ -3977,8 +3983,7 @@ void PeerManagerImpl::ProcessMessage( // Store the new addresses std::vector vAddrOk; - int64_t nNow = GetAdjustedTime(); - int64_t nSince = nNow - 10 * 60; + const auto current_a_time{Now()}; // Update/increment addr rate limiting bucket. const auto current_time{GetTime()}; @@ -4014,8 +4019,9 @@ void PeerManagerImpl::ProcessMessage( if (!MayHaveUsefulAddressDB(addr.nServices) && !HasAllDesirableServiceFlags(addr.nServices)) continue; - if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) - addr.nTime = nNow - 5 * 24 * 60 * 60; + if (addr.nTime <= NodeSeconds{100000000s} || addr.nTime > current_a_time + 10min) { + addr.nTime = current_a_time - 5 * 24h; + } AddAddressKnown(*peer, addr); if (m_banman && (m_banman->IsDiscouraged(addr) || m_banman->IsBanned(addr))) { // Do not process banned/discouraged addresses beyond remembering we received them @@ -4023,7 +4029,7 @@ void PeerManagerImpl::ProcessMessage( } ++num_proc; bool fReachable = IsReachable(addr); - if (addr.nTime > nSince && !peer->m_getaddr_sent && vAddr.size() <= 10 && addr.IsRoutable()) { + if (addr.nTime > current_a_time - 10min && !peer->m_getaddr_sent && vAddr.size() <= 10 && addr.IsRoutable()) { // Relay to a limited number of other nodes RelayAddress(pfrom.GetId(), addr, fReachable); } @@ -4036,7 +4042,7 @@ void PeerManagerImpl::ProcessMessage( LogPrint(BCLog::NET, "Received addr: %u addresses (%u processed, %u rate-limited) from peer=%d\n", vAddr.size(), num_proc, num_rate_limit, pfrom.GetId()); - m_addrman.Add(vAddrOk, pfrom.addr, 2 * 60 * 60); + m_addrman.Add(vAddrOk, pfrom.addr, 2h); if (vAddr.size() < 1000) peer->m_getaddr_sent = false; // AddrFetch: Require multiple addresses to avoid disconnecting on self-announcements @@ -5662,7 +5668,7 @@ void PeerManagerImpl::MaybeSendAddr(CNode& node, Peer& peer, std::chrono::micros peer.m_addr_known->reset(); } if (std::optional local_service = GetLocalAddrForPeer(node)) { - CAddress local_addr{*local_service, peer.m_our_services, (uint32_t)GetAdjustedTime()}; + CAddress local_addr{*local_service, peer.m_our_services, Now()}; FastRandomContext insecure_rand; PushAddress(peer, local_addr, insecure_rand); } diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index ed1e8005e8..d184be669f 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -908,13 +908,13 @@ void ThreadImport(ChainstateManager& chainman, CDeterministicMNManager& dmnman, // and reduce further locking overhead for cs_main in other parts of code including GUI LogPrintf("Filling coin cache with masternode UTXOs...\n"); LOCK(cs_main); - int64_t nStart = GetTimeMillis(); + const auto start{SteadyClock::now()}; auto mnList = dmnman.GetListAtChainTip(); mnList.ForEachMN(false, [&](auto& dmn) { Coin coin; GetUTXOCoin(chainman.ActiveChainstate(), dmn.collateralOutpoint, coin); }); - LogPrintf("Filling coin cache with masternode UTXOs: done in %dms\n", GetTimeMillis() - nStart); + LogPrintf("Filling coin cache with masternode UTXOs: done in %dms\n", Ticks(SteadyClock::now() - start)); } if (mn_activeman != nullptr) { diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 6e7b646eff..ea4bce94d6 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -43,7 +43,6 @@ #include #include #include -#include #include #include #include diff --git a/src/protocol.h b/src/protocol.h index 9a9f1cb4ca..7da040e0df 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -3,10 +3,6 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef __cplusplus -#error This header can only be compiled as C++. -#endif - #ifndef BITCOIN_PROTOCOL_H #define BITCOIN_PROTOCOL_H @@ -15,13 +11,11 @@ #include #include #include -#include - #include +#include - +#include #include -#include #include /** Message header. @@ -404,7 +398,7 @@ static inline bool MayHaveUsefulAddressDB(ServiceFlags services) /** A CService with information about it as peer */ class CAddress : public CService { - static constexpr uint32_t TIME_INIT{100000000}; + static constexpr std::chrono::seconds TIME_INIT{100000000}; /** Historically, CAddress disk serialization stored the CLIENT_VERSION, optionally OR'ed with * the ADDRV2_FORMAT flag to indicate V2 serialization. The first field has since been @@ -434,7 +428,7 @@ class CAddress : public CService public: CAddress() : CService{} {}; explicit CAddress(CService ipIn, ServiceFlags nServicesIn) : CService{ipIn}, nServices{nServicesIn} {}; - CAddress(CService ipIn, ServiceFlags nServicesIn, uint32_t nTimeIn) : CService{ipIn}, nTime{nTimeIn}, nServices{nServicesIn} {}; + CAddress(CService ipIn, ServiceFlags nServicesIn, NodeSeconds time) : CService{ipIn}, nTime{time}, nServices{nServicesIn} {}; SERIALIZE_METHODS(CAddress, obj) { @@ -467,8 +461,7 @@ public: use_v2 = s.GetVersion() & ADDRV2_FORMAT; } - SER_READ(obj, obj.nTime = TIME_INIT); - READWRITE(obj.nTime); + READWRITE(Using>(obj.nTime)); // nServices is serialized as CompactSize in V2; as uint64_t in V1. if (use_v2) { uint64_t services_tmp; @@ -483,8 +476,8 @@ public: SerReadWriteMany(os, ser_action, ReadWriteAsHelper(obj)); } - //! Always included in serialization. - uint32_t nTime{TIME_INIT}; + //! Always included in serialization. The behavior is unspecified if the value is not representable as uint32_t. + NodeSeconds nTime{TIME_INIT}; //! Serialized as uint64_t in V1, and as CompactSize in V2. ServiceFlags nServices{NODE_NONE}; diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 1c4f479410..f61b9147c4 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -968,7 +969,7 @@ static RPCHelpMan getnodeaddresses() for (const CAddress& addr : vAddr) { UniValue obj(UniValue::VOBJ); - obj.pushKV("time", (int)addr.nTime); + obj.pushKV("time", int64_t{TicksSinceEpoch(addr.nTime)}); obj.pushKV("services", (uint64_t)addr.nServices); obj.pushKV("address", addr.ToStringAddr()); obj.pushKV("port", addr.GetPort()); @@ -1017,7 +1018,7 @@ static RPCHelpMan addpeeraddress() if (net_addr.has_value()) { CAddress address{{net_addr.value(), port}, ServiceFlags{NODE_NETWORK}}; - address.nTime = GetAdjustedTime(); + address.nTime = Now(); // The source address is set equal to the address. This is equivalent to the peer // announcing itself. if (node.addrman->Add({address}, address)) { diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 17a83402a8..0e5f79da0f 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -16,13 +16,15 @@ #include #include #include +#include #include #include #include #include -#include // for unique_ptr +#include +#include #include #include @@ -43,7 +45,7 @@ static const std::string defaultPlatformUser = "platform-user"; struct RPCCommandExecutionInfo { std::string method; - int64_t start; + SteadyClock::time_point start; }; struct RPCServerInfo @@ -60,7 +62,7 @@ struct RPCCommandExecution explicit RPCCommandExecution(const std::string& method) { LOCK(g_rpc_server_info.mutex); - it = g_rpc_server_info.active_commands.insert(g_rpc_server_info.active_commands.end(), {method, GetTimeMicros()}); + it = g_rpc_server_info.active_commands.insert(g_rpc_server_info.active_commands.end(), {method, SteadyClock::now()}); } ~RPCCommandExecution() { @@ -272,7 +274,7 @@ static RPCHelpMan getrpcinfo() for (const RPCCommandExecutionInfo& info : g_rpc_server_info.active_commands) { UniValue entry(UniValue::VOBJ); entry.pushKV("method", info.method); - entry.pushKV("duration", GetTimeMicros() - info.start); + entry.pushKV("duration", int64_t{Ticks(SteadyClock::now() - info.start)}); active_commands.push_back(entry); } diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 933cf2138a..524c4552ff 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -4,10 +4,10 @@ #include -#include +#include #include -#include +#include #include #include @@ -41,7 +41,7 @@ void CScheduler::serviceQueue() // the time of the first item on the queue: while (!shouldStop() && !taskQueue.empty()) { - std::chrono::system_clock::time_point timeToWaitFor = taskQueue.begin()->first; + std::chrono::steady_clock::time_point timeToWaitFor = taskQueue.begin()->first; if (newTaskScheduled.wait_until(lock, timeToWaitFor) == std::cv_status::timeout) { break; // Exit loop after timeout, it means we reached the time of the event } @@ -70,7 +70,7 @@ void CScheduler::serviceQueue() newTaskScheduled.notify_one(); } -void CScheduler::schedule(CScheduler::Function f, std::chrono::system_clock::time_point t) +void CScheduler::schedule(CScheduler::Function f, std::chrono::steady_clock::time_point t) { { LOCK(newTaskMutex); @@ -87,7 +87,7 @@ void CScheduler::MockForward(std::chrono::seconds delta_seconds) LOCK(newTaskMutex); // use temp_queue to maintain updated schedule - std::multimap temp_queue; + std::multimap temp_queue; for (const auto& element : taskQueue) { temp_queue.emplace_hint(temp_queue.cend(), element.first - delta_seconds, element.second); @@ -112,8 +112,8 @@ void CScheduler::scheduleEvery(CScheduler::Function f, std::chrono::milliseconds scheduleFromNow([this, f, delta] { Repeat(*this, f, delta); }, delta); } -size_t CScheduler::getQueueInfo(std::chrono::system_clock::time_point& first, - std::chrono::system_clock::time_point& last) const +size_t CScheduler::getQueueInfo(std::chrono::steady_clock::time_point& first, + std::chrono::steady_clock::time_point& last) const { LOCK(newTaskMutex); size_t result = taskQueue.size(); @@ -141,7 +141,7 @@ void SingleThreadedSchedulerClient::MaybeScheduleProcessQueue() if (m_are_callbacks_running) return; if (m_callbacks_pending.empty()) return; } - m_scheduler.schedule([this] { this->ProcessQueue(); }, std::chrono::system_clock::now()); + m_scheduler.schedule([this] { this->ProcessQueue(); }, std::chrono::steady_clock::now()); } void SingleThreadedSchedulerClient::ProcessQueue() diff --git a/src/scheduler.h b/src/scheduler.h index 727270e195..5617235feb 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -46,12 +46,12 @@ public: typedef std::function Function; /** Call func at/after time t */ - void schedule(Function f, std::chrono::system_clock::time_point t) EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex); + void schedule(Function f, std::chrono::steady_clock::time_point t) EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex); /** Call f once after the delta has passed */ void scheduleFromNow(Function f, std::chrono::milliseconds delta) EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex) { - schedule(std::move(f), std::chrono::system_clock::now() + delta); + schedule(std::move(f), std::chrono::steady_clock::now() + delta); } /** @@ -93,8 +93,8 @@ public: * Returns number of tasks waiting to be serviced, * and first and last task times */ - size_t getQueueInfo(std::chrono::system_clock::time_point& first, - std::chrono::system_clock::time_point& last) const + size_t getQueueInfo(std::chrono::steady_clock::time_point& first, + std::chrono::steady_clock::time_point& last) const EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex); /** Returns true if there are threads actively running in serviceQueue() */ @@ -103,7 +103,7 @@ public: private: mutable Mutex newTaskMutex; std::condition_variable newTaskScheduled; - std::multimap taskQueue GUARDED_BY(newTaskMutex); + std::multimap taskQueue GUARDED_BY(newTaskMutex); int nThreadsServicingQueue GUARDED_BY(newTaskMutex){0}; bool stopRequested GUARDED_BY(newTaskMutex){false}; bool stopWhenEmpty GUARDED_BY(newTaskMutex){false}; diff --git a/src/serialize.h b/src/serialize.h index 14e56baef4..c8b57ad482 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -704,6 +704,29 @@ struct CompactSizeFormatter } }; +template +struct ChronoFormatter { + template + void Unser(Stream& s, Tp& tp) + { + U u; + s >> u; + // Lossy deserialization does not make sense, so force Wnarrowing + tp = Tp{typename Tp::duration{typename Tp::duration::rep{u}}}; + } + template + void Ser(Stream& s, Tp tp) + { + if constexpr (LOSSY) { + s << U(tp.time_since_epoch().count()); + } else { + s << U{tp.time_since_epoch().count()}; + } + } +}; +template +using LossyChronoFormatter = ChronoFormatter; + template struct LimitedStringFormatter { diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index 1e2e3a8b6c..ce9a2bca82 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -309,7 +309,7 @@ BOOST_AUTO_TEST_CASE(addrman_new_multiplicity) { auto addrman = std::make_unique(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node)); CAddress addr{CAddress(ResolveService("253.3.3.3", 8333), NODE_NONE)}; - int64_t start_time{GetAdjustedTime()}; + const auto start_time{Now()}; addr.nTime = start_time; // test that multiplicity stays at 1 if nTime doesn't increase @@ -328,7 +328,7 @@ BOOST_AUTO_TEST_CASE(addrman_new_multiplicity) for (unsigned int i = 1; i < 400; ++i) { std::string addr_ip{ToString(i % 256) + "." + ToString(i >> 8 % 256) + ".1.1"}; CNetAddr source{ResolveIP(addr_ip)}; - addr.nTime = start_time + i; + addr.nTime = start_time + std::chrono::seconds{i}; addrman->Add({addr}, source); } AddressPosition addr_pos_multi = addrman->FindAddressEntry(addr).value(); @@ -379,15 +379,15 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr) BOOST_CHECK_EQUAL(vAddr1.size(), 0U); CAddress addr1 = CAddress(ResolveService("250.250.2.1", 8333), NODE_NONE); - addr1.nTime = GetAdjustedTime(); // Set time so isTerrible = false + addr1.nTime = Now(); // Set time so isTerrible = false CAddress addr2 = CAddress(ResolveService("250.251.2.2", 9999), NODE_NONE); - addr2.nTime = GetAdjustedTime(); + addr2.nTime = Now(); CAddress addr3 = CAddress(ResolveService("251.252.2.3", 8333), NODE_NONE); - addr3.nTime = GetAdjustedTime(); + addr3.nTime = Now(); CAddress addr4 = CAddress(ResolveService("252.253.3.4", 8333), NODE_NONE); - addr4.nTime = GetAdjustedTime(); + addr4.nTime = Now(); CAddress addr5 = CAddress(ResolveService("252.254.4.5", 8333), NODE_NONE); - addr5.nTime = GetAdjustedTime(); + addr5.nTime = Now(); CNetAddr source1 = ResolveIP("250.1.2.1"); CNetAddr source2 = ResolveIP("250.2.3.3"); @@ -413,7 +413,7 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr) CAddress addr = CAddress(ResolveService(strAddr), NODE_NONE); // Ensure that for all addrs in addrman, isTerrible == false. - addr.nTime = GetAdjustedTime(); + addr.nTime = Now(); addrman->Add({addr}, ResolveIP(strAddr)); if (i % 8 == 0) addrman->Good(addr); @@ -905,8 +905,8 @@ BOOST_AUTO_TEST_CASE(addrman_evictionworks) // Ensure test of address fails, so that it is evicted. // Update entry in tried by setting last good connection in the deep past. - BOOST_CHECK(!addrman->Good(info, /*nTime=*/1)); - addrman->Attempt(info, /*fCountFailure=*/false, /*nTime=*/GetAdjustedTime() - 61); + BOOST_CHECK(!addrman->Good(info, NodeSeconds{1s})); + addrman->Attempt(info, /*fCountFailure=*/false, Now() - 61s); // Should swap 36 for 19. addrman->ResolveCollisions(); @@ -1053,7 +1053,7 @@ BOOST_AUTO_TEST_CASE(addrman_update_address) CNetAddr source{ResolveIP("252.2.2.2")}; CAddress addr{CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE)}; - int64_t start_time{GetAdjustedTime() - 10000}; + const auto start_time{Now() - 10000s}; addr.nTime = start_time; BOOST_CHECK(addrman->Add({addr}, source)); BOOST_CHECK_EQUAL(addrman->Size(), 1U); @@ -1065,7 +1065,7 @@ BOOST_AUTO_TEST_CASE(addrman_update_address) addrman->SetServices(addr_diff_port, NODE_NETWORK_LIMITED); std::vector vAddr1{addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt)}; BOOST_CHECK_EQUAL(vAddr1.size(), 1U); - BOOST_CHECK_EQUAL(vAddr1.at(0).nTime, start_time); + BOOST_CHECK(vAddr1.at(0).nTime == start_time); BOOST_CHECK_EQUAL(vAddr1.at(0).nServices, NODE_NONE); // Updating an addrman entry with the correct port is successful @@ -1073,7 +1073,7 @@ BOOST_AUTO_TEST_CASE(addrman_update_address) addrman->SetServices(addr, NODE_NETWORK_LIMITED); std::vector vAddr2 = addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt); BOOST_CHECK_EQUAL(vAddr2.size(), 1U); - BOOST_CHECK(vAddr2.at(0).nTime >= start_time + 10000); + BOOST_CHECK(vAddr2.at(0).nTime >= start_time + 10000s); BOOST_CHECK_EQUAL(vAddr2.at(0).nServices, NODE_NETWORK_LIMITED); } diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp index 0de69455c7..32e5787273 100644 --- a/src/test/fuzz/addrman.cpp +++ b/src/test/fuzz/addrman.cpp @@ -114,11 +114,11 @@ void FillAddrman(AddrMan& addrman, FuzzedDataProvider& fuzzed_data_provider) for (size_t j = 0; j < num_addresses; ++j) { const auto addr = CAddress{CService{RandAddr(fuzzed_data_provider, fast_random_context), 8333}, NODE_NETWORK}; - const auto time_penalty = fast_random_context.randrange(100000001); + const std::chrono::seconds time_penalty{fast_random_context.randrange(100000001)}; addrman.Add({addr}, source, time_penalty); if (n > 0 && addrman.Size() % n == 0) { - addrman.Good(addr, GetTime()); + addrman.Good(addr, Now()); } // Add 10% of the addresses from more than one source. @@ -162,7 +162,7 @@ public: CSipHasher hasher(0, 0); auto addr_key = a.GetKey(); auto source_key = a.source.GetAddrBytes(); - hasher.Write(a.nLastSuccess); + hasher.Write(TicksSinceEpoch(a.m_last_success)); hasher.Write(a.nAttempts); hasher.Write(a.nRefCount); hasher.Write(a.fInTried); @@ -176,8 +176,8 @@ public: }; auto addrinfo_eq = [](const AddrInfo& lhs, const AddrInfo& rhs) { - return std::tie(static_cast(lhs), lhs.source, lhs.nLastSuccess, lhs.nAttempts, lhs.nRefCount, lhs.fInTried) == - std::tie(static_cast(rhs), rhs.source, rhs.nLastSuccess, rhs.nAttempts, rhs.nRefCount, rhs.fInTried); + return std::tie(static_cast(lhs), lhs.source, lhs.m_last_success, lhs.nAttempts, lhs.nRefCount, lhs.fInTried) == + std::tie(static_cast(rhs), rhs.source, rhs.m_last_success, rhs.nAttempts, rhs.nRefCount, rhs.fInTried); }; using Addresses = std::unordered_set; @@ -279,25 +279,25 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman) } const std::optional opt_net_addr = ConsumeDeserializable(fuzzed_data_provider); if (opt_net_addr) { - addr_man.Add(addresses, *opt_net_addr, fuzzed_data_provider.ConsumeIntegralInRange(0, 100000000)); + addr_man.Add(addresses, *opt_net_addr, std::chrono::seconds{ConsumeTime(fuzzed_data_provider, 0, 100000000)}); } }, [&] { const std::optional opt_service = ConsumeDeserializable(fuzzed_data_provider); if (opt_service) { - addr_man.Good(*opt_service, ConsumeTime(fuzzed_data_provider)); + addr_man.Good(*opt_service, NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); } }, [&] { const std::optional opt_service = ConsumeDeserializable(fuzzed_data_provider); if (opt_service) { - addr_man.Attempt(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider)); + addr_man.Attempt(*opt_service, fuzzed_data_provider.ConsumeBool(), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); } }, [&] { const std::optional opt_service = ConsumeDeserializable(fuzzed_data_provider); if (opt_service) { - addr_man.Connected(*opt_service, ConsumeTime(fuzzed_data_provider)); + addr_man.Connected(*opt_service, NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); } }, [&] { diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp index 00f874212e..ee91b15ed0 100644 --- a/src/test/fuzz/fuzz.cpp +++ b/src/test/fuzz/fuzz.cpp @@ -188,7 +188,7 @@ int main(int argc, char** argv) return 0; } std::signal(SIGABRT, signal_handler); - int64_t start_time = GetTimeSeconds(); + const auto start_time{Now()}; int tested = 0; for (int i = 1; i < argc; ++i) { fs::path input_path(*(argv + i)); @@ -209,8 +209,8 @@ int main(int argc, char** argv) buffer.clear(); } } - int64_t end_time = GetTimeSeconds(); - std::cout << g_fuzz_target << ": succeeded against " << tested << " files in " << (end_time - start_time) << "s." << std::endl; + const auto end_time{Now()}; + std::cout << g_fuzz_target << ": succeeded against " << tested << " files in " << count_seconds(end_time - start_time) << "s." << std::endl; #endif return 0; } diff --git a/src/test/fuzz/util.cpp b/src/test/fuzz/util.cpp index bd55d99f97..c23151cc50 100644 --- a/src/test/fuzz/util.cpp +++ b/src/test/fuzz/util.cpp @@ -390,6 +390,11 @@ bool ContainsSpentInput(const CTransaction& tx, const CCoinsViewCache& inputs) n return false; } +CAddress ConsumeAddress(FuzzedDataProvider& fuzzed_data_provider) noexcept +{ + return {ConsumeService(fuzzed_data_provider), ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS), NodeSeconds{std::chrono::seconds{fuzzed_data_provider.ConsumeIntegral()}}}; +} + FILE* FuzzedFileProvider::open() { SetFuzzedErrNo(m_fuzzed_data_provider); diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h index f93ee3719c..43864a5453 100644 --- a/src/test/fuzz/util.h +++ b/src/test/fuzz/util.h @@ -307,10 +307,7 @@ inline CService ConsumeService(FuzzedDataProvider& fuzzed_data_provider) noexcep return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral()}; } -inline CAddress ConsumeAddress(FuzzedDataProvider& fuzzed_data_provider) noexcept -{ - return {ConsumeService(fuzzed_data_provider), ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS), fuzzed_data_provider.ConsumeIntegral()}; -} +CAddress ConsumeAddress(FuzzedDataProvider& fuzzed_data_provider) noexcept; template auto ConsumeNode(FuzzedDataProvider& fuzzed_data_provider, const std::optional& node_id_in = std::nullopt) noexcept diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index 800aa6fea7..02a958185f 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -670,9 +670,6 @@ BOOST_AUTO_TEST_CASE(get_local_addr_for_peer_port) const uint16_t bind_port = 20001; m_node.args->ForceSetArg("-bind", strprintf("3.4.5.6:%u", bind_port)); - const uint32_t current_time = static_cast(GetAdjustedTime()); - SetMockTime(current_time); - // Our address:port as seen from the peer, completely different from the above. in_addr peer_us_addr; peer_us_addr.s_addr = htonl(0x02030405); @@ -696,7 +693,7 @@ BOOST_AUTO_TEST_CASE(get_local_addr_for_peer_port) // Without the fix peer_us:8333 is chosen instead of the proper peer_us:bind_port. auto chosen_local_addr = GetLocalAddrForPeer(peer_out); BOOST_REQUIRE(chosen_local_addr); - const CAddress expected{CService{peer_us_addr, bind_port}, NODE_NETWORK, current_time}; + const CService expected{peer_us_addr, bind_port}; BOOST_CHECK(*chosen_local_addr == expected); // Create a peer with a routable IPv4 address (inbound). diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index d9fe06d759..ffaac9144d 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -337,21 +337,21 @@ BOOST_AUTO_TEST_CASE(netbase_getgroup) // try a few edge cases for port, service flags and time. static const std::vector fixture_addresses({ - CAddress( + CAddress{ CService(CNetAddr(in6_addr(IN6ADDR_LOOPBACK_INIT)), 0 /* port */), NODE_NONE, - 0x4966bc61U /* Fri Jan 9 02:54:25 UTC 2009 */ - ), - CAddress( + NodeSeconds{0x4966bc61s}, /* Fri Jan 9 02:54:25 UTC 2009 */ + }, + CAddress{ CService(CNetAddr(in6_addr(IN6ADDR_LOOPBACK_INIT)), 0x00f1 /* port */), NODE_NETWORK, - 0x83766279U /* Tue Nov 22 11:22:33 UTC 2039 */ - ), - CAddress( + NodeSeconds{0x83766279s}, /* Tue Nov 22 11:22:33 UTC 2039 */ + }, + CAddress{ CService(CNetAddr(in6_addr(IN6ADDR_LOOPBACK_INIT)), 0xf1f2 /* port */), static_cast(NODE_NETWORK_LIMITED), - 0xffffffffU /* Sun Feb 7 06:28:15 UTC 2106 */ - ) + NodeSeconds{0xffffffffs}, /* Sun Feb 7 06:28:15 UTC 2106 */ + }, }); // fixture_addresses should equal to this when serialized in V1 format. diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp index b560608e46..bd3dbc1da4 100644 --- a/src/test/scheduler_tests.cpp +++ b/src/test/scheduler_tests.cpp @@ -15,13 +15,13 @@ BOOST_AUTO_TEST_SUITE(scheduler_tests) -static void microTask(CScheduler& s, std::mutex& mutex, int& counter, int delta, std::chrono::system_clock::time_point rescheduleTime) +static void microTask(CScheduler& s, std::mutex& mutex, int& counter, int delta, std::chrono::steady_clock::time_point rescheduleTime) { { std::lock_guard lock(mutex); counter += delta; } - std::chrono::system_clock::time_point noTime = std::chrono::system_clock::time_point::min(); + auto noTime = std::chrono::steady_clock::time_point::min(); if (rescheduleTime != noTime) { CScheduler::Function f = std::bind(µTask, std::ref(s), std::ref(mutex), std::ref(counter), -delta + 1, noTime); s.schedule(f, rescheduleTime); @@ -49,15 +49,15 @@ BOOST_AUTO_TEST_CASE(manythreads) auto randomMsec = [](FastRandomContext& rc) -> int { return -11 + (int)rc.randrange(1012); }; // [-11, 1000] auto randomDelta = [](FastRandomContext& rc) -> int { return -1000 + (int)rc.randrange(2001); }; // [-1000, 1000] - std::chrono::system_clock::time_point start = std::chrono::system_clock::now(); - std::chrono::system_clock::time_point now = start; - std::chrono::system_clock::time_point first, last; + auto start = std::chrono::steady_clock::now(); + auto now = start; + std::chrono::steady_clock::time_point first, last; size_t nTasks = microTasks.getQueueInfo(first, last); BOOST_CHECK(nTasks == 0); for (int i = 0; i < 100; ++i) { - std::chrono::system_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng)); - std::chrono::system_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng)); + auto t = now + std::chrono::microseconds(randomMsec(rng)); + auto tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng)); int whichCounter = zeroToNine(rng); CScheduler::Function f = std::bind(µTask, std::ref(microTasks), std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]), @@ -75,14 +75,14 @@ BOOST_AUTO_TEST_CASE(manythreads) microThreads.emplace_back(std::bind(&CScheduler::serviceQueue, µTasks)); UninterruptibleSleep(std::chrono::microseconds{600}); - now = std::chrono::system_clock::now(); + now = std::chrono::steady_clock::now(); // More threads and more tasks: for (int i = 0; i < 5; i++) microThreads.emplace_back(std::bind(&CScheduler::serviceQueue, µTasks)); for (int i = 0; i < 100; i++) { - std::chrono::system_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng)); - std::chrono::system_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng)); + auto t = now + std::chrono::microseconds(randomMsec(rng)); + auto tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng)); int whichCounter = zeroToNine(rng); CScheduler::Function f = std::bind(µTask, std::ref(microTasks), std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]), @@ -111,8 +111,8 @@ BOOST_AUTO_TEST_CASE(wait_until_past) Mutex mtx; WAIT_LOCK(mtx, lock); - const auto no_wait= [&](const std::chrono::seconds& d) { - return condvar.wait_until(lock, std::chrono::system_clock::now() - d); + const auto no_wait = [&](const std::chrono::seconds& d) { + return condvar.wait_until(lock, std::chrono::steady_clock::now() - d); }; BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::seconds{1})); @@ -184,7 +184,7 @@ BOOST_AUTO_TEST_CASE(mockforward) scheduler.scheduleFromNow(dummy, std::chrono::minutes{8}); // check taskQueue - std::chrono::system_clock::time_point first, last; + std::chrono::steady_clock::time_point first, last; size_t num_tasks = scheduler.getQueueInfo(first, last); BOOST_CHECK_EQUAL(num_tasks, 3ul); @@ -205,7 +205,7 @@ BOOST_AUTO_TEST_CASE(mockforward) BOOST_CHECK_EQUAL(counter, 2); // check that the time of the remaining job has been updated - std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); + auto now = std::chrono::steady_clock::now(); int delta = std::chrono::duration_cast(first - now).count(); // should be between 2 & 3 minutes from now BOOST_CHECK(delta > 2*60 && delta < 3*60); diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index ea9c735178..1a53f5cb16 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -297,9 +297,6 @@ BOOST_AUTO_TEST_CASE(util_FormatParseISO8601DateTime) BOOST_CHECK_EQUAL(ParseISO8601DateTime("1970-01-01T00:00:00Z"), 0); BOOST_CHECK_EQUAL(ParseISO8601DateTime("1960-01-01T00:00:00Z"), 0); BOOST_CHECK_EQUAL(ParseISO8601DateTime("2011-09-30T23:36:17Z"), 1317425777); - - auto time = GetTimeSeconds(); - BOOST_CHECK_EQUAL(ParseISO8601DateTime(FormatISO8601DateTime(time)), time); } BOOST_AUTO_TEST_CASE(util_FormatISO8601Date) @@ -1528,8 +1525,12 @@ BOOST_AUTO_TEST_CASE(util_time_GetTime) for (const auto& num_sleep : {0ms, 1ms}) { UninterruptibleSleep(num_sleep); BOOST_CHECK_EQUAL(111, GetTime()); // Deprecated time getter + BOOST_CHECK_EQUAL(111, Now().time_since_epoch().count()); + BOOST_CHECK_EQUAL(111, TicksSinceEpoch(NodeClock::now())); + BOOST_CHECK_EQUAL(111, TicksSinceEpoch(Now())); BOOST_CHECK_EQUAL(111, GetTime().count()); BOOST_CHECK_EQUAL(111000, GetTime().count()); + BOOST_CHECK_EQUAL(111000, TicksSinceEpoch(NodeClock::now())); BOOST_CHECK_EQUAL(111000000, GetTime().count()); } diff --git a/src/timedata.h b/src/timedata.h index 902b4dc0f8..3779486c1f 100644 --- a/src/timedata.h +++ b/src/timedata.h @@ -5,9 +5,12 @@ #ifndef BITCOIN_TIMEDATA_H #define BITCOIN_TIMEDATA_H +#include + #include -#include -#include +#include +#include +#include #include static const int64_t DEFAULT_MAX_TIME_ADJUSTMENT = 70 * 60; diff --git a/src/util/time.cpp b/src/util/time.cpp index fa0ad5b5a4..2b9a469d9a 100644 --- a/src/util/time.cpp +++ b/src/util/time.cpp @@ -66,20 +66,16 @@ bool ChronoSanityCheck() return true; } -template -T GetTime() +NodeClock::time_point NodeClock::now() noexcept { const auto mocktime{g_mock_time.load(std::memory_order_relaxed)}; const auto ret{ mocktime.count() ? mocktime : - std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch())}; + std::chrono::system_clock::now().time_since_epoch()}; assert(ret > 0s); - return ret; -} -template std::chrono::seconds GetTime(); -template std::chrono::milliseconds GetTime(); -template std::chrono::microseconds GetTime(); + return time_point{ret}; +}; template static T GetSystemTime() @@ -111,11 +107,6 @@ int64_t GetTimeMicros() return int64_t{GetSystemTime().count()}; } -int64_t GetTimeSeconds() -{ - return int64_t{GetSystemTime().count()}; -} - int64_t GetTime() { return GetTime().count(); } std::string FormatISO8601DateTime(int64_t nTime) { diff --git a/src/util/time.h b/src/util/time.h index 36300b29fd..306a1ae3f2 100644 --- a/src/util/time.h +++ b/src/util/time.h @@ -14,6 +14,16 @@ using namespace std::chrono_literals; +/** Mockable clock in the context of tests, otherwise the system clock */ +struct NodeClock : public std::chrono::system_clock { + using time_point = std::chrono::time_point; + /** Return current system time or mocked time, if set */ + static time_point now() noexcept; + static std::time_t to_time_t(const time_point&) = delete; // unused + static time_point from_time_t(std::time_t) = delete; // unused +}; +using NodeSeconds = std::chrono::time_point; + using SteadyClock = std::chrono::steady_clock; using SteadySeconds = std::chrono::time_point; using SteadyMilliseconds = std::chrono::time_point; @@ -22,19 +32,30 @@ using SteadyMicroseconds = std::chrono::time_point +constexpr auto Ticks(Dur2 d) +{ + return std::chrono::duration_cast(d).count(); +} +template +constexpr auto TicksSinceEpoch(Timepoint t) +{ + return Ticks(t.time_since_epoch()); +} constexpr int64_t count_seconds(std::chrono::seconds t) { return t.count(); } constexpr int64_t count_milliseconds(std::chrono::milliseconds t) { return t.count(); } constexpr int64_t count_microseconds(std::chrono::microseconds t) { return t.count(); } +using HoursDouble = std::chrono::duration; using SecondsDouble = std::chrono::duration; /** @@ -44,7 +65,11 @@ inline double CountSecondsDouble(SecondsDouble t) { return t.count(); } /** * DEPRECATED - * Use either GetTimeSeconds (not mockable) or GetTime (mockable) + * Use either ClockType::now() or Now() if a cast is needed. + * ClockType is + * - std::chrono::steady_clock for steady time + * - std::chrono::system_clock for system time + * - NodeClock for mockable system time */ int64_t GetTime(); @@ -52,8 +77,6 @@ int64_t GetTime(); int64_t GetTimeMillis(); /** Returns the system time (not mockable) */ int64_t GetTimeMicros(); -/** Returns the system time (not mockable) */ -int64_t GetTimeSeconds(); // Like GetTime(), but not mockable /** * DEPRECATED @@ -69,9 +92,6 @@ void SetMockTime(std::chrono::seconds mock_time_in); /** For testing */ std::chrono::seconds GetMockTime(); -/** Return system time (or mocked time, if set) */ -template -T GetTime(); /** * Return the current time point cast to the given precicion. Only use this * when an exact precicion is needed, otherwise use T::clock::now() directly. @@ -81,6 +101,12 @@ T Now() { return std::chrono::time_point_cast(T::clock::now()); } +/** DEPRECATED, see GetTime */ +template +T GetTime() +{ + return Now>().time_since_epoch(); +} /** * ISO 8601 formatting is preferred. Use the FormatISO8601{DateTime,Date,Time} diff --git a/src/validation.cpp b/src/validation.cpp index 1074bb065f..5f093aa1b2 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -4603,7 +4603,7 @@ void CChainState::LoadExternalBlockFile( // Either both should be specified (-reindex), or neither (-loadblock). assert(!dbp == !blocks_with_unknown_parent); - int64_t nStart = GetTimeMillis(); + const auto start{SteadyClock::now()}; int nLoaded = 0; try { @@ -4739,7 +4739,7 @@ void CChainState::LoadExternalBlockFile( } catch (const std::runtime_error& e) { AbortNode(std::string("System error: ") + e.what()); } - LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, GetTimeMillis() - nStart); + LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, Ticks(SteadyClock::now() - start)); } void CChainState::CheckBlockIndex() diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp index 4ffc08335c..1509f9c177 100644 --- a/src/wallet/bdb.cpp +++ b/src/wallet/bdb.cpp @@ -533,7 +533,7 @@ bool BerkeleyDatabase::Rewrite(const char* pszSkip) void BerkeleyEnvironment::Flush(bool fShutdown) { - int64_t nStart = GetTimeMillis(); + const auto start{SteadyClock::now()}; // Flush log data to the actual data file on all files that are not in use LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: [%s] Flush(%s)%s\n", strPath, fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started"); if (!fDbEnvInit) @@ -560,7 +560,7 @@ void BerkeleyEnvironment::Flush(bool fShutdown) no_dbs_accessed = false; } } - LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart); + LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", Ticks(SteadyClock::now() - start)); if (fShutdown) { char** listp; if (no_dbs_accessed) { @@ -589,14 +589,14 @@ bool BerkeleyDatabase::PeriodicFlush() if (m_refcount < 0) return false; LogPrint(BCLog::WALLETDB, "Flushing %s\n", strFile); - int64_t nStart = GetTimeMillis(); + const auto start{SteadyClock::now()}; // Flush wallet file so it's self contained env->CloseDb(strFile); env->CheckpointLSN(strFile); m_refcount = -1; - LogPrint(BCLog::WALLETDB, "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart); + LogPrint(BCLog::WALLETDB, "Flushed %s %dms\n", strFile, Ticks(SteadyClock::now() - start)); return true; } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e5c638d754..869af8d63b 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4725,7 +4725,7 @@ std::shared_ptr CWallet::Create(interfaces::Chain* chain, interfaces::C { const std::string& walletFile = database->Filename(); - int64_t nStart = GetTimeMillis(); + const auto start{SteadyClock::now()}; // TODO: Can't use std::make_shared because we need a custom deleter but // should be possible to use std::allocate_shared. std::shared_ptr walletInstance(new CWallet(chain, coinjoin_loader, name, std::move(database)), ReleaseWallet); @@ -4971,7 +4971,7 @@ std::shared_ptr CWallet::Create(interfaces::Chain* chain, interfaces::C walletInstance->m_confirm_target = gArgs.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); walletInstance->m_spend_zero_conf_change = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); - walletInstance->WalletLogPrintf("Wallet completed loading in %15dms\n", GetTimeMillis() - nStart); + walletInstance->WalletLogPrintf("Wallet completed loading in %15dms\n", Ticks(SteadyClock::now() - start)); // Try to top up keypool. No-op if the wallet is locked. walletInstance->TopUpKeyPool(); diff --git a/test/functional/p2p_addr_relay.py b/test/functional/p2p_addr_relay.py index e89734fcfe..01cb550ec0 100755 --- a/test/functional/p2p_addr_relay.py +++ b/test/functional/p2p_addr_relay.py @@ -6,6 +6,8 @@ Test addr relay """ +import random + from test_framework.messages import ( CAddress, NODE_NETWORK, @@ -18,9 +20,19 @@ from test_framework.p2p import ( p2p_lock, ) from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, assert_greater_than -import random +from test_framework.util import ( + assert_equal, + assert_greater_than, + assert_greater_than_or_equal +) +ONE_MINUTE = 60 +TEN_MINUTES = 10 * ONE_MINUTE +ONE_HOUR = 60 * ONE_MINUTE +TWO_HOURS = 2 * ONE_HOUR +ONE_DAY = 24 * ONE_HOUR + +ADDR_DESTINATIONS_THRESHOLD = 4 class AddrReceiver(P2PInterface): num_ipv4_received = 0 @@ -82,6 +94,9 @@ class AddrTest(BitcoinTestFramework): self.relay_tests() self.inbound_blackhole_tests() + self.destination_rotates_once_in_24_hours_test() + self.destination_rotates_more_than_once_over_several_days_test() + # This test populates the addrman, which can impact the node's behavior # in subsequent tests self.getaddr_tests() @@ -361,6 +376,56 @@ class AddrTest(BitcoinTestFramework): self.nodes[0].disconnect_p2ps() + def get_nodes_that_received_addr(self, peer, receiver_peer, addr_receivers, + time_interval_1, time_interval_2): + + # Clean addr response related to the initial getaddr. There is no way to avoid initial + # getaddr because the peer won't self-announce then. + for addr_receiver in addr_receivers: + addr_receiver.num_ipv4_received = 0 + + for _ in range(10): + self.mocktime += time_interval_1 + self.msg.addrs[0].time = self.mocktime + TEN_MINUTES + self.nodes[0].setmocktime(self.mocktime) + with self.nodes[0].assert_debug_log(['received: addr (31 bytes) peer=0']): + peer.send_and_ping(self.msg) + self.mocktime += time_interval_2 + self.nodes[0].setmocktime(self.mocktime) + receiver_peer.sync_with_ping() + return [node for node in addr_receivers if node.addr_received()] + + def destination_rotates_once_in_24_hours_test(self): + self.restart_node(0, []) + + self.log.info('Test within 24 hours an addr relay destination is rotated at most once') + self.nodes[0].setmocktime(self.mocktime) + self.msg = self.setup_addr_msg(1) + self.addr_receivers = [] + peer = self.nodes[0].add_p2p_connection(P2PInterface()) + receiver_peer = self.nodes[0].add_p2p_connection(AddrReceiver()) + addr_receivers = [self.nodes[0].add_p2p_connection(AddrReceiver()) for _ in range(20)] + nodes_received_addr = self.get_nodes_that_received_addr(peer, receiver_peer, addr_receivers, 0, TWO_HOURS) # 10 intervals of 2 hours + # Per RelayAddress, we would announce these addrs to 2 destinations per day. + # Since it's at most one rotation, at most 4 nodes can receive ADDR. + assert_greater_than_or_equal(ADDR_DESTINATIONS_THRESHOLD, len(nodes_received_addr)) + self.nodes[0].disconnect_p2ps() + + def destination_rotates_more_than_once_over_several_days_test(self): + self.restart_node(0, []) + + self.log.info('Test after several days an addr relay destination is rotated more than once') + self.msg = self.setup_addr_msg(1) + peer = self.nodes[0].add_p2p_connection(P2PInterface()) + receiver_peer = self.nodes[0].add_p2p_connection(AddrReceiver()) + addr_receivers = [self.nodes[0].add_p2p_connection(AddrReceiver()) for _ in range(20)] + # 10 intervals of 1 day (+ 1 hour, which should be enough to cover 30-min Poisson in most cases) + nodes_received_addr = self.get_nodes_that_received_addr(peer, receiver_peer, addr_receivers, ONE_DAY, ONE_HOUR) + # Now that there should have been more than one rotation, more than + # ADDR_DESTINATIONS_THRESHOLD nodes should have received ADDR. + assert_greater_than(len(nodes_received_addr), ADDR_DESTINATIONS_THRESHOLD) + self.nodes[0].disconnect_p2ps() + if __name__ == '__main__': AddrTest().main()