-
Notifications
You must be signed in to change notification settings - Fork 37.7k
Several randomness improvements #29625
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. Code CoverageFor detailed information about the code coverage, see the test coverage report. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the Possibly this is due to a silent merge conflict (the changes in this pull request being Leave a comment here, if you need help tracking down a confusing failure. |
Concept ACK There are a few places in the fuzz tests where this will allow to easily replace
Great, this should help with #29018 |
What's the impact on the fuzz corpus of switching to a different (?) deterministic RNG? |
I would expect that switching to a different rng should have no meaningful effect on the corpus itself. The corpus for a particular harness might change but the coverage for the code we intend to test should remain the same. This is because using rng in a fuzz harness only makes sense in very rare cases. It should never be used in a way that can significantly affect the coverage reached, otherwise there is no point in using a coverage-guided fuzzer, we could just pipe /dev/random to our harnesses. For example, if we need to populate some data that we don't really expect to have an impact on the thing we are testing, we might use rng instead of consuming from the fuzz input (we do this in the p2p transport harnesses to fill message contents, which are essentially irrelevant to the transport logic). Switching to deterministic rng can cause a corpus' coverage to grow because coverage-guided feedback loops start working more reliably when the code under test is deterministic. This can vary from harness to harness, but we've seen coverage-guided fuzzers find bugs once we've improved on determinism. |
b8d2aa9
to
3ad67b0
Compare
c07a68c
to
b5c10a4
Compare
019d483
to
1b75d68
Compare
ACK ce80942 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re-ACK ce80942 🐈
Show signature
Signature:
untrusted comment: signature from minisign secret key on empty file; verify via: minisign -Vm "${path_to_any_empty_file}" -P RWTRmVTMeKV5noAMqVlsMugDDCyyTSbA3Re5AkUrhvLVln0tSaFWglOw -x "${path_to_this_whole_four_line_signature_blob}"
RUTRmVTMeKV5npGrKx1nqXCw5zeVHdtdYURB/KlyA/LMFgpNCs+SkW9a8N95d+U4AP1RJMi+krxU1A3Yux4bpwZNLvVBKy0wLgM=
trusted comment: re-ACK ce8094246ee95232e9d84f7e37f3c0a43ef587ce 🐈
iPUv215Td9GqZ8iEiD8qyKJufeqJCMlNIHYw/Ha/B/66jmXmcrePorRaTQ+YqnML9Nkr4A+IaY3dqwDPvXI5Cg==
Reminder for myself: #29625 (comment)
@@ -2522,7 +2522,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& | |||
if (a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) { | |||
MakeAndPushMessage(pfrom, NetMsgType::CMPCTBLOCK, *a_recent_compact_block); | |||
} else { | |||
CBlockHeaderAndShortTxIDs cmpctblock{*pblock, GetRand<uint64_t>()}; | |||
CBlockHeaderAndShortTxIDs cmpctblock{*pblock, FastRandomContext().rand64()}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: (haven't tried), but in a follow-up this could use m_rng
, because the lock is already taken IIRC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in #30393
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK ce80942
Mainly code review. Did not attempt to check thread safety compliance. (Passed make check
& test/functional/test_runner.py
).
e2d1f84 - random: make GetRand() support entire range (incl. max)
The commit message title probably should be "random: make GetRand<T>() support entire range (incl. max)", since the overloads taking parameters still are exclusive at the end. It felt dangerous that GetRand<T>(void)
behavior differed in this way from the overloads - happy others suggested the calls be inlined as randrange()
and rand<T>()
are much clearer!
if (bits == 64) return Impl().rand64(); | ||
uint64_t ret; | ||
if (bits <= bitbuf_size) { | ||
// If there is enough entropy left in bitbuf, return its bottom bits bits. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In 21ce9d8: nit: typo - "bits bits"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not actually a typo, it means "Return the bottom bits
many bits", but I do see why it'd confusing. Will address if I have to repush.
// is F(x) = -log(1 - x). | ||
// | ||
// Combining the two, and using log1p(x) = log(1 + x), we obtain the following: | ||
return -std::log1p((uniform >> 11) * -0x1.0p-53); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In d5fcbe9: Always throwing away 11 bits of entropy in the new version compared to 0 before the PR. I guess you want to preserve the expression from the linked site and it's unclear whether not throwing away the bits would be more performant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I wanted to keep MakeExponential
as stand-alone reviewable as possible. I don't think any of the call sites are so performance-critical that the difference matters.
@@ -126,7 +126,7 @@ std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(int n_candida | |||
/*fRelevantServices=*/random_context.randbool(), | |||
/*m_relay_txs=*/random_context.randbool(), | |||
/*fBloomFilter=*/random_context.randbool(), | |||
/*nKeyedNetGroup=*/random_context.randrange(100), | |||
/*nKeyedNetGroup=*/random_context.randrange(100u), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In e2d1f84: Now we are in C++20 land, it might be time to use real designated initializers instead of comments when touching blocks like these?
.id=id,
.m_connected=std::chrono::seconds{random_context.randrange(100)},
.m_min_ping_time=std::chrono::microseconds{random_context.randrange(100)},
.m_last_block_time=std::chrono::seconds{random_context.randrange(100)},
.m_last_tx_time=std::chrono::seconds{random_context.randrange(100)},
.fRelevantServices=random_context.randbool(),
.m_relay_txs=random_context.randbool(),
.fBloomFilter=random_context.randbool(),
.nKeyedNetGroup=random_context.randrange(100u),
.prefer_evict=random_context.randbool(),
.m_is_local=random_context.randbool(),
.m_network=ALL_NETWORKS[random_context.randrange(ALL_NETWORKS.size())],
.m_noban=false,
.m_conn_type=ConnectionType::INBOUND,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is already a list initialization, so I don't think clang-tidy can pick up the named args at all. Happy to review a follow-up, if you decide to open one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Follow-up: #30397
if (m_opts.check_ratio == 0) return; | ||
|
||
if (GetRand(m_opts.check_ratio) >= 1) return; | ||
if (FastRandomContext().randrange(m_opts.check_ratio) >= 1) return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In ddc184d: The commit message in e2d1f84 made me do a survey of (mis)uses of GetRand()
. 2 similar cases stood out to me at first, but they appear correct after some noodling. This is one of them and the other is check_block_index
in validation.cpp.
(check_ratio
is often set to 1
(always) or 0
(never)).
Sharing the same return
path actually makes the behavior slightly quicker for me to grok:
if (m_opts.check_ratio == 0
|| FastRandomContext().randrange(m_opts.check_ratio) >= 1)
return;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK ce80942
…ction 6ecda04 random: drop ad-hoc Shuffle in favor of std::shuffle (Pieter Wuille) da28a26 bench random: benchmark more functions, and add InsecureRandomContext (Pieter Wuille) 0a9bbc6 random bench refactor: move to new bench/random.cpp (Pieter Wuille) Pull request description: This adds benchmarks for various operations on `FastRandomContext` and `InsecureRandomContext`, and then removes the ad-hoc `Shuffle` functions, now that it appears that standard library `std::shuffle` has comparable performance. The other reason for keeping `Shuffle`, namely the fact that libstdc++ used self-move (which debug mode panics on) has been fixed as well (see #29625 (comment)). ACKs for top commit: achow101: ACK 6ecda04 hodlinator: ACK 6ecda04 dergoegge: Code review ACK 6ecda04 Tree-SHA512: 2560b7312410581ff2b9bd0716e0f1558d910b5eadb9544785c972384985ac0f11f72d6b2797cfe2e7eb71fa57c30cffd98cc009cb4ee87a18b1524694211417
e233ec0 refactor: Use designated initializer (Hodlinator) Pull request description: Block was recently touched (e2d1f84) and the codebase recently switched to C++20 which allows this to improve robustness. Follow-up suggested in #29625 (comment) ACKs for top commit: maflcko: ACK e233ec0 Tree-SHA512: ce3a18f513421e923710a43c8f97db1badb7ff5c6bdbfd62d9543312d2225731db5c14bef16feb47c43b84fad4dc24485086634b680feba422d2b7b363e13fa6
Ported to the CMake-based build system in hebasto#264. |
uint256 ret; | ||
rng.Keystream(MakeWritableByteSpan(ret)); | ||
return ret; | ||
ProcRand(nullptr, 0, RNGLevel::PERIODIC, /*always_use_real_rng=*/false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test-only follow-up question: Just a nit, because this only affects tests, but for consistency, I couldn't figure out why the periodic seed does not use the real RNG. The result is only used internally, so it should be fine, in line with the other calls. For example, SeedStrengthen
, which is called as part of the periodic seeding and also uses the real RNG. Also, if this were problematic and made tests non-deterministic, the same issues should appear when someone in the tests called GetStrongRandBytes
or Random_SanityCheck
, no?
This PR contains a number of vaguely-related improvements to the random module.
The specific changes and more detailed rationale is in the commit messages, but the highlights are:
XoRoShiRo128PlusPlus
(previously a test-only RNG) moves to random.h and becomesInsecureRandomContext
, which is even faster thanFastRandomContext
but non-cryptographic. It also gets all helper randomness functions (randrange
,fillrand
, ...), making it a lot more succinct to use.GetStrongRandBytes
) but non-repeating (likeGetRand()
used to be wheng_mock_deterministic_tests
was used), either fixed, or from a random seed (overridden by env var).GetRandMillis
,GetRandMicros
,GetExponentialRand
) are converted into member functions ofFastRandomContext
(andInsecureRandomContext
).GetRand<T>()
(without argument) can now return the maximum value of the type (previously e.g.GetRand<uint32_t>()
would never return 0xffffffff).