From 4eaa239dc3e189369d59144b524cb2808cbef8c3 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 11 Mar 2024 10:17:20 -0400 Subject: [PATCH] random: convert GetRand{Micros,Millis} into randrange There are only a few call sites of these throughout the codebase, so move the functionality into FastRandomContext, and rewrite all call sites. This requires the callers to explicit construct FastRandomContext objects, which do add to the verbosity, but also make potentially apparent locations where the code can be improved by reusing a FastRandomContext object (see further commit). --- src/net.cpp | 3 ++- src/net_processing.cpp | 6 +++--- src/random.h | 33 +++++++++++++++++++-------------- src/test/random_tests.cpp | 24 ++++++++++++------------ 4 files changed, 36 insertions(+), 30 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index ac66540022..9e969718b7 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -3475,7 +3475,8 @@ std::vector CConnman::GetAddresses(CNode& requestor, size_t max_addres // nodes to be "terrible" (see IsTerrible()) if the timestamps are older than 30 days, // max. 24 hours of "penalty" due to cache shouldn't make any meaningful difference // in terms of the freshness of the response. - cache_entry.m_cache_entry_expiration = current_time + std::chrono::hours(21) + GetRandMicros(std::chrono::hours(6)); + cache_entry.m_cache_entry_expiration = current_time + + 21h + FastRandomContext().randrange(6h); } return cache_entry.m_addrs_response_cache; } diff --git a/src/net_processing.cpp b/src/net_processing.cpp index d674811ba7..ce3ff71df1 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1698,7 +1698,7 @@ void PeerManagerImpl::ReattemptInitialBroadcast(CScheduler& scheduler) // Schedule next run for 10-15 minutes in the future. // We add randomness on every cycle to avoid the possibility of P2P fingerprinting. - const std::chrono::milliseconds delta = 10min + GetRandMillis(5min); + const auto delta = 10min + FastRandomContext().randrange(5min); scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta); } @@ -2050,7 +2050,7 @@ void PeerManagerImpl::StartScheduledTasks(CScheduler& scheduler) scheduler.scheduleEvery([this] { this->CheckForStaleTipAndEvictPeers(); }, std::chrono::seconds{EXTRA_PEER_CHECK_INTERVAL}); // schedule next run for 10-15 minutes in the future - const std::chrono::milliseconds delta = 10min + GetRandMillis(5min); + const auto delta = 10min + FastRandomContext().randrange(5min); scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta); } @@ -5753,7 +5753,7 @@ void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, Peer& peer, std::chrono::mi // until scheduled broadcast, then move the broadcast to within MAX_FEEFILTER_CHANGE_DELAY. else if (current_time + MAX_FEEFILTER_CHANGE_DELAY < peer.m_next_send_feefilter && (currentFilter < 3 * peer.m_fee_filter_sent / 4 || currentFilter > 4 * peer.m_fee_filter_sent / 3)) { - peer.m_next_send_feefilter = current_time + GetRandomDuration(MAX_FEEFILTER_CHANGE_DELAY); + peer.m_next_send_feefilter = current_time + FastRandomContext().randrange(MAX_FEEFILTER_CHANGE_DELAY); } } diff --git a/src/random.h b/src/random.h index 1573e49ef7..b9cba1d602 100644 --- a/src/random.h +++ b/src/random.h @@ -132,6 +132,13 @@ concept RandomNumberGenerator = requires(T& rng, Span s) { requires std::derived_from, RandomMixin>>; }; +/** A concept for C++ std::chrono durations. */ +template +concept StdChronoDuration = requires { + [](std::type_identity>){}( + std::type_identity()); +}; + /** Mixin class that provides helper randomness functions. * * Intended to be used through CRTP: https://en.cppreference.com/w/cpp/language/crtp. @@ -300,7 +307,7 @@ public: } /** Generate a uniform random duration in the range from 0 (inclusive) to range (exclusive). */ - template + template requires StdChronoDuration typename Chrono::duration rand_uniform_duration(typename Chrono::duration range) noexcept { using Dur = typename Chrono::duration; @@ -309,6 +316,17 @@ public: /* interval [0..0] */ Dur{0}; }; + /** Generate a uniform random duration in the range [0..max). Precondition: max.count() > 0 */ + template + Dur randrange(typename std::common_type_t range) noexcept + // Having the compiler infer the template argument from the function argument + // is dangerous, because the desired return value generally has a different + // type than the function argument. So std::common_type is used to force the + // call site to specify the type of the return value. + { + return Dur{Impl().randrange(range.count())}; + } + // Compatibility with the UniformRandomBitGenerator concept typedef uint64_t result_type; static constexpr uint64_t min() noexcept { return 0; } @@ -426,19 +444,6 @@ void Shuffle(I first, I last, R&& rng) } } -/** Generate a uniform random duration in the range [0..max). Precondition: max.count() > 0 */ -template -D GetRandomDuration(typename std::common_type::type max) noexcept -// Having the compiler infer the template argument from the function argument -// is dangerous, because the desired return value generally has a different -// type than the function argument. So std::common_type is used to force the -// call site to specify the type of the return value. -{ - return D{FastRandomContext().randrange(max.count())}; -}; -constexpr auto GetRandMicros = GetRandomDuration; -constexpr auto GetRandMillis = GetRandomDuration; - /* Number of random bytes returned by GetOSRand. * When changing this constant make sure to change all call sites, and make * sure that the underlying OS APIs for all platforms support the number. diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp index 6932e186d5..b7479d310c 100644 --- a/src/test/random_tests.cpp +++ b/src/test/random_tests.cpp @@ -30,18 +30,18 @@ BOOST_AUTO_TEST_CASE(fastrandom_tests_deterministic) { BOOST_CHECK_EQUAL(FastRandomContext().rand(), uint64_t{9330418229102544152u}); BOOST_CHECK_EQUAL(FastRandomContext().rand(), int{618925161}); - BOOST_CHECK_EQUAL(GetRandMicros(std::chrono::hours{1}).count(), 1271170921); - BOOST_CHECK_EQUAL(GetRandMillis(std::chrono::hours{1}).count(), 2803534); + BOOST_CHECK_EQUAL(FastRandomContext().randrange(1h).count(), 1271170921); + BOOST_CHECK_EQUAL(FastRandomContext().randrange(1h).count(), 2803534); BOOST_CHECK_EQUAL(FastRandomContext().rand(), uint64_t{10170981140880778086u}); BOOST_CHECK_EQUAL(FastRandomContext().rand(), int{1689082725}); - BOOST_CHECK_EQUAL(GetRandMicros(std::chrono::hours{1}).count(), 2464643716); - BOOST_CHECK_EQUAL(GetRandMillis(std::chrono::hours{1}).count(), 2312205); + BOOST_CHECK_EQUAL(FastRandomContext().randrange(1h).count(), 2464643716); + BOOST_CHECK_EQUAL(FastRandomContext().randrange(1h).count(), 2312205); BOOST_CHECK_EQUAL(FastRandomContext().rand(), uint64_t{5689404004456455543u}); BOOST_CHECK_EQUAL(FastRandomContext().rand(), int{785839937}); - BOOST_CHECK_EQUAL(GetRandMicros(std::chrono::hours{1}).count(), 93558804); - BOOST_CHECK_EQUAL(GetRandMillis(std::chrono::hours{1}).count(), 507022); + BOOST_CHECK_EQUAL(FastRandomContext().randrange(1h).count(), 93558804); + BOOST_CHECK_EQUAL(FastRandomContext().randrange(1h).count(), 507022); } { @@ -84,18 +84,18 @@ BOOST_AUTO_TEST_CASE(fastrandom_tests_nondeterministic) { BOOST_CHECK(FastRandomContext().rand() != uint64_t{9330418229102544152u}); BOOST_CHECK(FastRandomContext().rand() != int{618925161}); - BOOST_CHECK(GetRandMicros(std::chrono::hours{1}).count() != 1271170921); - BOOST_CHECK(GetRandMillis(std::chrono::hours{1}).count() != 2803534); + BOOST_CHECK(FastRandomContext().randrange(1h).count() != 1271170921); + BOOST_CHECK(FastRandomContext().randrange(1h).count() != 2803534); BOOST_CHECK(FastRandomContext().rand() != uint64_t{10170981140880778086u}); BOOST_CHECK(FastRandomContext().rand() != int{1689082725}); - BOOST_CHECK(GetRandMicros(std::chrono::hours{1}).count() != 2464643716); - BOOST_CHECK(GetRandMillis(std::chrono::hours{1}).count() != 2312205); + BOOST_CHECK(FastRandomContext().randrange(1h).count() != 2464643716); + BOOST_CHECK(FastRandomContext().randrange(1h).count() != 2312205); BOOST_CHECK(FastRandomContext().rand() != uint64_t{5689404004456455543u}); BOOST_CHECK(FastRandomContext().rand() != int{785839937}); - BOOST_CHECK(GetRandMicros(std::chrono::hours{1}).count() != 93558804); - BOOST_CHECK(GetRandMillis(std::chrono::hours{1}).count() != 507022); + BOOST_CHECK(FastRandomContext().randrange(1h).count() != 93558804); + BOOST_CHECK(FastRandomContext().randrange(1h).count() != 507022); } {