2323#include < array>
2424#include < cmath>
2525#include < cstdlib>
26+ #include < optional>
2627#include < thread>
2728
2829#ifdef WIN32
@@ -417,6 +418,10 @@ class RNGState {
417418 uint64_t m_counter GUARDED_BY (m_mutex) = 0;
418419 bool m_strongly_seeded GUARDED_BY (m_mutex) = false;
419420
421+ /* * If not nullopt, the output of this RNGState is redirected and drawn from here
422+ * (unless always_use_real_rng is passed to MixExtract). */
423+ std::optional<ChaCha20> m_deterministic_prng GUARDED_BY (m_mutex);
424+
420425 Mutex m_events_mutex;
421426 CSHA256 m_events_hasher GUARDED_BY (m_events_mutex);
422427
@@ -457,11 +462,21 @@ class RNGState {
457462 m_events_hasher.Write (events_hash, 32 );
458463 }
459464
465+ /* * Make the output of MixExtract (unless always_use_real_rng) deterministic, with specified seed. */
466+ void MakeDeterministic (const uint256& seed) noexcept EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
467+ {
468+ LOCK (m_mutex);
469+ m_deterministic_prng.emplace (MakeByteSpan (seed));
470+ }
471+
460472 /* * Extract up to 32 bytes of entropy from the RNG state, mixing in new entropy from hasher.
461473 *
462474 * If this function has never been called with strong_seed = true, false is returned.
475+ *
476+ * If always_use_real_rng is false, and MakeDeterministic has been called before, output
477+ * from the deterministic PRNG instead.
463478 */
464- bool MixExtract (unsigned char * out, size_t num, CSHA512&& hasher, bool strong_seed) noexcept EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
479+ bool MixExtract (unsigned char * out, size_t num, CSHA512&& hasher, bool strong_seed, bool always_use_real_rng ) noexcept EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
465480 {
466481 assert (num <= 32 );
467482 unsigned char buf[64 ];
@@ -479,6 +494,13 @@ class RNGState {
479494 hasher.Finalize (buf);
480495 // Store the last 32 bytes of the hash output as new RNG state.
481496 memcpy (m_state, buf + 32 , 32 );
497+ // Handle requests for deterministic randomness.
498+ if (!always_use_real_rng && m_deterministic_prng.has_value ()) [[unlikely]] {
499+ // Overwrite the beginning of buf, which will be used for output.
500+ m_deterministic_prng->Keystream (AsWritableBytes (Span{buf, num}));
501+ // Do not require strong seeding for deterministic output.
502+ ret = true ;
503+ }
482504 }
483505 // If desired, copy (up to) the first 32 bytes of the hash output as output.
484506 if (num) {
@@ -552,8 +574,9 @@ static void SeedSlow(CSHA512& hasher, RNGState& rng) noexcept
552574static void SeedStrengthen (CSHA512& hasher, RNGState& rng, SteadyClock::duration dur) noexcept
553575{
554576 // Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher.
577+ // Never use the deterministic PRNG for this, as the result is only used internally.
555578 unsigned char strengthen_seed[32 ];
556- rng.MixExtract (strengthen_seed, sizeof (strengthen_seed), CSHA512 (hasher), false );
579+ rng.MixExtract (strengthen_seed, sizeof (strengthen_seed), CSHA512 (hasher), false , /* always_use_real_rng= */ true );
557580 // Strengthen the seed, and feed it into hasher.
558581 Strengthen (strengthen_seed, dur, hasher);
559582}
@@ -604,7 +627,7 @@ enum class RNGLevel {
604627 PERIODIC, // !< Called by RandAddPeriodic()
605628};
606629
607- static void ProcRand (unsigned char * out, int num, RNGLevel level) noexcept
630+ static void ProcRand (unsigned char * out, int num, RNGLevel level, bool always_use_real_rng ) noexcept
608631{
609632 // Make sure the RNG is initialized first (as all Seed* function possibly need hwrand to be available).
610633 RNGState& rng = GetRNGState ();
@@ -625,24 +648,40 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level) noexcept
625648 }
626649
627650 // Combine with and update state
628- if (!rng.MixExtract (out, num, std::move (hasher), false )) {
651+ if (!rng.MixExtract (out, num, std::move (hasher), false , always_use_real_rng )) {
629652 // On the first invocation, also seed with SeedStartup().
630653 CSHA512 startup_hasher;
631654 SeedStartup (startup_hasher, rng);
632- rng.MixExtract (out, num, std::move (startup_hasher), true );
655+ rng.MixExtract (out, num, std::move (startup_hasher), true , always_use_real_rng );
633656 }
634657}
635658
636- void GetRandBytes (Span<unsigned char > bytes) noexcept { ProcRand (bytes.data (), bytes.size (), RNGLevel::FAST); }
637- void GetStrongRandBytes (Span<unsigned char > bytes) noexcept { ProcRand (bytes.data (), bytes.size (), RNGLevel::SLOW); }
638- void RandAddPeriodic () noexcept { ProcRand (nullptr , 0 , RNGLevel::PERIODIC); }
639- void RandAddEvent (const uint32_t event_info) noexcept { GetRNGState ().AddEvent (event_info); }
659+ /* * Internal function to set g_determinstic_rng. Only accessed from tests. */
660+ void MakeRandDeterministicDANGEROUS (const uint256& seed) noexcept
661+ {
662+ GetRNGState ().MakeDeterministic (seed);
663+ }
640664
641- bool g_mock_deterministic_tests{false };
665+ void GetRandBytes (Span<unsigned char > bytes) noexcept
666+ {
667+ ProcRand (bytes.data (), bytes.size (), RNGLevel::FAST, /* always_use_real_rng=*/ false );
668+ }
669+
670+ void GetStrongRandBytes (Span<unsigned char > bytes) noexcept
671+ {
672+ ProcRand (bytes.data (), bytes.size (), RNGLevel::SLOW, /* always_use_real_rng=*/ true );
673+ }
674+
675+ void RandAddPeriodic () noexcept
676+ {
677+ ProcRand (nullptr , 0 , RNGLevel::PERIODIC, /* always_use_real_rng=*/ false );
678+ }
679+
680+ void RandAddEvent (const uint32_t event_info) noexcept { GetRNGState ().AddEvent (event_info); }
642681
643682uint64_t GetRandInternal (uint64_t nMax) noexcept
644683{
645- return FastRandomContext (g_mock_deterministic_tests ).randrange (nMax);
684+ return FastRandomContext ().randrange (nMax);
646685}
647686
648687uint256 GetRandHash () noexcept
@@ -708,7 +747,7 @@ bool Random_SanityCheck()
708747 CSHA512 to_add;
709748 to_add.Write ((const unsigned char *)&start, sizeof (start));
710749 to_add.Write ((const unsigned char *)&stop, sizeof (stop));
711- GetRNGState ().MixExtract (nullptr , 0 , std::move (to_add), false );
750+ GetRNGState ().MixExtract (nullptr , 0 , std::move (to_add), false , /* always_use_real_rng= */ true );
712751
713752 return true ;
714753}
@@ -734,7 +773,7 @@ FastRandomContext& FastRandomContext::operator=(FastRandomContext&& from) noexce
734773void RandomInit ()
735774{
736775 // Invoke RNG code to trigger initialization (if not already performed)
737- ProcRand (nullptr , 0 , RNGLevel::FAST);
776+ ProcRand (nullptr , 0 , RNGLevel::FAST, /* always_use_real_rng= */ true );
738777
739778 ReportHardwareRand ();
740779}
0 commit comments