mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-06-02 01:04:43 +02:00
random: Improve RandomMixin::randbits
The previous randbits code would, when requesting more randomness than available in its random bits buffer, discard the remaining entropy and generate new. Benchmarks show that it's usually better to first consume the existing randomness and only then generate new ones. This adds some complexity to randbits, but it doesn't weigh up against the reduced need to generate more randomness.
This commit is contained in:
@@ -39,9 +39,9 @@ BOOST_AUTO_TEST_CASE(fastrandom_tests)
|
||||
BOOST_CHECK_EQUAL(7, ctx.rand_uniform_delay(time_point, 9s).time_since_epoch().count());
|
||||
BOOST_CHECK_EQUAL(-6, ctx.rand_uniform_delay(time_point, -9s).time_since_epoch().count());
|
||||
BOOST_CHECK_EQUAL(1, ctx.rand_uniform_delay(time_point, 0s).time_since_epoch().count());
|
||||
BOOST_CHECK_EQUAL(1467825113502396065, ctx.rand_uniform_delay(time_point, 9223372036854775807s).time_since_epoch().count());
|
||||
BOOST_CHECK_EQUAL(-970181367944767837, ctx.rand_uniform_delay(time_point, -9223372036854775807s).time_since_epoch().count());
|
||||
BOOST_CHECK_EQUAL(24761, ctx.rand_uniform_delay(time_point, 9h).time_since_epoch().count());
|
||||
BOOST_CHECK_EQUAL(4652286523065884857, ctx.rand_uniform_delay(time_point, 9223372036854775807s).time_since_epoch().count());
|
||||
BOOST_CHECK_EQUAL(-8813961240025683129, ctx.rand_uniform_delay(time_point, -9223372036854775807s).time_since_epoch().count());
|
||||
BOOST_CHECK_EQUAL(26443, ctx.rand_uniform_delay(time_point, 9h).time_since_epoch().count());
|
||||
}
|
||||
BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32());
|
||||
BOOST_CHECK_EQUAL(ctx1.rand32(), ctx2.rand32());
|
||||
@@ -103,6 +103,52 @@ BOOST_AUTO_TEST_CASE(fastrandom_randbits)
|
||||
}
|
||||
}
|
||||
|
||||
/** Verify that RandomMixin::randbits returns 0 and 1 for every requested bit. */
|
||||
BOOST_AUTO_TEST_CASE(randbits_test)
|
||||
{
|
||||
FastRandomContext ctx_lens; //!< RNG for producing the lengths requested from ctx_test.
|
||||
FastRandomContext ctx_test; //!< The RNG being tested.
|
||||
int ctx_test_bitsleft{0}; //!< (Assumed value of) ctx_test::bitbuf_len
|
||||
|
||||
// Run the entire test 5 times.
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
// count (first) how often it has occurred, and (second) how often it was true:
|
||||
// - for every bit position, in every requested bits count (0 + 1 + 2 + ... + 64 = 2080)
|
||||
// - for every value of ctx_test_bitsleft (0..63 = 64)
|
||||
std::vector<std::pair<uint64_t, uint64_t>> seen(2080 * 64);
|
||||
while (true) {
|
||||
// Loop 1000 times, just to not continuously check std::all_of.
|
||||
for (int j = 0; j < 1000; ++j) {
|
||||
// Decide on a number of bits to request (0 through 64, inclusive; don't use randbits/randrange).
|
||||
int bits = ctx_lens.rand64() % 65;
|
||||
// Generate that many bits.
|
||||
uint64_t gen = ctx_test.randbits(bits);
|
||||
// Make sure the result is in range.
|
||||
if (bits < 64) BOOST_CHECK_EQUAL(gen >> bits, 0);
|
||||
// Mark all the seen bits in the output.
|
||||
for (int bit = 0; bit < bits; ++bit) {
|
||||
int idx = bit + (bits * (bits - 1)) / 2 + 2080 * ctx_test_bitsleft;
|
||||
seen[idx].first += 1;
|
||||
seen[idx].second += (gen >> bit) & 1;
|
||||
}
|
||||
// Update ctx_test_bitself.
|
||||
if (bits > ctx_test_bitsleft) {
|
||||
ctx_test_bitsleft = ctx_test_bitsleft + 64 - bits;
|
||||
} else {
|
||||
ctx_test_bitsleft -= bits;
|
||||
}
|
||||
}
|
||||
// Loop until every bit position/combination is seen 242 times.
|
||||
if (std::all_of(seen.begin(), seen.end(), [](const auto& x) { return x.first >= 242; })) break;
|
||||
}
|
||||
// Check that each bit appears within 7.78 standard deviations of 50%
|
||||
// (each will fail with P < 1/(2080 * 64 * 10^9)).
|
||||
for (const auto& val : seen) {
|
||||
assert(fabs(val.first * 0.5 - val.second) < sqrt(val.first * 0.25) * 7.78);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Does-it-compile test for compatibility with standard library RNG interface. */
|
||||
BOOST_AUTO_TEST_CASE(stdrandom_test)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user