mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-21 14:34:49 +01:00
Merge bitcoin/bitcoin#29625: Several randomness improvements
ce8094246e
random: replace construct/assign with explicit Reseed() (Pieter Wuille)2ae392d561
random: use LogError for init failure (Pieter Wuille)97e16f5704
tests: make fuzz tests (mostly) deterministic with fixed seed (Pieter Wuille)2c91330dd6
random: cleanup order, comments, static (Pieter Wuille)8e31cf9c9b
net, net_processing: use existing RNG objects more (Pieter Wuille)d5fcbe966b
random: improve precision of MakeExponentiallyDistributed (Pieter Wuille)cfb0dfe2cf
random: convert GetExponentialRand into rand_exp_duration (Pieter Wuille)4eaa239dc3
random: convert GetRand{Micros,Millis} into randrange (Pieter Wuille)82de1b80d9
net: use GetRandMicros for cache expiration (Pieter Wuille)ddc184d999
random: get rid of GetRand by inlining (Pieter Wuille)e2d1f84858
random: make GetRand() support entire range (incl. max) (Pieter Wuille)810cdf6b4e
tests: overhaul deterministic test randomness (Pieter Wuille)6cfdc5b104
random: convert XoRoShiRo128PlusPlus into full RNG (Pieter Wuille)8cc2f45065
random: move XoRoShiRo128PlusPlus into random module (Pieter Wuille)8f5ac0d0b6
xoroshiro128plusplus: drop comment about nonexisting copy() (Pieter Wuille)8924f5120f
random: modernize XoRoShiRo128PlusPlus a bit (Pieter Wuille)ddb7d26cfd
random: add RandomMixin::randbits with compile-known bits (Pieter Wuille)21ce9d8658
random: Improve RandomMixin::randbits (Pieter Wuille)9b14d3d2da
random: refactor: move rand* utilities to RandomMixin (Pieter Wuille)40dd86fc3b
random: use BasicByte concept in randbytes (Pieter Wuille)27cefc7fd6
random: add a few noexcepts to FastRandomContext (Pieter Wuille)b3b382dde2
random: move rand256() and randbytes() to .h file (Pieter Wuille)493a2e024e
random: write rand256() in function of fillrand() (Pieter Wuille) Pull request description: 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 becomes `InsecureRandomContext`, which is even faster than `FastRandomContext` but non-cryptographic. It also gets all helper randomness functions (`randrange`, `fillrand`, ...), making it a lot more succinct to use. * During tests, **all** randomness is made deterministic (except for `GetStrongRandBytes`) but non-repeating (like `GetRand()` used to be when `g_mock_deterministic_tests` was used), either fixed, or from a random seed (overridden by env var). * Several infrequently used top-level functions (`GetRandMillis`, `GetRandMicros`, `GetExponentialRand`) are converted into member functions of `FastRandomContext` (and `InsecureRandomContext`). * `GetRand<T>()` (without argument) can now return the maximum value of the type (previously e.g. `GetRand<uint32_t>()` would never return 0xffffffff). ACKs for top commit: achow101: ACKce8094246e
maflcko: re-ACKce8094246e
🐈 hodlinator: ACKce8094246e
dergoegge: utACKce8094246e
Tree-SHA512: 79bc0cbafaf27e95012c1ce2947a8ca6f9a3c78af5f1f16e69354b6fc9b987a28858adf4cd356dc5baf21163e9af8dcc24e70f8d7173be870e8a3ddcdd47c02c
This commit is contained in:
commit
5c0cd205a1
42 changed files with 738 additions and 564 deletions
|
@ -173,8 +173,7 @@ BITCOIN_TESTS =\
|
|||
test/validation_flush_tests.cpp \
|
||||
test/validation_tests.cpp \
|
||||
test/validationinterface_tests.cpp \
|
||||
test/versionbits_tests.cpp \
|
||||
test/xoroshiro128plusplus_tests.cpp
|
||||
test/versionbits_tests.cpp
|
||||
|
||||
if ENABLE_WALLET
|
||||
BITCOIN_TESTS += \
|
||||
|
|
|
@ -23,8 +23,7 @@ TEST_UTIL_H = \
|
|||
test/util/str.h \
|
||||
test/util/transaction_utils.h \
|
||||
test/util/txmempool.h \
|
||||
test/util/validation.h \
|
||||
test/util/xoroshiro128plusplus.h
|
||||
test/util/validation.h
|
||||
|
||||
if ENABLE_WALLET
|
||||
TEST_UTIL_H += wallet/test/util.h
|
||||
|
|
|
@ -53,7 +53,7 @@ template <typename Data>
|
|||
bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data)
|
||||
{
|
||||
// Generate random temporary filename
|
||||
const uint16_t randv{GetRand<uint16_t>()};
|
||||
const uint16_t randv{FastRandomContext().rand<uint16_t>()};
|
||||
std::string tmpfn = strprintf("%s.%04x", prefix, randv);
|
||||
|
||||
// open temp output file
|
||||
|
|
|
@ -776,7 +776,7 @@ std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool new_only, std::option
|
|||
const AddrInfo& info{it_found->second};
|
||||
|
||||
// With probability GetChance() * chance_factor, return the entry.
|
||||
if (insecure_rand.randbits(30) < chance_factor * info.GetChance() * (1 << 30)) {
|
||||
if (insecure_rand.randbits<30>() < chance_factor * info.GetChance() * (1 << 30)) {
|
||||
LogPrint(BCLog::ADDRMAN, "Selected %s from %s\n", info.ToStringAddrPort(), search_tried ? "tried" : "new");
|
||||
return {info, info.m_last_try};
|
||||
}
|
||||
|
|
|
@ -239,7 +239,7 @@ bool CRollingBloomFilter::contains(Span<const unsigned char> vKey) const
|
|||
|
||||
void CRollingBloomFilter::reset()
|
||||
{
|
||||
nTweak = GetRand<unsigned int>();
|
||||
nTweak = FastRandomContext().rand<unsigned int>();
|
||||
nEntriesThisGeneration = 0;
|
||||
nGeneration = 1;
|
||||
std::fill(data.begin(), data.end(), 0);
|
||||
|
|
|
@ -25,7 +25,7 @@ static_assert(sizeof(CompressedHeader) == 48);
|
|||
|
||||
HeadersSyncState::HeadersSyncState(NodeId id, const Consensus::Params& consensus_params,
|
||||
const CBlockIndex* chain_start, const arith_uint256& minimum_required_work) :
|
||||
m_commit_offset(GetRand<unsigned>(HEADER_COMMITMENT_PERIOD)),
|
||||
m_commit_offset(FastRandomContext().randrange<unsigned>(HEADER_COMMITMENT_PERIOD)),
|
||||
m_id(id), m_consensus_params(consensus_params),
|
||||
m_chain_start(chain_start),
|
||||
m_minimum_required_work(minimum_required_work),
|
||||
|
|
|
@ -1273,11 +1273,12 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
node.addrman = std::move(*addrman);
|
||||
}
|
||||
|
||||
FastRandomContext rng;
|
||||
assert(!node.banman);
|
||||
node.banman = std::make_unique<BanMan>(args.GetDataDirNet() / "banlist", &uiInterface, args.GetIntArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
|
||||
assert(!node.connman);
|
||||
node.connman = std::make_unique<CConnman>(GetRand<uint64_t>(),
|
||||
GetRand<uint64_t>(),
|
||||
node.connman = std::make_unique<CConnman>(rng.rand64(),
|
||||
rng.rand64(),
|
||||
*node.addrman, *node.netgroupman, chainparams, args.GetBoolArg("-networkactive", true));
|
||||
|
||||
assert(!node.fee_estimator);
|
||||
|
|
15
src/net.cpp
15
src/net.cpp
|
@ -2481,9 +2481,9 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
|
|||
auto start = GetTime<std::chrono::microseconds>();
|
||||
|
||||
// Minimum time before next feeler connection (in microseconds).
|
||||
auto next_feeler = GetExponentialRand(start, FEELER_INTERVAL);
|
||||
auto next_extra_block_relay = GetExponentialRand(start, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
|
||||
auto next_extra_network_peer{GetExponentialRand(start, EXTRA_NETWORK_PEER_INTERVAL)};
|
||||
auto next_feeler = start + rng.rand_exp_duration(FEELER_INTERVAL);
|
||||
auto next_extra_block_relay = start + rng.rand_exp_duration(EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
|
||||
auto next_extra_network_peer{start + rng.rand_exp_duration(EXTRA_NETWORK_PEER_INTERVAL)};
|
||||
const bool dnsseed = gArgs.GetBoolArg("-dnsseed", DEFAULT_DNSSEED);
|
||||
bool add_fixed_seeds = gArgs.GetBoolArg("-fixedseeds", DEFAULT_FIXEDSEEDS);
|
||||
const bool use_seednodes{gArgs.IsArgSet("-seednode")};
|
||||
|
@ -2642,10 +2642,10 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
|
|||
// Because we can promote these connections to block-relay-only
|
||||
// connections, they do not get their own ConnectionType enum
|
||||
// (similar to how we deal with extra outbound peers).
|
||||
next_extra_block_relay = GetExponentialRand(now, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
|
||||
next_extra_block_relay = now + rng.rand_exp_duration(EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
|
||||
conn_type = ConnectionType::BLOCK_RELAY;
|
||||
} else if (now > next_feeler) {
|
||||
next_feeler = GetExponentialRand(now, FEELER_INTERVAL);
|
||||
next_feeler = now + rng.rand_exp_duration(FEELER_INTERVAL);
|
||||
conn_type = ConnectionType::FEELER;
|
||||
fFeeler = true;
|
||||
} else if (nOutboundFullRelay == m_max_outbound_full_relay &&
|
||||
|
@ -2658,7 +2658,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
|
|||
// This is not attempted if the user changed -maxconnections to a value
|
||||
// so low that less than MAX_OUTBOUND_FULL_RELAY_CONNECTIONS are made,
|
||||
// to prevent interactions with otherwise protected outbound peers.
|
||||
next_extra_network_peer = GetExponentialRand(now, EXTRA_NETWORK_PEER_INTERVAL);
|
||||
next_extra_network_peer = now + rng.rand_exp_duration(EXTRA_NETWORK_PEER_INTERVAL);
|
||||
} else {
|
||||
// skip to next iteration of while loop
|
||||
continue;
|
||||
|
@ -3475,7 +3475,8 @@ std::vector<CAddress> 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) + GetRandMillis(std::chrono::hours(6));
|
||||
cache_entry.m_cache_entry_expiration = current_time +
|
||||
21h + FastRandomContext().randrange<std::chrono::microseconds>(6h);
|
||||
}
|
||||
return cache_entry.m_addrs_response_cache;
|
||||
}
|
||||
|
|
|
@ -936,7 +936,7 @@ private:
|
|||
* accurately determine when we received the transaction (and potentially
|
||||
* determine the transaction's origin). */
|
||||
std::chrono::microseconds NextInvToInbounds(std::chrono::microseconds now,
|
||||
std::chrono::seconds average_interval);
|
||||
std::chrono::seconds average_interval) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
|
||||
|
||||
|
||||
// All of the following cache a recent block, and are protected by m_most_recent_block_mutex
|
||||
|
@ -1244,7 +1244,7 @@ std::chrono::microseconds PeerManagerImpl::NextInvToInbounds(std::chrono::micros
|
|||
// If this function were called from multiple threads simultaneously
|
||||
// it would possible that both update the next send variable, and return a different result to their caller.
|
||||
// This is not possible in practice as only the net processing thread invokes this function.
|
||||
m_next_inv_to_inbounds = GetExponentialRand(now, average_interval);
|
||||
m_next_inv_to_inbounds = now + m_rng.rand_exp_duration(average_interval);
|
||||
}
|
||||
return m_next_inv_to_inbounds;
|
||||
}
|
||||
|
@ -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<std::chrono::milliseconds>(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<std::chrono::milliseconds>(5min);
|
||||
scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta);
|
||||
}
|
||||
|
||||
|
@ -2124,7 +2124,7 @@ void PeerManagerImpl::BlockDisconnected(const std::shared_ptr<const CBlock> &blo
|
|||
*/
|
||||
void PeerManagerImpl::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock)
|
||||
{
|
||||
auto pcmpctblock = std::make_shared<const CBlockHeaderAndShortTxIDs>(*pblock, GetRand<uint64_t>());
|
||||
auto pcmpctblock = std::make_shared<const CBlockHeaderAndShortTxIDs>(*pblock, FastRandomContext().rand64());
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
|
@ -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()};
|
||||
MakeAndPushMessage(pfrom, NetMsgType::CMPCTBLOCK, cmpctblock);
|
||||
}
|
||||
} else {
|
||||
|
@ -5617,7 +5617,7 @@ void PeerManagerImpl::MaybeSendPing(CNode& node_to, Peer& peer, std::chrono::mic
|
|||
if (pingSend) {
|
||||
uint64_t nonce;
|
||||
do {
|
||||
nonce = GetRand<uint64_t>();
|
||||
nonce = FastRandomContext().rand64();
|
||||
} while (nonce == 0);
|
||||
peer.m_ping_queued = false;
|
||||
peer.m_ping_start = now;
|
||||
|
@ -5654,13 +5654,13 @@ void PeerManagerImpl::MaybeSendAddr(CNode& node, Peer& peer, std::chrono::micros
|
|||
CAddress local_addr{*local_service, peer.m_our_services, Now<NodeSeconds>()};
|
||||
PushAddress(peer, local_addr);
|
||||
}
|
||||
peer.m_next_local_addr_send = GetExponentialRand(current_time, AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
|
||||
peer.m_next_local_addr_send = current_time + m_rng.rand_exp_duration(AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
|
||||
}
|
||||
|
||||
// We sent an `addr` message to this peer recently. Nothing more to do.
|
||||
if (current_time <= peer.m_next_addr_send) return;
|
||||
|
||||
peer.m_next_addr_send = GetExponentialRand(current_time, AVG_ADDRESS_BROADCAST_INTERVAL);
|
||||
peer.m_next_addr_send = current_time + m_rng.rand_exp_duration(AVG_ADDRESS_BROADCAST_INTERVAL);
|
||||
|
||||
if (!Assume(peer.m_addrs_to_send.size() <= MAX_ADDR_TO_SEND)) {
|
||||
// Should be impossible since we always check size before adding to
|
||||
|
@ -5747,13 +5747,13 @@ void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, Peer& peer, std::chrono::mi
|
|||
MakeAndPushMessage(pto, NetMsgType::FEEFILTER, filterToSend);
|
||||
peer.m_fee_filter_sent = filterToSend;
|
||||
}
|
||||
peer.m_next_send_feefilter = GetExponentialRand(current_time, AVG_FEEFILTER_BROADCAST_INTERVAL);
|
||||
peer.m_next_send_feefilter = current_time + m_rng.rand_exp_duration(AVG_FEEFILTER_BROADCAST_INTERVAL);
|
||||
}
|
||||
// If the fee filter has changed substantially and it's still more than MAX_FEEFILTER_CHANGE_DELAY
|
||||
// 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<std::chrono::microseconds>(MAX_FEEFILTER_CHANGE_DELAY);
|
||||
peer.m_next_send_feefilter = current_time + m_rng.randrange<std::chrono::microseconds>(MAX_FEEFILTER_CHANGE_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5984,7 +5984,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
|||
CBlock block;
|
||||
const bool ret{m_chainman.m_blockman.ReadBlockFromDisk(block, *pBestIndex)};
|
||||
assert(ret);
|
||||
CBlockHeaderAndShortTxIDs cmpctblock{block, GetRand<uint64_t>()};
|
||||
CBlockHeaderAndShortTxIDs cmpctblock{block, m_rng.rand64()};
|
||||
MakeAndPushMessage(*pto, NetMsgType::CMPCTBLOCK, cmpctblock);
|
||||
}
|
||||
state.pindexBestHeaderSent = pBestIndex;
|
||||
|
@ -6059,7 +6059,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
|||
if (pto->IsInboundConn()) {
|
||||
tx_relay->m_next_inv_send_time = NextInvToInbounds(current_time, INBOUND_INVENTORY_BROADCAST_INTERVAL);
|
||||
} else {
|
||||
tx_relay->m_next_inv_send_time = GetExponentialRand(current_time, OUTBOUND_INVENTORY_BROADCAST_INTERVAL);
|
||||
tx_relay->m_next_inv_send_time = current_time + m_rng.rand_exp_duration(OUTBOUND_INVENTORY_BROADCAST_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -567,8 +567,8 @@ class CServiceHash
|
|||
{
|
||||
public:
|
||||
CServiceHash()
|
||||
: m_salt_k0{GetRand<uint64_t>()},
|
||||
m_salt_k1{GetRand<uint64_t>()}
|
||||
: m_salt_k0{FastRandomContext().rand64()},
|
||||
m_salt_k1{FastRandomContext().rand64()}
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ public:
|
|||
LOCK(m_txreconciliation_mutex);
|
||||
|
||||
LogPrintLevel(BCLog::TXRECONCILIATION, BCLog::Level::Debug, "Pre-register peer=%d\n", peer_id);
|
||||
const uint64_t local_salt{GetRand(UINT64_MAX)};
|
||||
const uint64_t local_salt{FastRandomContext().rand64()};
|
||||
|
||||
// We do this exactly once per peer (which are unique by NodeId, see GetNewNodeId) so it's
|
||||
// safe to assume we don't have this record yet.
|
||||
|
|
205
src/random.cpp
205
src/random.cpp
|
@ -23,6 +23,7 @@
|
|||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
|
||||
#ifdef WIN32
|
||||
|
@ -44,13 +45,23 @@
|
|||
#include <sys/auxv.h>
|
||||
#endif
|
||||
|
||||
[[noreturn]] static void RandFailure()
|
||||
namespace {
|
||||
|
||||
/* 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.
|
||||
* (many cap out at 256 bytes).
|
||||
*/
|
||||
static const int NUM_OS_RANDOM_BYTES = 32;
|
||||
|
||||
|
||||
[[noreturn]] void RandFailure()
|
||||
{
|
||||
LogPrintf("Failed to read randomness, aborting\n");
|
||||
LogError("Failed to read randomness, aborting\n");
|
||||
std::abort();
|
||||
}
|
||||
|
||||
static inline int64_t GetPerformanceCounter() noexcept
|
||||
inline int64_t GetPerformanceCounter() noexcept
|
||||
{
|
||||
// Read the hardware time stamp counter when available.
|
||||
// See https://en.wikipedia.org/wiki/Time_Stamp_Counter for more information.
|
||||
|
@ -71,10 +82,10 @@ static inline int64_t GetPerformanceCounter() noexcept
|
|||
}
|
||||
|
||||
#ifdef HAVE_GETCPUID
|
||||
static bool g_rdrand_supported = false;
|
||||
static bool g_rdseed_supported = false;
|
||||
static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000;
|
||||
static constexpr uint32_t CPUID_F7_EBX_RDSEED = 0x00040000;
|
||||
bool g_rdrand_supported = false;
|
||||
bool g_rdseed_supported = false;
|
||||
constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000;
|
||||
constexpr uint32_t CPUID_F7_EBX_RDSEED = 0x00040000;
|
||||
#ifdef bit_RDRND
|
||||
static_assert(CPUID_F1_ECX_RDRAND == bit_RDRND, "Unexpected value for bit_RDRND");
|
||||
#endif
|
||||
|
@ -82,7 +93,7 @@ static_assert(CPUID_F1_ECX_RDRAND == bit_RDRND, "Unexpected value for bit_RDRND"
|
|||
static_assert(CPUID_F7_EBX_RDSEED == bit_RDSEED, "Unexpected value for bit_RDSEED");
|
||||
#endif
|
||||
|
||||
static void InitHardwareRand()
|
||||
void InitHardwareRand()
|
||||
{
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
GetCPUID(1, 0, eax, ebx, ecx, edx);
|
||||
|
@ -95,7 +106,7 @@ static void InitHardwareRand()
|
|||
}
|
||||
}
|
||||
|
||||
static void ReportHardwareRand()
|
||||
void ReportHardwareRand()
|
||||
{
|
||||
// This must be done in a separate function, as InitHardwareRand() may be indirectly called
|
||||
// from global constructors, before logging is initialized.
|
||||
|
@ -111,7 +122,7 @@ static void ReportHardwareRand()
|
|||
*
|
||||
* Must only be called when RdRand is supported.
|
||||
*/
|
||||
static uint64_t GetRdRand() noexcept
|
||||
uint64_t GetRdRand() noexcept
|
||||
{
|
||||
// RdRand may very rarely fail. Invoke it up to 10 times in a loop to reduce this risk.
|
||||
#ifdef __i386__
|
||||
|
@ -146,7 +157,7 @@ static uint64_t GetRdRand() noexcept
|
|||
*
|
||||
* Must only be called when RdSeed is supported.
|
||||
*/
|
||||
static uint64_t GetRdSeed() noexcept
|
||||
uint64_t GetRdSeed() noexcept
|
||||
{
|
||||
// RdSeed may fail when the HW RNG is overloaded. Loop indefinitely until enough entropy is gathered,
|
||||
// but pause after every failure.
|
||||
|
@ -180,16 +191,16 @@ static uint64_t GetRdSeed() noexcept
|
|||
|
||||
#elif defined(__aarch64__) && defined(HWCAP2_RNG)
|
||||
|
||||
static bool g_rndr_supported = false;
|
||||
bool g_rndr_supported = false;
|
||||
|
||||
static void InitHardwareRand()
|
||||
void InitHardwareRand()
|
||||
{
|
||||
if (getauxval(AT_HWCAP2) & HWCAP2_RNG) {
|
||||
g_rndr_supported = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void ReportHardwareRand()
|
||||
void ReportHardwareRand()
|
||||
{
|
||||
// This must be done in a separate function, as InitHardwareRand() may be indirectly called
|
||||
// from global constructors, before logging is initialized.
|
||||
|
@ -202,7 +213,7 @@ static void ReportHardwareRand()
|
|||
*
|
||||
* Must only be called when RNDR is supported.
|
||||
*/
|
||||
static uint64_t GetRNDR() noexcept
|
||||
uint64_t GetRNDR() noexcept
|
||||
{
|
||||
uint8_t ok;
|
||||
uint64_t r1;
|
||||
|
@ -220,7 +231,7 @@ static uint64_t GetRNDR() noexcept
|
|||
*
|
||||
* Must only be called when RNDRRS is supported.
|
||||
*/
|
||||
static uint64_t GetRNDRRS() noexcept
|
||||
uint64_t GetRNDRRS() noexcept
|
||||
{
|
||||
uint8_t ok;
|
||||
uint64_t r1;
|
||||
|
@ -240,12 +251,12 @@ static uint64_t GetRNDRRS() noexcept
|
|||
* Slower sources should probably be invoked separately, and/or only from
|
||||
* RandAddPeriodic (which is called once a minute).
|
||||
*/
|
||||
static void InitHardwareRand() {}
|
||||
static void ReportHardwareRand() {}
|
||||
void InitHardwareRand() {}
|
||||
void ReportHardwareRand() {}
|
||||
#endif
|
||||
|
||||
/** Add 64 bits of entropy gathered from hardware to hasher. Do nothing if not supported. */
|
||||
static void SeedHardwareFast(CSHA512& hasher) noexcept {
|
||||
void SeedHardwareFast(CSHA512& hasher) noexcept {
|
||||
#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
|
||||
if (g_rdrand_supported) {
|
||||
uint64_t out = GetRdRand();
|
||||
|
@ -262,7 +273,7 @@ static void SeedHardwareFast(CSHA512& hasher) noexcept {
|
|||
}
|
||||
|
||||
/** Add 256 bits of entropy gathered from hardware to hasher. Do nothing if not supported. */
|
||||
static void SeedHardwareSlow(CSHA512& hasher) noexcept {
|
||||
void SeedHardwareSlow(CSHA512& hasher) noexcept {
|
||||
#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
|
||||
// When we want 256 bits of entropy, prefer RdSeed over RdRand, as it's
|
||||
// guaranteed to produce independent randomness on every call.
|
||||
|
@ -295,7 +306,7 @@ static void SeedHardwareSlow(CSHA512& hasher) noexcept {
|
|||
}
|
||||
|
||||
/** Use repeated SHA512 to strengthen the randomness in seed32, and feed into hasher. */
|
||||
static void Strengthen(const unsigned char (&seed)[32], SteadyClock::duration dur, CSHA512& hasher) noexcept
|
||||
void Strengthen(const unsigned char (&seed)[32], SteadyClock::duration dur, CSHA512& hasher) noexcept
|
||||
{
|
||||
CSHA512 inner_hasher;
|
||||
inner_hasher.Write(seed, sizeof(seed));
|
||||
|
@ -326,7 +337,7 @@ static void Strengthen(const unsigned char (&seed)[32], SteadyClock::duration du
|
|||
/** Fallback: get 32 bytes of system entropy from /dev/urandom. The most
|
||||
* compatible way to get cryptographic randomness on UNIX-ish platforms.
|
||||
*/
|
||||
[[maybe_unused]] static void GetDevURandom(unsigned char *ent32)
|
||||
[[maybe_unused]] void GetDevURandom(unsigned char *ent32)
|
||||
{
|
||||
int f = open("/dev/urandom", O_RDONLY);
|
||||
if (f == -1) {
|
||||
|
@ -401,8 +412,6 @@ void GetOSRand(unsigned char *ent32)
|
|||
#endif
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class RNGState {
|
||||
Mutex m_mutex;
|
||||
/* The RNG state consists of 256 bits of entropy, taken from the output of
|
||||
|
@ -417,6 +426,10 @@ class RNGState {
|
|||
uint64_t m_counter GUARDED_BY(m_mutex) = 0;
|
||||
bool m_strongly_seeded GUARDED_BY(m_mutex) = false;
|
||||
|
||||
/** If not nullopt, the output of this RNGState is redirected and drawn from here
|
||||
* (unless always_use_real_rng is passed to MixExtract). */
|
||||
std::optional<ChaCha20> m_deterministic_prng GUARDED_BY(m_mutex);
|
||||
|
||||
Mutex m_events_mutex;
|
||||
CSHA256 m_events_hasher GUARDED_BY(m_events_mutex);
|
||||
|
||||
|
@ -457,11 +470,21 @@ public:
|
|||
m_events_hasher.Write(events_hash, 32);
|
||||
}
|
||||
|
||||
/** Make the output of MixExtract (unless always_use_real_rng) deterministic, with specified seed. */
|
||||
void MakeDeterministic(const uint256& seed) noexcept EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
||||
{
|
||||
LOCK(m_mutex);
|
||||
m_deterministic_prng.emplace(MakeByteSpan(seed));
|
||||
}
|
||||
|
||||
/** Extract up to 32 bytes of entropy from the RNG state, mixing in new entropy from hasher.
|
||||
*
|
||||
* If this function has never been called with strong_seed = true, false is returned.
|
||||
*
|
||||
* If always_use_real_rng is false, and MakeDeterministic has been called before, output
|
||||
* from the deterministic PRNG instead.
|
||||
*/
|
||||
bool MixExtract(unsigned char* out, size_t num, CSHA512&& hasher, bool strong_seed) noexcept EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
||||
bool MixExtract(unsigned char* out, size_t num, CSHA512&& hasher, bool strong_seed, bool always_use_real_rng) noexcept EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
||||
{
|
||||
assert(num <= 32);
|
||||
unsigned char buf[64];
|
||||
|
@ -479,6 +502,13 @@ public:
|
|||
hasher.Finalize(buf);
|
||||
// Store the last 32 bytes of the hash output as new RNG state.
|
||||
memcpy(m_state, buf + 32, 32);
|
||||
// Handle requests for deterministic randomness.
|
||||
if (!always_use_real_rng && m_deterministic_prng.has_value()) [[unlikely]] {
|
||||
// Overwrite the beginning of buf, which will be used for output.
|
||||
m_deterministic_prng->Keystream(AsWritableBytes(Span{buf, num}));
|
||||
// Do not require strong seeding for deterministic output.
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
// If desired, copy (up to) the first 32 bytes of the hash output as output.
|
||||
if (num) {
|
||||
|
@ -499,20 +529,19 @@ RNGState& GetRNGState() noexcept
|
|||
static std::vector<RNGState, secure_allocator<RNGState>> g_rng(1);
|
||||
return g_rng[0];
|
||||
}
|
||||
}
|
||||
|
||||
/* A note on the use of noexcept in the seeding functions below:
|
||||
*
|
||||
* None of the RNG code should ever throw any exception.
|
||||
*/
|
||||
|
||||
static void SeedTimestamp(CSHA512& hasher) noexcept
|
||||
void SeedTimestamp(CSHA512& hasher) noexcept
|
||||
{
|
||||
int64_t perfcounter = GetPerformanceCounter();
|
||||
hasher.Write((const unsigned char*)&perfcounter, sizeof(perfcounter));
|
||||
}
|
||||
|
||||
static void SeedFast(CSHA512& hasher) noexcept
|
||||
void SeedFast(CSHA512& hasher) noexcept
|
||||
{
|
||||
unsigned char buffer[32];
|
||||
|
||||
|
@ -527,7 +556,7 @@ static void SeedFast(CSHA512& hasher) noexcept
|
|||
SeedTimestamp(hasher);
|
||||
}
|
||||
|
||||
static void SeedSlow(CSHA512& hasher, RNGState& rng) noexcept
|
||||
void SeedSlow(CSHA512& hasher, RNGState& rng) noexcept
|
||||
{
|
||||
unsigned char buffer[32];
|
||||
|
||||
|
@ -549,16 +578,17 @@ static void SeedSlow(CSHA512& hasher, RNGState& rng) noexcept
|
|||
}
|
||||
|
||||
/** Extract entropy from rng, strengthen it, and feed it into hasher. */
|
||||
static void SeedStrengthen(CSHA512& hasher, RNGState& rng, SteadyClock::duration dur) noexcept
|
||||
void SeedStrengthen(CSHA512& hasher, RNGState& rng, SteadyClock::duration dur) noexcept
|
||||
{
|
||||
// Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher.
|
||||
// Never use the deterministic PRNG for this, as the result is only used internally.
|
||||
unsigned char strengthen_seed[32];
|
||||
rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false);
|
||||
rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false, /*always_use_real_rng=*/true);
|
||||
// Strengthen the seed, and feed it into hasher.
|
||||
Strengthen(strengthen_seed, dur, hasher);
|
||||
}
|
||||
|
||||
static void SeedPeriodic(CSHA512& hasher, RNGState& rng) noexcept
|
||||
void SeedPeriodic(CSHA512& hasher, RNGState& rng) noexcept
|
||||
{
|
||||
// Everything that the 'fast' seeder includes
|
||||
SeedFast(hasher);
|
||||
|
@ -578,7 +608,7 @@ static void SeedPeriodic(CSHA512& hasher, RNGState& rng) noexcept
|
|||
SeedStrengthen(hasher, rng, 10ms);
|
||||
}
|
||||
|
||||
static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept
|
||||
void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept
|
||||
{
|
||||
// Gather 256 bits of hardware randomness, if available
|
||||
SeedHardwareSlow(hasher);
|
||||
|
@ -604,7 +634,7 @@ enum class RNGLevel {
|
|||
PERIODIC, //!< Called by RandAddPeriodic()
|
||||
};
|
||||
|
||||
static void ProcRand(unsigned char* out, int num, RNGLevel level) noexcept
|
||||
void ProcRand(unsigned char* out, int num, RNGLevel level, bool always_use_real_rng) noexcept
|
||||
{
|
||||
// Make sure the RNG is initialized first (as all Seed* function possibly need hwrand to be available).
|
||||
RNGState& rng = GetRNGState();
|
||||
|
@ -625,65 +655,61 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level) noexcept
|
|||
}
|
||||
|
||||
// Combine with and update state
|
||||
if (!rng.MixExtract(out, num, std::move(hasher), false)) {
|
||||
if (!rng.MixExtract(out, num, std::move(hasher), false, always_use_real_rng)) {
|
||||
// On the first invocation, also seed with SeedStartup().
|
||||
CSHA512 startup_hasher;
|
||||
SeedStartup(startup_hasher, rng);
|
||||
rng.MixExtract(out, num, std::move(startup_hasher), true);
|
||||
rng.MixExtract(out, num, std::move(startup_hasher), true, always_use_real_rng);
|
||||
}
|
||||
}
|
||||
|
||||
void GetRandBytes(Span<unsigned char> bytes) noexcept { ProcRand(bytes.data(), bytes.size(), RNGLevel::FAST); }
|
||||
void GetStrongRandBytes(Span<unsigned char> bytes) noexcept { ProcRand(bytes.data(), bytes.size(), RNGLevel::SLOW); }
|
||||
void RandAddPeriodic() noexcept { ProcRand(nullptr, 0, RNGLevel::PERIODIC); }
|
||||
} // namespace
|
||||
|
||||
|
||||
/** Internal function to set g_determinstic_rng. Only accessed from tests. */
|
||||
void MakeRandDeterministicDANGEROUS(const uint256& seed) noexcept
|
||||
{
|
||||
GetRNGState().MakeDeterministic(seed);
|
||||
}
|
||||
|
||||
void GetRandBytes(Span<unsigned char> bytes) noexcept
|
||||
{
|
||||
ProcRand(bytes.data(), bytes.size(), RNGLevel::FAST, /*always_use_real_rng=*/false);
|
||||
}
|
||||
|
||||
void GetStrongRandBytes(Span<unsigned char> bytes) noexcept
|
||||
{
|
||||
ProcRand(bytes.data(), bytes.size(), RNGLevel::SLOW, /*always_use_real_rng=*/true);
|
||||
}
|
||||
|
||||
void RandAddPeriodic() noexcept
|
||||
{
|
||||
ProcRand(nullptr, 0, RNGLevel::PERIODIC, /*always_use_real_rng=*/false);
|
||||
}
|
||||
|
||||
void RandAddEvent(const uint32_t event_info) noexcept { GetRNGState().AddEvent(event_info); }
|
||||
|
||||
bool g_mock_deterministic_tests{false};
|
||||
|
||||
uint64_t GetRandInternal(uint64_t nMax) noexcept
|
||||
{
|
||||
return FastRandomContext(g_mock_deterministic_tests).randrange(nMax);
|
||||
}
|
||||
|
||||
uint256 GetRandHash() noexcept
|
||||
{
|
||||
uint256 hash;
|
||||
GetRandBytes(hash);
|
||||
return hash;
|
||||
}
|
||||
|
||||
void FastRandomContext::RandomSeed()
|
||||
void FastRandomContext::RandomSeed() noexcept
|
||||
{
|
||||
uint256 seed = GetRandHash();
|
||||
rng.SetKey(MakeByteSpan(seed));
|
||||
requires_seed = false;
|
||||
}
|
||||
|
||||
uint256 FastRandomContext::rand256() noexcept
|
||||
{
|
||||
if (requires_seed) RandomSeed();
|
||||
uint256 ret;
|
||||
rng.Keystream(MakeWritableByteSpan(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename B>
|
||||
std::vector<B> FastRandomContext::randbytes(size_t len)
|
||||
{
|
||||
std::vector<B> ret(len);
|
||||
fillrand(MakeWritableByteSpan(ret));
|
||||
return ret;
|
||||
}
|
||||
template std::vector<unsigned char> FastRandomContext::randbytes(size_t);
|
||||
template std::vector<std::byte> FastRandomContext::randbytes(size_t);
|
||||
|
||||
void FastRandomContext::fillrand(Span<std::byte> output)
|
||||
void FastRandomContext::fillrand(Span<std::byte> output) noexcept
|
||||
{
|
||||
if (requires_seed) RandomSeed();
|
||||
rng.Keystream(output);
|
||||
}
|
||||
|
||||
FastRandomContext::FastRandomContext(const uint256& seed) noexcept : requires_seed(false), rng(MakeByteSpan(seed)), bitbuf_size(0) {}
|
||||
FastRandomContext::FastRandomContext(const uint256& seed) noexcept : requires_seed(false), rng(MakeByteSpan(seed)) {}
|
||||
|
||||
void FastRandomContext::Reseed(const uint256& seed) noexcept
|
||||
{
|
||||
FlushCache();
|
||||
requires_seed = false;
|
||||
rng = {MakeByteSpan(seed)};
|
||||
}
|
||||
|
||||
bool Random_SanityCheck()
|
||||
{
|
||||
|
@ -726,41 +752,38 @@ bool Random_SanityCheck()
|
|||
CSHA512 to_add;
|
||||
to_add.Write((const unsigned char*)&start, sizeof(start));
|
||||
to_add.Write((const unsigned char*)&stop, sizeof(stop));
|
||||
GetRNGState().MixExtract(nullptr, 0, std::move(to_add), false);
|
||||
GetRNGState().MixExtract(nullptr, 0, std::move(to_add), false, /*always_use_real_rng=*/true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static constexpr std::array<std::byte, ChaCha20::KEYLEN> ZERO_KEY{};
|
||||
|
||||
FastRandomContext::FastRandomContext(bool fDeterministic) noexcept : requires_seed(!fDeterministic), rng(ZERO_KEY), bitbuf_size(0)
|
||||
FastRandomContext::FastRandomContext(bool fDeterministic) noexcept : requires_seed(!fDeterministic), rng(ZERO_KEY)
|
||||
{
|
||||
// Note that despite always initializing with ZERO_KEY, requires_seed is set to true if not
|
||||
// fDeterministic. That means the rng will be reinitialized with a secure random key upon first
|
||||
// use.
|
||||
}
|
||||
|
||||
FastRandomContext& FastRandomContext::operator=(FastRandomContext&& from) noexcept
|
||||
{
|
||||
requires_seed = from.requires_seed;
|
||||
rng = from.rng;
|
||||
bitbuf = from.bitbuf;
|
||||
bitbuf_size = from.bitbuf_size;
|
||||
from.requires_seed = true;
|
||||
from.bitbuf_size = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void RandomInit()
|
||||
{
|
||||
// Invoke RNG code to trigger initialization (if not already performed)
|
||||
ProcRand(nullptr, 0, RNGLevel::FAST);
|
||||
ProcRand(nullptr, 0, RNGLevel::FAST, /*always_use_real_rng=*/true);
|
||||
|
||||
ReportHardwareRand();
|
||||
}
|
||||
|
||||
std::chrono::microseconds GetExponentialRand(std::chrono::microseconds now, std::chrono::seconds average_interval)
|
||||
double MakeExponentiallyDistributed(uint64_t uniform) noexcept
|
||||
{
|
||||
double unscaled = -std::log1p(GetRand(uint64_t{1} << 48) * -0.0000000000000035527136788 /* -1/2^48 */);
|
||||
return now + std::chrono::duration_cast<std::chrono::microseconds>(unscaled * average_interval + 0.5us);
|
||||
// To convert uniform into an exponentially-distributed double, we use two steps:
|
||||
// - Convert uniform into a uniformly-distributed double in range [0, 1), use the expression
|
||||
// ((uniform >> 11) * 0x1.0p-53), as described in https://prng.di.unimi.it/ under
|
||||
// "Generating uniform doubles in the unit interval". Call this value x.
|
||||
// - Given an x in uniformly distributed in [0, 1), we find an exponentially distributed value
|
||||
// by applying the quantile function to it. For the exponential distribution with mean 1 this
|
||||
// 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);
|
||||
}
|
||||
|
|
525
src/random.h
525
src/random.h
|
@ -10,12 +10,15 @@
|
|||
#include <crypto/common.h>
|
||||
#include <span.h>
|
||||
#include <uint256.h>
|
||||
#include <util/check.h>
|
||||
|
||||
#include <bit>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
|
@ -25,8 +28,8 @@
|
|||
* The following (classes of) functions interact with that state by mixing in new
|
||||
* entropy, and optionally extracting random output from it:
|
||||
*
|
||||
* - The GetRand*() class of functions, as well as construction of FastRandomContext objects,
|
||||
* perform 'fast' seeding, consisting of mixing in:
|
||||
* - GetRandBytes, GetRandHash, GetRandDur, as well as construction of FastRandomContext
|
||||
* objects, perform 'fast' seeding, consisting of mixing in:
|
||||
* - A stack pointer (indirectly committing to calling thread and call stack)
|
||||
* - A high-precision timestamp (rdtsc when available, c++ high_resolution_clock otherwise)
|
||||
* - 64 bits from the hardware RNG (rdrand) when available.
|
||||
|
@ -35,7 +38,7 @@
|
|||
* FastRandomContext on the other hand does not protect against this once created, but
|
||||
* is even faster (and acceptable to use inside tight loops).
|
||||
*
|
||||
* - The GetStrongRand*() class of function perform 'slow' seeding, including everything
|
||||
* - The GetStrongRandBytes() function performs 'slow' seeding, including everything
|
||||
* that fast seeding includes, but additionally:
|
||||
* - OS entropy (/dev/urandom, getrandom(), ...). The application will terminate if
|
||||
* this entropy source fails.
|
||||
|
@ -50,75 +53,34 @@
|
|||
* - Strengthen the entropy for 10 ms using repeated SHA512.
|
||||
* This is run once every minute.
|
||||
*
|
||||
* On first use of the RNG (regardless of what function is called first), all entropy
|
||||
* sources used in the 'slow' seeder are included, but also:
|
||||
* - 256 bits from the hardware RNG (rdseed or rdrand) when available.
|
||||
* - Dynamic environment data (performance monitoring, ...)
|
||||
* - Static environment data
|
||||
* - Strengthen the entropy for 100 ms using repeated SHA512.
|
||||
* - On first use of the RNG (regardless of what function is called first), all entropy
|
||||
* sources used in the 'slow' seeder are included, but also:
|
||||
* - 256 bits from the hardware RNG (rdseed or rdrand) when available.
|
||||
* - Dynamic environment data (performance monitoring, ...)
|
||||
* - Static environment data
|
||||
* - Strengthen the entropy for 100 ms using repeated SHA512.
|
||||
*
|
||||
* When mixing in new entropy, H = SHA512(entropy || old_rng_state) is computed, and
|
||||
* (up to) the first 32 bytes of H are produced as output, while the last 32 bytes
|
||||
* become the new RNG state.
|
||||
*
|
||||
* During tests, the RNG can be put into a special deterministic mode, in which the output
|
||||
* of all RNG functions, with the exception of GetStrongRandBytes(), is replaced with the
|
||||
* output of a deterministic RNG. This deterministic RNG does not gather entropy, and is
|
||||
* unaffected by RandAddPeriodic() or RandAddEvent(). It produces pseudorandom data that
|
||||
* only depends on the seed it was initialized with, possibly until it is reinitialized.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generate random data via the internal PRNG.
|
||||
*
|
||||
* These functions are designed to be fast (sub microsecond), but do not necessarily
|
||||
* meaningfully add entropy to the PRNG state.
|
||||
*
|
||||
* Thread-safe.
|
||||
*/
|
||||
void GetRandBytes(Span<unsigned char> bytes) noexcept;
|
||||
/** Generate a uniform random integer in the range [0..range). Precondition: range > 0 */
|
||||
uint64_t GetRandInternal(uint64_t nMax) noexcept;
|
||||
/** Generate a uniform random integer of type T in the range [0..nMax)
|
||||
* nMax defaults to std::numeric_limits<T>::max()
|
||||
* Precondition: nMax > 0, T is an integral type, no larger than uint64_t
|
||||
*/
|
||||
template<typename T>
|
||||
T GetRand(T nMax=std::numeric_limits<T>::max()) noexcept {
|
||||
static_assert(std::is_integral<T>(), "T must be integral");
|
||||
static_assert(std::numeric_limits<T>::max() <= std::numeric_limits<uint64_t>::max(), "GetRand only supports up to uint64_t");
|
||||
return T(GetRandInternal(nMax));
|
||||
}
|
||||
/** Generate a uniform random duration in the range [0..max). Precondition: max.count() > 0 */
|
||||
template <typename D>
|
||||
D GetRandomDuration(typename std::common_type<D>::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.
|
||||
{
|
||||
assert(max.count() > 0);
|
||||
return D{GetRand(max.count())};
|
||||
};
|
||||
constexpr auto GetRandMicros = GetRandomDuration<std::chrono::microseconds>;
|
||||
constexpr auto GetRandMillis = GetRandomDuration<std::chrono::milliseconds>;
|
||||
|
||||
/* ============================= INITIALIZATION AND ADDING ENTROPY ============================= */
|
||||
|
||||
/**
|
||||
* Return a timestamp in the future sampled from an exponential distribution
|
||||
* (https://en.wikipedia.org/wiki/Exponential_distribution). This distribution
|
||||
* is memoryless and should be used for repeated network events (e.g. sending a
|
||||
* certain type of message) to minimize leaking information to observers.
|
||||
* Initialize global RNG state and log any CPU features that are used.
|
||||
*
|
||||
* The probability of an event occurring before time x is 1 - e^-(x/a) where a
|
||||
* is the average interval between events.
|
||||
* */
|
||||
std::chrono::microseconds GetExponentialRand(std::chrono::microseconds now, std::chrono::seconds average_interval);
|
||||
|
||||
uint256 GetRandHash() noexcept;
|
||||
|
||||
/**
|
||||
* Gather entropy from various sources, feed it into the internal PRNG, and
|
||||
* generate random data using it.
|
||||
*
|
||||
* This function will cause failure whenever the OS RNG fails.
|
||||
*
|
||||
* Thread-safe.
|
||||
* Calling this function is optional. RNG state will be initialized when first
|
||||
* needed if it is not called.
|
||||
*/
|
||||
void GetStrongRandBytes(Span<unsigned char> bytes) noexcept;
|
||||
void RandomInit();
|
||||
|
||||
/**
|
||||
* Gather entropy from various expensive sources, and feed them to the PRNG state.
|
||||
|
@ -135,42 +97,299 @@ void RandAddPeriodic() noexcept;
|
|||
*/
|
||||
void RandAddEvent(const uint32_t event_info) noexcept;
|
||||
|
||||
|
||||
/* =========================== BASE RANDOMNESS GENERATION FUNCTIONS ===========================
|
||||
*
|
||||
* All produced randomness is eventually generated by one of these functions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generate random data via the internal PRNG.
|
||||
*
|
||||
* These functions are designed to be fast (sub microsecond), but do not necessarily
|
||||
* meaningfully add entropy to the PRNG state.
|
||||
*
|
||||
* In test mode (see SeedRandomForTest in src/test/util/random.h), the normal PRNG state is
|
||||
* bypassed, and a deterministic, seeded, PRNG is used instead.
|
||||
*
|
||||
* Thread-safe.
|
||||
*/
|
||||
void GetRandBytes(Span<unsigned char> bytes) noexcept;
|
||||
|
||||
/**
|
||||
* Gather entropy from various sources, feed it into the internal PRNG, and
|
||||
* generate random data using it.
|
||||
*
|
||||
* This function will cause failure whenever the OS RNG fails.
|
||||
*
|
||||
* The normal PRNG is never bypassed here, even in test mode.
|
||||
*
|
||||
* Thread-safe.
|
||||
*/
|
||||
void GetStrongRandBytes(Span<unsigned char> bytes) noexcept;
|
||||
|
||||
|
||||
/* ============================= RANDOM NUMBER GENERATION CLASSES =============================
|
||||
*
|
||||
* In this section, 3 classes are defined:
|
||||
* - RandomMixin: a base class that adds functionality to all RNG classes.
|
||||
* - FastRandomContext: a cryptographic RNG (seeded through GetRandBytes in its default
|
||||
* constructor).
|
||||
* - InsecureRandomContext: a non-cryptographic, very fast, RNG.
|
||||
*/
|
||||
|
||||
// Forward declaration of RandomMixin, used in RandomNumberGenerator concept.
|
||||
template<typename T>
|
||||
class RandomMixin;
|
||||
|
||||
/** A concept for RandomMixin-based random number generators. */
|
||||
template<typename T>
|
||||
concept RandomNumberGenerator = requires(T& rng, Span<std::byte> s) {
|
||||
// A random number generator must provide rand64().
|
||||
{ rng.rand64() } noexcept -> std::same_as<uint64_t>;
|
||||
// A random number generator must derive from RandomMixin, which adds other rand* functions.
|
||||
requires std::derived_from<std::remove_reference_t<T>, RandomMixin<std::remove_reference_t<T>>>;
|
||||
};
|
||||
|
||||
/** A concept for C++ std::chrono durations. */
|
||||
template<typename T>
|
||||
concept StdChronoDuration = requires {
|
||||
[]<class Rep, class Period>(std::type_identity<std::chrono::duration<Rep, Period>>){}(
|
||||
std::type_identity<T>());
|
||||
};
|
||||
|
||||
/** Given a uniformly random uint64_t, return an exponentially distributed double with mean 1. */
|
||||
double MakeExponentiallyDistributed(uint64_t uniform) noexcept;
|
||||
|
||||
/** Mixin class that provides helper randomness functions.
|
||||
*
|
||||
* Intended to be used through CRTP: https://en.cppreference.com/w/cpp/language/crtp.
|
||||
* An RNG class FunkyRNG would derive publicly from RandomMixin<FunkyRNG>. This permits
|
||||
* RandomMixin from accessing the derived class's rand64() function, while also allowing
|
||||
* the derived class to provide more.
|
||||
*
|
||||
* The derived class must satisfy the RandomNumberGenerator concept.
|
||||
*/
|
||||
template<typename T>
|
||||
class RandomMixin
|
||||
{
|
||||
private:
|
||||
uint64_t bitbuf{0};
|
||||
int bitbuf_size{0};
|
||||
|
||||
/** Access the underlying generator.
|
||||
*
|
||||
* This also enforces the RandomNumberGenerator concept. We cannot declare that in the template
|
||||
* (no template<RandomNumberGenerator T>) because the type isn't fully instantiated yet there.
|
||||
*/
|
||||
RandomNumberGenerator auto& Impl() noexcept { return static_cast<T&>(*this); }
|
||||
|
||||
protected:
|
||||
constexpr void FlushCache() noexcept
|
||||
{
|
||||
bitbuf = 0;
|
||||
bitbuf_size = 0;
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr RandomMixin() noexcept = default;
|
||||
|
||||
// Do not permit copying or moving an RNG.
|
||||
RandomMixin(const RandomMixin&) = delete;
|
||||
RandomMixin& operator=(const RandomMixin&) = delete;
|
||||
RandomMixin(RandomMixin&&) = delete;
|
||||
RandomMixin& operator=(RandomMixin&&) = delete;
|
||||
|
||||
/** Generate a random (bits)-bit integer. */
|
||||
uint64_t randbits(int bits) noexcept
|
||||
{
|
||||
Assume(bits <= 64);
|
||||
// Requests for the full 64 bits are passed through.
|
||||
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.
|
||||
ret = bitbuf;
|
||||
bitbuf >>= bits;
|
||||
bitbuf_size -= bits;
|
||||
} else {
|
||||
// If not, return all of bitbuf, supplemented with the (bits - bitbuf_size) bottom
|
||||
// bits of a newly generated 64-bit number on top. The remainder of that generated
|
||||
// number becomes the new bitbuf.
|
||||
uint64_t gen = Impl().rand64();
|
||||
ret = (gen << bitbuf_size) | bitbuf;
|
||||
bitbuf = gen >> (bits - bitbuf_size);
|
||||
bitbuf_size = 64 + bitbuf_size - bits;
|
||||
}
|
||||
// Return the bottom bits bits of ret.
|
||||
return ret & ((uint64_t{1} << bits) - 1);
|
||||
}
|
||||
|
||||
/** Same as above, but with compile-time fixed bits count. */
|
||||
template<int Bits>
|
||||
uint64_t randbits() noexcept
|
||||
{
|
||||
static_assert(Bits >= 0 && Bits <= 64);
|
||||
if constexpr (Bits == 64) {
|
||||
return Impl().rand64();
|
||||
} else {
|
||||
uint64_t ret;
|
||||
if (Bits <= bitbuf_size) {
|
||||
ret = bitbuf;
|
||||
bitbuf >>= Bits;
|
||||
bitbuf_size -= Bits;
|
||||
} else {
|
||||
uint64_t gen = Impl().rand64();
|
||||
ret = (gen << bitbuf_size) | bitbuf;
|
||||
bitbuf = gen >> (Bits - bitbuf_size);
|
||||
bitbuf_size = 64 + bitbuf_size - Bits;
|
||||
}
|
||||
constexpr uint64_t MASK = (uint64_t{1} << Bits) - 1;
|
||||
return ret & MASK;
|
||||
}
|
||||
}
|
||||
|
||||
/** Generate a random integer in the range [0..range), with range > 0. */
|
||||
template<std::integral I>
|
||||
I randrange(I range) noexcept
|
||||
{
|
||||
static_assert(std::numeric_limits<I>::max() <= std::numeric_limits<uint64_t>::max());
|
||||
Assume(range > 0);
|
||||
uint64_t maxval = range - 1U;
|
||||
int bits = std::bit_width(maxval);
|
||||
while (true) {
|
||||
uint64_t ret = Impl().randbits(bits);
|
||||
if (ret <= maxval) return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/** Fill a Span with random bytes. */
|
||||
void fillrand(Span<std::byte> span) noexcept
|
||||
{
|
||||
while (span.size() >= 8) {
|
||||
uint64_t gen = Impl().rand64();
|
||||
WriteLE64(UCharCast(span.data()), gen);
|
||||
span = span.subspan(8);
|
||||
}
|
||||
if (span.size() >= 4) {
|
||||
uint32_t gen = Impl().rand32();
|
||||
WriteLE32(UCharCast(span.data()), gen);
|
||||
span = span.subspan(4);
|
||||
}
|
||||
while (span.size()) {
|
||||
span[0] = std::byte(Impl().template randbits<8>());
|
||||
span = span.subspan(1);
|
||||
}
|
||||
}
|
||||
|
||||
/** Generate a random integer in its entire (non-negative) range. */
|
||||
template<std::integral I>
|
||||
I rand() noexcept
|
||||
{
|
||||
static_assert(std::numeric_limits<I>::max() <= std::numeric_limits<uint64_t>::max());
|
||||
static constexpr auto BITS = std::bit_width(uint64_t(std::numeric_limits<I>::max()));
|
||||
static_assert(std::numeric_limits<I>::max() == std::numeric_limits<uint64_t>::max() >> (64 - BITS));
|
||||
return I(Impl().template randbits<BITS>());
|
||||
}
|
||||
|
||||
/** Generate random bytes. */
|
||||
template <BasicByte B = unsigned char>
|
||||
std::vector<B> randbytes(size_t len) noexcept
|
||||
{
|
||||
std::vector<B> ret(len);
|
||||
Impl().fillrand(MakeWritableByteSpan(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Generate a random 32-bit integer. */
|
||||
uint32_t rand32() noexcept { return Impl().template randbits<32>(); }
|
||||
|
||||
/** generate a random uint256. */
|
||||
uint256 rand256() noexcept
|
||||
{
|
||||
uint256 ret;
|
||||
Impl().fillrand(MakeWritableByteSpan(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Generate a random boolean. */
|
||||
bool randbool() noexcept { return Impl().template randbits<1>(); }
|
||||
|
||||
/** Return the time point advanced by a uniform random duration. */
|
||||
template <typename Tp>
|
||||
Tp rand_uniform_delay(const Tp& time, typename Tp::duration range) noexcept
|
||||
{
|
||||
return time + Impl().template rand_uniform_duration<Tp>(range);
|
||||
}
|
||||
|
||||
/** Generate a uniform random duration in the range from 0 (inclusive) to range (exclusive). */
|
||||
template <typename Chrono> requires StdChronoDuration<typename Chrono::duration>
|
||||
typename Chrono::duration rand_uniform_duration(typename Chrono::duration range) noexcept
|
||||
{
|
||||
using Dur = typename Chrono::duration;
|
||||
return range.count() > 0 ? /* interval [0..range) */ Dur{Impl().randrange(range.count())} :
|
||||
range.count() < 0 ? /* interval (range..0] */ -Dur{Impl().randrange(-range.count())} :
|
||||
/* interval [0..0] */ Dur{0};
|
||||
};
|
||||
|
||||
/** Generate a uniform random duration in the range [0..max). Precondition: max.count() > 0 */
|
||||
template <StdChronoDuration Dur>
|
||||
Dur randrange(typename std::common_type_t<Dur> 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())};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a duration sampled from an exponential distribution
|
||||
* (https://en.wikipedia.org/wiki/Exponential_distribution). Successive events
|
||||
* whose intervals are distributed according to this form a memoryless Poisson
|
||||
* process. This should be used for repeated network events (e.g. sending a
|
||||
* certain type of message) to minimize leaking information to observers.
|
||||
*
|
||||
* The probability of an event occurring before time x is 1 - e^-(x/a) where a
|
||||
* is the average interval between events.
|
||||
* */
|
||||
std::chrono::microseconds rand_exp_duration(std::chrono::microseconds mean) noexcept
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
auto unscaled = MakeExponentiallyDistributed(Impl().rand64());
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(unscaled * mean + 0.5us);
|
||||
}
|
||||
|
||||
// Compatibility with the UniformRandomBitGenerator concept
|
||||
typedef uint64_t result_type;
|
||||
static constexpr uint64_t min() noexcept { return 0; }
|
||||
static constexpr uint64_t max() noexcept { return std::numeric_limits<uint64_t>::max(); }
|
||||
inline uint64_t operator()() noexcept { return Impl().rand64(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* Fast randomness source. This is seeded once with secure random data, but
|
||||
* is completely deterministic and does not gather more entropy after that.
|
||||
*
|
||||
* This class is not thread-safe.
|
||||
*/
|
||||
class FastRandomContext
|
||||
class FastRandomContext : public RandomMixin<FastRandomContext>
|
||||
{
|
||||
private:
|
||||
bool requires_seed;
|
||||
ChaCha20 rng;
|
||||
|
||||
uint64_t bitbuf;
|
||||
int bitbuf_size;
|
||||
|
||||
void RandomSeed();
|
||||
|
||||
void FillBitBuffer()
|
||||
{
|
||||
bitbuf = rand64();
|
||||
bitbuf_size = 64;
|
||||
}
|
||||
void RandomSeed() noexcept;
|
||||
|
||||
public:
|
||||
/** Construct a FastRandomContext with GetRandHash()-based entropy (or zero key if fDeterministic). */
|
||||
explicit FastRandomContext(bool fDeterministic = false) noexcept;
|
||||
|
||||
/** Initialize with explicit seed (only for testing) */
|
||||
explicit FastRandomContext(const uint256& seed) noexcept;
|
||||
|
||||
// Do not permit copying a FastRandomContext (move it, or create a new one to get reseeded).
|
||||
FastRandomContext(const FastRandomContext&) = delete;
|
||||
FastRandomContext(FastRandomContext&&) = delete;
|
||||
FastRandomContext& operator=(const FastRandomContext&) = delete;
|
||||
|
||||
/** Move a FastRandomContext. If the original one is used again, it will be reseeded. */
|
||||
FastRandomContext& operator=(FastRandomContext&& from) noexcept;
|
||||
/** Reseed with explicit seed (only for testing). */
|
||||
void Reseed(const uint256& seed) noexcept;
|
||||
|
||||
/** Generate a random 64-bit integer. */
|
||||
uint64_t rand64() noexcept
|
||||
|
@ -181,76 +400,64 @@ public:
|
|||
return ReadLE64(UCharCast(buf.data()));
|
||||
}
|
||||
|
||||
/** Generate a random (bits)-bit integer. */
|
||||
uint64_t randbits(int bits) noexcept
|
||||
{
|
||||
if (bits == 0) {
|
||||
return 0;
|
||||
} else if (bits > 32) {
|
||||
return rand64() >> (64 - bits);
|
||||
} else {
|
||||
if (bitbuf_size < bits) FillBitBuffer();
|
||||
uint64_t ret = bitbuf & (~uint64_t{0} >> (64 - bits));
|
||||
bitbuf >>= bits;
|
||||
bitbuf_size -= bits;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/** Generate a random integer in the range [0..range).
|
||||
* Precondition: range > 0.
|
||||
*/
|
||||
uint64_t randrange(uint64_t range) noexcept
|
||||
{
|
||||
assert(range);
|
||||
--range;
|
||||
int bits = std::bit_width(range);
|
||||
while (true) {
|
||||
uint64_t ret = randbits(bits);
|
||||
if (ret <= range) return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/** Generate random bytes. */
|
||||
template <typename B = unsigned char>
|
||||
std::vector<B> randbytes(size_t len);
|
||||
|
||||
/** Fill a byte Span with random bytes. */
|
||||
void fillrand(Span<std::byte> output);
|
||||
|
||||
/** Generate a random 32-bit integer. */
|
||||
uint32_t rand32() noexcept { return randbits(32); }
|
||||
|
||||
/** generate a random uint256. */
|
||||
uint256 rand256() noexcept;
|
||||
|
||||
/** Generate a random boolean. */
|
||||
bool randbool() noexcept { return randbits(1); }
|
||||
|
||||
/** Return the time point advanced by a uniform random duration. */
|
||||
template <typename Tp>
|
||||
Tp rand_uniform_delay(const Tp& time, typename Tp::duration range)
|
||||
{
|
||||
return time + rand_uniform_duration<Tp>(range);
|
||||
}
|
||||
|
||||
/** Generate a uniform random duration in the range from 0 (inclusive) to range (exclusive). */
|
||||
template <typename Chrono>
|
||||
typename Chrono::duration rand_uniform_duration(typename Chrono::duration range) noexcept
|
||||
{
|
||||
using Dur = typename Chrono::duration;
|
||||
return range.count() > 0 ? /* interval [0..range) */ Dur{randrange(range.count())} :
|
||||
range.count() < 0 ? /* interval (range..0] */ -Dur{randrange(-range.count())} :
|
||||
/* interval [0..0] */ Dur{0};
|
||||
};
|
||||
|
||||
// Compatibility with the UniformRandomBitGenerator concept
|
||||
typedef uint64_t result_type;
|
||||
static constexpr uint64_t min() { return 0; }
|
||||
static constexpr uint64_t max() { return std::numeric_limits<uint64_t>::max(); }
|
||||
inline uint64_t operator()() noexcept { return rand64(); }
|
||||
/** Fill a byte Span with random bytes. This overrides the RandomMixin version. */
|
||||
void fillrand(Span<std::byte> output) noexcept;
|
||||
};
|
||||
|
||||
/** xoroshiro128++ PRNG. Extremely fast, not appropriate for cryptographic purposes.
|
||||
*
|
||||
* Memory footprint is very small, period is 2^128 - 1.
|
||||
* This class is not thread-safe.
|
||||
*
|
||||
* Reference implementation available at https://prng.di.unimi.it/xoroshiro128plusplus.c
|
||||
* See https://prng.di.unimi.it/
|
||||
*/
|
||||
class InsecureRandomContext : public RandomMixin<InsecureRandomContext>
|
||||
{
|
||||
uint64_t m_s0;
|
||||
uint64_t m_s1;
|
||||
|
||||
[[nodiscard]] constexpr static uint64_t SplitMix64(uint64_t& seedval) noexcept
|
||||
{
|
||||
uint64_t z = (seedval += 0x9e3779b97f4a7c15);
|
||||
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
|
||||
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
|
||||
return z ^ (z >> 31);
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr explicit InsecureRandomContext(uint64_t seedval) noexcept
|
||||
: m_s0(SplitMix64(seedval)), m_s1(SplitMix64(seedval)) {}
|
||||
|
||||
constexpr void Reseed(uint64_t seedval) noexcept
|
||||
{
|
||||
FlushCache();
|
||||
m_s0 = SplitMix64(seedval);
|
||||
m_s1 = SplitMix64(seedval);
|
||||
}
|
||||
|
||||
constexpr uint64_t rand64() noexcept
|
||||
{
|
||||
uint64_t s0 = m_s0, s1 = m_s1;
|
||||
const uint64_t result = std::rotl(s0 + s1, 17) + s0;
|
||||
s1 ^= s0;
|
||||
m_s0 = std::rotl(s0, 49) ^ s1 ^ (s1 << 21);
|
||||
m_s1 = std::rotl(s1, 28);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* ==================== CONVENIENCE FUNCTIONS FOR COMMONLY USED RANDOMNESS ==================== */
|
||||
|
||||
/** Generate a random uint256. */
|
||||
inline uint256 GetRandHash() noexcept
|
||||
{
|
||||
uint256 hash;
|
||||
GetRandBytes(hash);
|
||||
return hash;
|
||||
}
|
||||
|
||||
/** More efficient than using std::shuffle on a FastRandomContext.
|
||||
*
|
||||
* This is more efficient as std::shuffle will consume entropy in groups of
|
||||
|
@ -261,7 +468,7 @@ public:
|
|||
* debug mode detects and panics on. This is a known issue, see
|
||||
* https://stackoverflow.com/questions/22915325/avoiding-self-assignment-in-stdshuffle
|
||||
*/
|
||||
template <typename I, typename R>
|
||||
template <typename I, RandomNumberGenerator R>
|
||||
void Shuffle(I first, I last, R&& rng)
|
||||
{
|
||||
while (first != last) {
|
||||
|
@ -274,29 +481,11 @@ void Shuffle(I first, I last, R&& rng)
|
|||
}
|
||||
}
|
||||
|
||||
/* 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.
|
||||
* (many cap out at 256 bytes).
|
||||
*/
|
||||
static const int NUM_OS_RANDOM_BYTES = 32;
|
||||
|
||||
/** Get 32 bytes of system entropy. Do not use this in application code: use
|
||||
* GetStrongRandBytes instead.
|
||||
*/
|
||||
void GetOSRand(unsigned char* ent32);
|
||||
/* ============================= MISCELLANEOUS TEST-ONLY FUNCTIONS ============================= */
|
||||
|
||||
/** Check that OS randomness is available and returning the requested number
|
||||
* of bytes.
|
||||
*/
|
||||
bool Random_SanityCheck();
|
||||
|
||||
/**
|
||||
* Initialize global RNG state and log any CPU features that are used.
|
||||
*
|
||||
* Calling this function is optional. RNG state will be initialized when first
|
||||
* needed if it is not called.
|
||||
*/
|
||||
void RandomInit();
|
||||
|
||||
#endif // BITCOIN_RANDOM_H
|
||||
|
|
|
@ -463,8 +463,7 @@ static std::vector<unsigned char> RandomData()
|
|||
|
||||
BOOST_AUTO_TEST_CASE(rolling_bloom)
|
||||
{
|
||||
SeedInsecureRand(SeedRand::ZEROS);
|
||||
g_mock_deterministic_tests = true;
|
||||
SeedRandomForTest(SeedRand::ZEROS);
|
||||
|
||||
// last-100-entry, 1% false positive:
|
||||
CRollingBloomFilter rb1(100, 0.01);
|
||||
|
@ -491,7 +490,7 @@ BOOST_AUTO_TEST_CASE(rolling_bloom)
|
|||
++nHits;
|
||||
}
|
||||
// Expect about 100 hits
|
||||
BOOST_CHECK_EQUAL(nHits, 75U);
|
||||
BOOST_CHECK_EQUAL(nHits, 71U);
|
||||
|
||||
BOOST_CHECK(rb1.contains(data[DATASIZE-1]));
|
||||
rb1.reset();
|
||||
|
@ -519,7 +518,7 @@ BOOST_AUTO_TEST_CASE(rolling_bloom)
|
|||
++nHits;
|
||||
}
|
||||
// Expect about 5 false positives
|
||||
BOOST_CHECK_EQUAL(nHits, 6U);
|
||||
BOOST_CHECK_EQUAL(nHits, 3U);
|
||||
|
||||
// last-1000-entry, 0.01% false positive:
|
||||
CRollingBloomFilter rb2(1000, 0.001);
|
||||
|
@ -530,7 +529,6 @@ BOOST_AUTO_TEST_CASE(rolling_bloom)
|
|||
for (int i = 0; i < DATASIZE; i++) {
|
||||
BOOST_CHECK(rb2.contains(data[i]));
|
||||
}
|
||||
g_mock_deterministic_tests = false;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
@ -307,8 +307,7 @@ UtxoData::iterator FindRandomFrom(const std::set<COutPoint> &utxoSet) {
|
|||
// has the expected effect (the other duplicate is overwritten at all cache levels)
|
||||
BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
|
||||
{
|
||||
SeedInsecureRand(SeedRand::ZEROS);
|
||||
g_mock_deterministic_tests = true;
|
||||
SeedRandomForTest(SeedRand::ZEROS);
|
||||
|
||||
bool spent_a_duplicate_coinbase = false;
|
||||
// A simple map to track what we expect the cache stack to represent.
|
||||
|
@ -496,8 +495,6 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
|
|||
|
||||
// Verify coverage.
|
||||
BOOST_CHECK(spent_a_duplicate_coinbase);
|
||||
|
||||
g_mock_deterministic_tests = false;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ccoins_serialization)
|
||||
|
|
|
@ -1195,7 +1195,7 @@ BOOST_AUTO_TEST_CASE(muhash_tests)
|
|||
uint256 res;
|
||||
int table[4];
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
table[i] = g_insecure_rand_ctx.randbits(3);
|
||||
table[i] = g_insecure_rand_ctx.randbits<3>();
|
||||
}
|
||||
for (int order = 0; order < 4; ++order) {
|
||||
MuHash3072 acc;
|
||||
|
@ -1215,8 +1215,8 @@ BOOST_AUTO_TEST_CASE(muhash_tests)
|
|||
}
|
||||
}
|
||||
|
||||
MuHash3072 x = FromInt(g_insecure_rand_ctx.randbits(4)); // x=X
|
||||
MuHash3072 y = FromInt(g_insecure_rand_ctx.randbits(4)); // x=X, y=Y
|
||||
MuHash3072 x = FromInt(g_insecure_rand_ctx.randbits<4>()); // x=X
|
||||
MuHash3072 y = FromInt(g_insecure_rand_ctx.randbits<4>()); // x=X, y=Y
|
||||
MuHash3072 z; // x=X, y=Y, z=1
|
||||
z *= x; // x=X, y=Y, z=X
|
||||
z *= y; // x=X, y=Y, z=X*Y
|
||||
|
|
|
@ -33,11 +33,11 @@ BOOST_AUTO_TEST_SUITE(cuckoocache_tests);
|
|||
|
||||
/* Test that no values not inserted into the cache are read out of it.
|
||||
*
|
||||
* There are no repeats in the first 200000 insecure_GetRandHash calls
|
||||
* There are no repeats in the first 200000 InsecureRand256() calls
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes)
|
||||
{
|
||||
SeedInsecureRand(SeedRand::ZEROS);
|
||||
SeedRandomForTest(SeedRand::ZEROS);
|
||||
CuckooCache::cache<uint256, SignatureCacheHasher> cc{};
|
||||
size_t megabytes = 4;
|
||||
cc.setup_bytes(megabytes << 20);
|
||||
|
@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes)
|
|||
template <typename Cache>
|
||||
static double test_cache(size_t megabytes, double load)
|
||||
{
|
||||
SeedInsecureRand(SeedRand::ZEROS);
|
||||
SeedRandomForTest(SeedRand::ZEROS);
|
||||
std::vector<uint256> hashes;
|
||||
Cache set{};
|
||||
size_t bytes = megabytes * (1 << 20);
|
||||
|
@ -126,7 +126,7 @@ template <typename Cache>
|
|||
static void test_cache_erase(size_t megabytes)
|
||||
{
|
||||
double load = 1;
|
||||
SeedInsecureRand(SeedRand::ZEROS);
|
||||
SeedRandomForTest(SeedRand::ZEROS);
|
||||
std::vector<uint256> hashes;
|
||||
Cache set{};
|
||||
size_t bytes = megabytes * (1 << 20);
|
||||
|
@ -189,7 +189,7 @@ template <typename Cache>
|
|||
static void test_cache_erase_parallel(size_t megabytes)
|
||||
{
|
||||
double load = 1;
|
||||
SeedInsecureRand(SeedRand::ZEROS);
|
||||
SeedRandomForTest(SeedRand::ZEROS);
|
||||
std::vector<uint256> hashes;
|
||||
Cache set{};
|
||||
size_t bytes = megabytes * (1 << 20);
|
||||
|
@ -293,7 +293,7 @@ static void test_cache_generations()
|
|||
// iterations with non-deterministic values, so it isn't "overfit" to the
|
||||
// specific entropy in FastRandomContext(true) and implementation of the
|
||||
// cache.
|
||||
SeedInsecureRand(SeedRand::ZEROS);
|
||||
SeedRandomForTest(SeedRand::ZEROS);
|
||||
|
||||
// block_activity models a chunk of network activity. n_insert elements are
|
||||
// added to the cache. The first and last n/4 are stored for removal later
|
||||
|
|
|
@ -124,7 +124,7 @@ public:
|
|||
explicit AddrManDeterministic(const NetGroupManager& netgroupman, FuzzedDataProvider& fuzzed_data_provider)
|
||||
: AddrMan(netgroupman, /*deterministic=*/true, GetCheckRatio())
|
||||
{
|
||||
WITH_LOCK(m_impl->cs, m_impl->insecure_rand = FastRandomContext{ConsumeUInt256(fuzzed_data_provider)});
|
||||
WITH_LOCK(m_impl->cs, m_impl->insecure_rand.Reseed(ConsumeUInt256(fuzzed_data_provider)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
|
||||
#include <bip324.h>
|
||||
#include <chainparams.h>
|
||||
#include <random.h>
|
||||
#include <span.h>
|
||||
#include <test/fuzz/FuzzedDataProvider.h>
|
||||
#include <test/fuzz/fuzz.h>
|
||||
#include <test/fuzz/util.h>
|
||||
#include <test/util/xoroshiro128plusplus.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
@ -56,7 +56,7 @@ FUZZ_TARGET(bip324_cipher_roundtrip, .init=Initialize)
|
|||
// (potentially buggy) edge cases triggered by specific values of contents/AAD, so we can avoid
|
||||
// reading the actual data for those from the fuzzer input (which would need large amounts of
|
||||
// data).
|
||||
XoRoShiRo128PlusPlus rng(provider.ConsumeIntegral<uint64_t>());
|
||||
InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
|
||||
|
||||
// Compare session IDs and garbage terminators.
|
||||
assert(initiator.GetSessionID() == responder.GetSessionID());
|
||||
|
@ -79,10 +79,8 @@ FUZZ_TARGET(bip324_cipher_roundtrip, .init=Initialize)
|
|||
unsigned length_bits = 2 * ((mode >> 5) & 7);
|
||||
unsigned length = provider.ConsumeIntegralInRange<unsigned>(0, (1 << length_bits) - 1);
|
||||
// Generate aad and content.
|
||||
std::vector<std::byte> aad(aad_length);
|
||||
for (auto& val : aad) val = std::byte{(uint8_t)rng()};
|
||||
std::vector<std::byte> contents(length);
|
||||
for (auto& val : contents) val = std::byte{(uint8_t)rng()};
|
||||
auto aad = rng.randbytes<std::byte>(aad_length);
|
||||
auto contents = rng.randbytes<std::byte>(length);
|
||||
|
||||
// Pick sides.
|
||||
auto& sender{from_init ? initiator : responder};
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <random.h>
|
||||
#include <span.h>
|
||||
#include <test/fuzz/util.h>
|
||||
#include <test/util/xoroshiro128plusplus.h>
|
||||
#include <util/bitset.h>
|
||||
|
||||
#include <bitset>
|
||||
|
@ -29,7 +29,7 @@ void TestType(FuzzBufferType buffer)
|
|||
* bitsets and their simulations do not matter for the purpose of detecting edge cases, thus
|
||||
* these are taken from a deterministically-seeded RNG instead. To provide some level of
|
||||
* variation however, pick the seed based on the buffer size and size of the chosen bitset. */
|
||||
XoRoShiRo128PlusPlus rng(buffer.size() + 0x10000 * S::Size());
|
||||
InsecureRandomContext rng(buffer.size() + 0x10000 * S::Size());
|
||||
|
||||
using Sim = std::bitset<S::Size()>;
|
||||
// Up to 4 real BitSets (initially 2).
|
||||
|
@ -124,7 +124,7 @@ void TestType(FuzzBufferType buffer)
|
|||
sim[dest].reset();
|
||||
real[dest] = S{};
|
||||
for (unsigned i = 0; i < S::Size(); ++i) {
|
||||
if (rng() & 1) {
|
||||
if (rng.randbool()) {
|
||||
sim[dest][i] = true;
|
||||
real[dest].Set(i);
|
||||
}
|
||||
|
@ -132,9 +132,9 @@ void TestType(FuzzBufferType buffer)
|
|||
break;
|
||||
} else if (dest < sim.size() && command-- == 0) {
|
||||
/* Assign initializer list. */
|
||||
unsigned r1 = rng() % S::Size();
|
||||
unsigned r2 = rng() % S::Size();
|
||||
unsigned r3 = rng() % S::Size();
|
||||
unsigned r1 = rng.randrange(S::Size());
|
||||
unsigned r2 = rng.randrange(S::Size());
|
||||
unsigned r3 = rng.randrange(S::Size());
|
||||
compare_fn(dest);
|
||||
sim[dest].reset();
|
||||
real[dest] = {r1, r2, r3};
|
||||
|
@ -166,8 +166,8 @@ void TestType(FuzzBufferType buffer)
|
|||
break;
|
||||
} else if (sim.size() < 4 && command-- == 0) {
|
||||
/* Construct with initializer list. */
|
||||
unsigned r1 = rng() % S::Size();
|
||||
unsigned r2 = rng() % S::Size();
|
||||
unsigned r1 = rng.randrange(S::Size());
|
||||
unsigned r2 = rng.randrange(S::Size());
|
||||
sim.emplace_back();
|
||||
sim.back().set(r1);
|
||||
sim.back().set(r2);
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <crypto/chacha20.h>
|
||||
#include <random.h>
|
||||
#include <test/fuzz/FuzzedDataProvider.h>
|
||||
#include <test/fuzz/fuzz.h>
|
||||
#include <test/fuzz/util.h>
|
||||
#include <test/util/xoroshiro128plusplus.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
|
@ -53,7 +53,7 @@ namespace
|
|||
once for a large block at once, and then the same data in chunks, comparing
|
||||
the outcome.
|
||||
|
||||
If UseCrypt, seeded Xoroshiro128++ output is used as input to Crypt().
|
||||
If UseCrypt, seeded InsecureRandomContext output is used as input to Crypt().
|
||||
If not, Keystream() is used directly, or sequences of 0x00 are encrypted.
|
||||
*/
|
||||
template<bool UseCrypt>
|
||||
|
@ -78,25 +78,11 @@ void ChaCha20SplitFuzz(FuzzedDataProvider& provider)
|
|||
data1.resize(total_bytes);
|
||||
data2.resize(total_bytes);
|
||||
|
||||
// If using Crypt(), initialize data1 and data2 with the same Xoroshiro128++ based
|
||||
// If using Crypt(), initialize data1 and data2 with the same InsecureRandomContext based
|
||||
// stream.
|
||||
if constexpr (UseCrypt) {
|
||||
uint64_t seed = provider.ConsumeIntegral<uint64_t>();
|
||||
XoRoShiRo128PlusPlus rng(seed);
|
||||
uint64_t bytes = 0;
|
||||
while (bytes < (total_bytes & ~uint64_t{7})) {
|
||||
uint64_t val = rng();
|
||||
WriteLE64(UCharCast(data1.data() + bytes), val);
|
||||
WriteLE64(UCharCast(data2.data() + bytes), val);
|
||||
bytes += 8;
|
||||
}
|
||||
if (bytes < total_bytes) {
|
||||
std::byte valbytes[8];
|
||||
uint64_t val = rng();
|
||||
WriteLE64(UCharCast(valbytes), val);
|
||||
std::copy(valbytes, valbytes + (total_bytes - bytes), data1.data() + bytes);
|
||||
std::copy(valbytes, valbytes + (total_bytes - bytes), data2.data() + bytes);
|
||||
}
|
||||
InsecureRandomContext(provider.ConsumeIntegral<uint64_t>()).fillrand(data1);
|
||||
std::copy(data1.begin(), data1.end(), data2.begin());
|
||||
}
|
||||
|
||||
// Whether UseCrypt is used or not, the two byte arrays must match.
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <netaddress.h>
|
||||
#include <netbase.h>
|
||||
#include <test/util/random.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <util/check.h>
|
||||
#include <util/fs.h>
|
||||
|
@ -101,6 +102,12 @@ void ResetCoverageCounters() {}
|
|||
|
||||
void initialize()
|
||||
{
|
||||
// By default, make the RNG deterministic with a fixed seed. This will affect all
|
||||
// randomness during the fuzz test, except:
|
||||
// - GetStrongRandBytes(), which is used for the creation of private key material.
|
||||
// - Creating a BasicTestingSetup or derived class will switch to a random seed.
|
||||
SeedRandomForTest(SeedRand::ZEROS);
|
||||
|
||||
// Terminate immediately if a fuzzing harness ever tries to create a socket.
|
||||
// Individual tests can override this by pointing CreateSock to a mocked alternative.
|
||||
CreateSock = [](int, int, int) -> std::unique_ptr<Sock> { std::terminate(); };
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <test/fuzz/FuzzedDataProvider.h>
|
||||
#include <test/fuzz/fuzz.h>
|
||||
#include <test/fuzz/util.h>
|
||||
#include <test/util/xoroshiro128plusplus.h>
|
||||
#include <util/chaintype.h>
|
||||
|
||||
#include <cassert>
|
||||
|
@ -104,7 +103,7 @@ FUZZ_TARGET(p2p_transport_serialization, .init = initialize_p2p_transport_serial
|
|||
|
||||
namespace {
|
||||
|
||||
template<typename R>
|
||||
template<RandomNumberGenerator R>
|
||||
void SimulationTest(Transport& initiator, Transport& responder, R& rng, FuzzedDataProvider& provider)
|
||||
{
|
||||
// Simulation test with two Transport objects, which send messages to each other, with
|
||||
|
@ -165,8 +164,7 @@ void SimulationTest(Transport& initiator, Transport& responder, R& rng, FuzzedDa
|
|||
// Determine size of message to send (limited to 75 kB for performance reasons).
|
||||
size_t size = provider.ConsumeIntegralInRange<uint32_t>(0, 75000);
|
||||
// Get payload of message from RNG.
|
||||
msg.data.resize(size);
|
||||
for (auto& v : msg.data) v = uint8_t(rng());
|
||||
msg.data = rng.randbytes(size);
|
||||
// Return.
|
||||
return msg;
|
||||
};
|
||||
|
@ -337,7 +335,7 @@ std::unique_ptr<Transport> MakeV1Transport(NodeId nodeid) noexcept
|
|||
return std::make_unique<V1Transport>(nodeid);
|
||||
}
|
||||
|
||||
template<typename RNG>
|
||||
template<RandomNumberGenerator RNG>
|
||||
std::unique_ptr<Transport> MakeV2Transport(NodeId nodeid, bool initiator, RNG& rng, FuzzedDataProvider& provider)
|
||||
{
|
||||
// Retrieve key
|
||||
|
@ -353,8 +351,7 @@ std::unique_ptr<Transport> MakeV2Transport(NodeId nodeid, bool initiator, RNG& r
|
|||
} else {
|
||||
// If it's longer, generate it from the RNG. This avoids having large amounts of
|
||||
// (hopefully) irrelevant data needing to be stored in the fuzzer data.
|
||||
garb.resize(garb_len);
|
||||
for (auto& v : garb) v = uint8_t(rng());
|
||||
garb = rng.randbytes(garb_len);
|
||||
}
|
||||
// Retrieve entropy
|
||||
auto ent = provider.ConsumeBytes<std::byte>(32);
|
||||
|
@ -378,7 +375,7 @@ FUZZ_TARGET(p2p_transport_bidirectional, .init = initialize_p2p_transport_serial
|
|||
{
|
||||
// Test with two V1 transports talking to each other.
|
||||
FuzzedDataProvider provider{buffer.data(), buffer.size()};
|
||||
XoRoShiRo128PlusPlus rng(provider.ConsumeIntegral<uint64_t>());
|
||||
InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
|
||||
auto t1 = MakeV1Transport(NodeId{0});
|
||||
auto t2 = MakeV1Transport(NodeId{1});
|
||||
if (!t1 || !t2) return;
|
||||
|
@ -389,7 +386,7 @@ FUZZ_TARGET(p2p_transport_bidirectional_v2, .init = initialize_p2p_transport_ser
|
|||
{
|
||||
// Test with two V2 transports talking to each other.
|
||||
FuzzedDataProvider provider{buffer.data(), buffer.size()};
|
||||
XoRoShiRo128PlusPlus rng(provider.ConsumeIntegral<uint64_t>());
|
||||
InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
|
||||
auto t1 = MakeV2Transport(NodeId{0}, true, rng, provider);
|
||||
auto t2 = MakeV2Transport(NodeId{1}, false, rng, provider);
|
||||
if (!t1 || !t2) return;
|
||||
|
@ -400,7 +397,7 @@ FUZZ_TARGET(p2p_transport_bidirectional_v1v2, .init = initialize_p2p_transport_s
|
|||
{
|
||||
// Test with a V1 initiator talking to a V2 responder.
|
||||
FuzzedDataProvider provider{buffer.data(), buffer.size()};
|
||||
XoRoShiRo128PlusPlus rng(provider.ConsumeIntegral<uint64_t>());
|
||||
InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
|
||||
auto t1 = MakeV1Transport(NodeId{0});
|
||||
auto t2 = MakeV2Transport(NodeId{1}, false, rng, provider);
|
||||
if (!t1 || !t2) return;
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <random.h>
|
||||
#include <span.h>
|
||||
#include <support/allocators/pool.h>
|
||||
#include <test/fuzz/FuzzedDataProvider.h>
|
||||
#include <test/fuzz/fuzz.h>
|
||||
#include <test/fuzz/util.h>
|
||||
#include <test/util/poolresourcetester.h>
|
||||
#include <test/util/xoroshiro128plusplus.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
|
@ -71,41 +71,14 @@ public:
|
|||
|
||||
void RandomContentFill(Entry& entry)
|
||||
{
|
||||
XoRoShiRo128PlusPlus rng(entry.seed);
|
||||
auto ptr = entry.span.data();
|
||||
auto size = entry.span.size();
|
||||
|
||||
while (size >= 8) {
|
||||
auto r = rng();
|
||||
std::memcpy(ptr, &r, 8);
|
||||
size -= 8;
|
||||
ptr += 8;
|
||||
}
|
||||
if (size > 0) {
|
||||
auto r = rng();
|
||||
std::memcpy(ptr, &r, size);
|
||||
}
|
||||
InsecureRandomContext(entry.seed).fillrand(entry.span);
|
||||
}
|
||||
|
||||
void RandomContentCheck(const Entry& entry)
|
||||
{
|
||||
XoRoShiRo128PlusPlus rng(entry.seed);
|
||||
auto ptr = entry.span.data();
|
||||
auto size = entry.span.size();
|
||||
|
||||
std::byte buf[8];
|
||||
while (size >= 8) {
|
||||
auto r = rng();
|
||||
std::memcpy(buf, &r, 8);
|
||||
assert(std::memcmp(buf, ptr, 8) == 0);
|
||||
size -= 8;
|
||||
ptr += 8;
|
||||
}
|
||||
if (size > 0) {
|
||||
auto r = rng();
|
||||
std::memcpy(buf, &r, size);
|
||||
assert(std::memcmp(buf, ptr, size) == 0);
|
||||
}
|
||||
std::vector<std::byte> expect(entry.span.size());
|
||||
InsecureRandomContext(entry.seed).fillrand(expect);
|
||||
assert(entry.span == expect);
|
||||
}
|
||||
|
||||
void Deallocate(const Entry& entry)
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <random.h>
|
||||
#include <span.h>
|
||||
#include <test/fuzz/util.h>
|
||||
#include <test/util/xoroshiro128plusplus.h>
|
||||
#include <util/vecdeque.h>
|
||||
|
||||
#include <deque>
|
||||
|
@ -28,7 +28,7 @@ void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
|
|||
{
|
||||
FuzzedDataProvider provider(buffer.data(), buffer.size());
|
||||
// Local RNG, only used for the seeds to initialize T objects with.
|
||||
XoRoShiRo128PlusPlus rng(provider.ConsumeIntegral<uint64_t>() ^ rng_tweak);
|
||||
InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>() ^ rng_tweak);
|
||||
|
||||
// Real circular buffers.
|
||||
std::vector<VecDeque<T>> real;
|
||||
|
@ -175,7 +175,7 @@ void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
|
|||
}
|
||||
if (existing_buffer_non_full && command-- == 0) {
|
||||
/* push_back() (copying) */
|
||||
tmp = T(rng());
|
||||
tmp = T(rng.rand64());
|
||||
size_t old_size = real[idx].size();
|
||||
size_t old_cap = real[idx].capacity();
|
||||
real[idx].push_back(*tmp);
|
||||
|
@ -191,7 +191,7 @@ void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
|
|||
}
|
||||
if (existing_buffer_non_full && command-- == 0) {
|
||||
/* push_back() (moving) */
|
||||
tmp = T(rng());
|
||||
tmp = T(rng.rand64());
|
||||
size_t old_size = real[idx].size();
|
||||
size_t old_cap = real[idx].capacity();
|
||||
sim[idx].push_back(*tmp);
|
||||
|
@ -207,7 +207,7 @@ void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
|
|||
}
|
||||
if (existing_buffer_non_full && command-- == 0) {
|
||||
/* emplace_back() */
|
||||
uint64_t seed{rng()};
|
||||
uint64_t seed{rng.rand64()};
|
||||
size_t old_size = real[idx].size();
|
||||
size_t old_cap = real[idx].capacity();
|
||||
sim[idx].emplace_back(seed);
|
||||
|
@ -223,7 +223,7 @@ void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
|
|||
}
|
||||
if (existing_buffer_non_full && command-- == 0) {
|
||||
/* push_front() (copying) */
|
||||
tmp = T(rng());
|
||||
tmp = T(rng.rand64());
|
||||
size_t old_size = real[idx].size();
|
||||
size_t old_cap = real[idx].capacity();
|
||||
real[idx].push_front(*tmp);
|
||||
|
@ -239,7 +239,7 @@ void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
|
|||
}
|
||||
if (existing_buffer_non_full && command-- == 0) {
|
||||
/* push_front() (moving) */
|
||||
tmp = T(rng());
|
||||
tmp = T(rng.rand64());
|
||||
size_t old_size = real[idx].size();
|
||||
size_t old_cap = real[idx].capacity();
|
||||
sim[idx].push_front(*tmp);
|
||||
|
@ -255,7 +255,7 @@ void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
|
|||
}
|
||||
if (existing_buffer_non_full && command-- == 0) {
|
||||
/* emplace_front() */
|
||||
uint64_t seed{rng()};
|
||||
uint64_t seed{rng.rand64()};
|
||||
size_t old_size = real[idx].size();
|
||||
size_t old_cap = real[idx].capacity();
|
||||
sim[idx].emplace_front(seed);
|
||||
|
@ -271,7 +271,7 @@ void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
|
|||
}
|
||||
if (existing_buffer_non_empty && command-- == 0) {
|
||||
/* front() [modifying] */
|
||||
tmp = T(rng());
|
||||
tmp = T(rng.rand64());
|
||||
size_t old_size = real[idx].size();
|
||||
assert(sim[idx].front() == real[idx].front());
|
||||
sim[idx].front() = *tmp;
|
||||
|
@ -281,7 +281,7 @@ void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
|
|||
}
|
||||
if (existing_buffer_non_empty && command-- == 0) {
|
||||
/* back() [modifying] */
|
||||
tmp = T(rng());
|
||||
tmp = T(rng.rand64());
|
||||
size_t old_size = real[idx].size();
|
||||
assert(sim[idx].back() == real[idx].back());
|
||||
sim[idx].back() = *tmp;
|
||||
|
@ -291,7 +291,7 @@ void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
|
|||
}
|
||||
if (existing_buffer_non_empty && command-- == 0) {
|
||||
/* operator[] [modifying] */
|
||||
tmp = T(rng());
|
||||
tmp = T(rng.rand64());
|
||||
size_t pos = provider.ConsumeIntegralInRange<size_t>(0, sim[idx].size() - 1);
|
||||
size_t old_size = real[idx].size();
|
||||
assert(sim[idx][pos] == real[idx][pos]);
|
||||
|
|
|
@ -106,7 +106,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
|
|||
// ecdsa_signature_parse_der_lax are executed during this test.
|
||||
// Specifically branches that run only when an ECDSA
|
||||
// signature's R and S values have leading zeros.
|
||||
g_insecure_rand_ctx = FastRandomContext{uint256{33}};
|
||||
g_insecure_rand_ctx.Reseed(uint256{33});
|
||||
|
||||
TxOrphanageTest orphanage;
|
||||
CKey key;
|
||||
|
|
|
@ -210,9 +210,9 @@ public:
|
|||
}
|
||||
|
||||
prevector_tester() {
|
||||
SeedInsecureRand();
|
||||
SeedRandomForTest();
|
||||
rand_seed = InsecureRand256();
|
||||
rand_cache = FastRandomContext(rand_seed);
|
||||
rand_cache.Reseed(rand_seed);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -20,28 +20,39 @@ BOOST_AUTO_TEST_CASE(osrandom_tests)
|
|||
BOOST_CHECK(Random_SanityCheck());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(fastrandom_tests)
|
||||
BOOST_AUTO_TEST_CASE(fastrandom_tests_deterministic)
|
||||
{
|
||||
// Check that deterministic FastRandomContexts are deterministic
|
||||
g_mock_deterministic_tests = true;
|
||||
FastRandomContext ctx1(true);
|
||||
FastRandomContext ctx2(true);
|
||||
SeedRandomForTest(SeedRand::ZEROS);
|
||||
FastRandomContext ctx1{true};
|
||||
FastRandomContext ctx2{true};
|
||||
|
||||
for (int i = 10; i > 0; --i) {
|
||||
BOOST_CHECK_EQUAL(GetRand<uint64_t>(), uint64_t{10393729187455219830U});
|
||||
BOOST_CHECK_EQUAL(GetRand<int>(), int{769702006});
|
||||
BOOST_CHECK_EQUAL(GetRandMicros(std::chrono::hours{1}).count(), 2917185654);
|
||||
BOOST_CHECK_EQUAL(GetRandMillis(std::chrono::hours{1}).count(), 2144374);
|
||||
{
|
||||
BOOST_CHECK_EQUAL(FastRandomContext().rand<uint64_t>(), uint64_t{9330418229102544152u});
|
||||
BOOST_CHECK_EQUAL(FastRandomContext().rand<int>(), int{618925161});
|
||||
BOOST_CHECK_EQUAL(FastRandomContext().randrange<std::chrono::microseconds>(1h).count(), 1271170921);
|
||||
BOOST_CHECK_EQUAL(FastRandomContext().randrange<std::chrono::milliseconds>(1h).count(), 2803534);
|
||||
|
||||
BOOST_CHECK_EQUAL(FastRandomContext().rand<uint64_t>(), uint64_t{10170981140880778086u});
|
||||
BOOST_CHECK_EQUAL(FastRandomContext().rand<int>(), int{1689082725});
|
||||
BOOST_CHECK_EQUAL(FastRandomContext().randrange<std::chrono::microseconds>(1h).count(), 2464643716);
|
||||
BOOST_CHECK_EQUAL(FastRandomContext().randrange<std::chrono::milliseconds>(1h).count(), 2312205);
|
||||
|
||||
BOOST_CHECK_EQUAL(FastRandomContext().rand<uint64_t>(), uint64_t{5689404004456455543u});
|
||||
BOOST_CHECK_EQUAL(FastRandomContext().rand<int>(), int{785839937});
|
||||
BOOST_CHECK_EQUAL(FastRandomContext().randrange<std::chrono::microseconds>(1h).count(), 93558804);
|
||||
BOOST_CHECK_EQUAL(FastRandomContext().randrange<std::chrono::milliseconds>(1h).count(), 507022);
|
||||
}
|
||||
|
||||
{
|
||||
constexpr SteadySeconds time_point{1s};
|
||||
FastRandomContext ctx{true};
|
||||
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());
|
||||
|
@ -65,15 +76,28 @@ BOOST_AUTO_TEST_CASE(fastrandom_tests)
|
|||
// Check with time-point type
|
||||
BOOST_CHECK_EQUAL(2782, ctx.rand_uniform_duration<SteadySeconds>(9h).count());
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(fastrandom_tests_nondeterministic)
|
||||
{
|
||||
// Check that a nondeterministic ones are not
|
||||
g_mock_deterministic_tests = false;
|
||||
for (int i = 10; i > 0; --i) {
|
||||
BOOST_CHECK(GetRand<uint64_t>() != uint64_t{10393729187455219830U});
|
||||
BOOST_CHECK(GetRand<int>() != int{769702006});
|
||||
BOOST_CHECK(GetRandMicros(std::chrono::hours{1}) != std::chrono::microseconds{2917185654});
|
||||
BOOST_CHECK(GetRandMillis(std::chrono::hours{1}) != std::chrono::milliseconds{2144374});
|
||||
{
|
||||
BOOST_CHECK(FastRandomContext().rand<uint64_t>() != uint64_t{9330418229102544152u});
|
||||
BOOST_CHECK(FastRandomContext().rand<int>() != int{618925161});
|
||||
BOOST_CHECK(FastRandomContext().randrange<std::chrono::microseconds>(1h).count() != 1271170921);
|
||||
BOOST_CHECK(FastRandomContext().randrange<std::chrono::milliseconds>(1h).count() != 2803534);
|
||||
|
||||
BOOST_CHECK(FastRandomContext().rand<uint64_t>() != uint64_t{10170981140880778086u});
|
||||
BOOST_CHECK(FastRandomContext().rand<int>() != int{1689082725});
|
||||
BOOST_CHECK(FastRandomContext().randrange<std::chrono::microseconds>(1h).count() != 2464643716);
|
||||
BOOST_CHECK(FastRandomContext().randrange<std::chrono::milliseconds>(1h).count() != 2312205);
|
||||
|
||||
BOOST_CHECK(FastRandomContext().rand<uint64_t>() != uint64_t{5689404004456455543u});
|
||||
BOOST_CHECK(FastRandomContext().rand<int>() != int{785839937});
|
||||
BOOST_CHECK(FastRandomContext().randrange<std::chrono::microseconds>(1h).count() != 93558804);
|
||||
BOOST_CHECK(FastRandomContext().randrange<std::chrono::milliseconds>(1h).count() != 507022);
|
||||
}
|
||||
|
||||
{
|
||||
FastRandomContext ctx3, ctx4;
|
||||
BOOST_CHECK(ctx3.rand64() != ctx4.rand64()); // extremely unlikely to be equal
|
||||
|
@ -103,6 +127,70 @@ 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_test1(true), ctx_test2(true); //!< The RNGs 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_test1.randbits(bits);
|
||||
// For certain bits counts, also test randbits<Bits> and compare.
|
||||
uint64_t gen2;
|
||||
if (bits == 0) {
|
||||
gen2 = ctx_test2.randbits<0>();
|
||||
} else if (bits == 1) {
|
||||
gen2 = ctx_test2.randbits<1>();
|
||||
} else if (bits == 7) {
|
||||
gen2 = ctx_test2.randbits<7>();
|
||||
} else if (bits == 32) {
|
||||
gen2 = ctx_test2.randbits<32>();
|
||||
} else if (bits == 51) {
|
||||
gen2 = ctx_test2.randbits<51>();
|
||||
} else if (bits == 64) {
|
||||
gen2 = ctx_test2.randbits<64>();
|
||||
} else {
|
||||
gen2 = ctx_test2.randbits(bits);
|
||||
}
|
||||
BOOST_CHECK_EQUAL(gen, gen2);
|
||||
// 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)
|
||||
{
|
||||
|
@ -155,4 +243,21 @@ BOOST_AUTO_TEST_CASE(shuffle_stat_test)
|
|||
BOOST_CHECK_EQUAL(sum, 12000U);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(xoroshiro128plusplus_reference_values)
|
||||
{
|
||||
// numbers generated from reference implementation
|
||||
InsecureRandomContext rng(0);
|
||||
BOOST_TEST(0x6f68e1e7e2646ee1 == rng());
|
||||
BOOST_TEST(0xbf971b7f454094ad == rng());
|
||||
BOOST_TEST(0x48f2de556f30de38 == rng());
|
||||
BOOST_TEST(0x6ea7c59f89bbfc75 == rng());
|
||||
|
||||
// seed with a random number
|
||||
rng.Reseed(0x1a26f3fa8546b47a);
|
||||
BOOST_TEST(0xc8dc5e08d844ac7d == rng());
|
||||
BOOST_TEST(0x5b5f1f6d499dad1b == rng());
|
||||
BOOST_TEST(0xbeb0031f93313d6f == rng());
|
||||
BOOST_TEST(0xbfbcf4f43a264497 == rng());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
@ -436,7 +436,7 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file_skip)
|
|||
BOOST_AUTO_TEST_CASE(streams_buffered_file_rand)
|
||||
{
|
||||
// Make this test deterministic.
|
||||
SeedInsecureRand(SeedRand::ZEROS);
|
||||
SeedRandomForTest(SeedRand::ZEROS);
|
||||
|
||||
fs::path streams_test_filename = m_args.GetDataDirBase() / "streams_test_tmp";
|
||||
for (int rep = 0; rep < 50; ++rep) {
|
||||
|
|
|
@ -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),
|
||||
/*prefer_evict=*/random_context.randbool(),
|
||||
/*m_is_local=*/random_context.randbool(),
|
||||
/*m_network=*/ALL_NETWORKS[random_context.randrange(ALL_NETWORKS.size())],
|
||||
|
|
|
@ -13,21 +13,26 @@
|
|||
|
||||
FastRandomContext g_insecure_rand_ctx;
|
||||
|
||||
/** Return the unsigned from the environment var if available, otherwise 0 */
|
||||
static uint256 GetUintFromEnv(const std::string& env_name)
|
||||
{
|
||||
const char* num = std::getenv(env_name.c_str());
|
||||
if (!num) return {};
|
||||
return uint256S(num);
|
||||
}
|
||||
extern void MakeRandDeterministicDANGEROUS(const uint256& seed) noexcept;
|
||||
|
||||
void Seed(FastRandomContext& ctx)
|
||||
void SeedRandomForTest(SeedRand seedtype)
|
||||
{
|
||||
// Should be enough to get the seed once for the process
|
||||
static uint256 seed{};
|
||||
static const std::string RANDOM_CTX_SEED{"RANDOM_CTX_SEED"};
|
||||
if (seed.IsNull()) seed = GetUintFromEnv(RANDOM_CTX_SEED);
|
||||
if (seed.IsNull()) seed = GetRandHash();
|
||||
|
||||
// Do this once, on the first call, regardless of seedtype, because once
|
||||
// MakeRandDeterministicDANGEROUS is called, the output of GetRandHash is
|
||||
// no longer truly random. It should be enough to get the seed once for the
|
||||
// process.
|
||||
static const uint256 ctx_seed = []() {
|
||||
// If RANDOM_CTX_SEED is set, use that as seed.
|
||||
const char* num = std::getenv(RANDOM_CTX_SEED.c_str());
|
||||
if (num) return uint256S(num);
|
||||
// Otherwise use a (truly) random value.
|
||||
return GetRandHash();
|
||||
}();
|
||||
|
||||
const uint256& seed{seedtype == SeedRand::SEED ? ctx_seed : uint256::ZERO};
|
||||
LogPrintf("%s: Setting random seed for current tests to %s=%s\n", __func__, RANDOM_CTX_SEED, seed.GetHex());
|
||||
ctx = FastRandomContext(seed);
|
||||
MakeRandDeterministicDANGEROUS(seed);
|
||||
g_insecure_rand_ctx.Reseed(GetRandHash());
|
||||
}
|
||||
|
|
|
@ -19,27 +19,13 @@
|
|||
*/
|
||||
extern FastRandomContext g_insecure_rand_ctx;
|
||||
|
||||
/**
|
||||
* Flag to make GetRand in random.h return the same number
|
||||
*/
|
||||
extern bool g_mock_deterministic_tests;
|
||||
|
||||
enum class SeedRand {
|
||||
ZEROS, //!< Seed with a compile time constant of zeros
|
||||
SEED, //!< Call the Seed() helper
|
||||
SEED, //!< Use (and report) random seed from environment, or a (truly) random one.
|
||||
};
|
||||
|
||||
/** Seed the given random ctx or use the seed passed in via an environment var */
|
||||
void Seed(FastRandomContext& ctx);
|
||||
|
||||
static inline void SeedInsecureRand(SeedRand seed = SeedRand::SEED)
|
||||
{
|
||||
if (seed == SeedRand::ZEROS) {
|
||||
g_insecure_rand_ctx = FastRandomContext(/*fDeterministic=*/true);
|
||||
} else {
|
||||
Seed(g_insecure_rand_ctx);
|
||||
}
|
||||
}
|
||||
/** Seed the RNG for testing. This affects all randomness, except GetStrongRandBytes(). */
|
||||
void SeedRandomForTest(SeedRand seed = SeedRand::SEED);
|
||||
|
||||
static inline uint32_t InsecureRand32()
|
||||
{
|
||||
|
|
|
@ -145,6 +145,10 @@ BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vecto
|
|||
}
|
||||
}
|
||||
|
||||
// Use randomly chosen seed for deterministic PRNG, so that (by default) test
|
||||
// data directories use a random name that doesn't overlap with other tests.
|
||||
SeedRandomForTest(SeedRand::SEED);
|
||||
|
||||
if (!m_node.args->IsArgSet("-testdatadir")) {
|
||||
// By default, the data directory has a random name
|
||||
const auto rand_str{g_insecure_rand_ctx_temp_path.rand256().ToString()};
|
||||
|
@ -178,7 +182,6 @@ BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vecto
|
|||
gArgs.ForceSetArg("-datadir", fs::PathToString(m_path_root));
|
||||
|
||||
SelectParams(chainType);
|
||||
SeedInsecureRand();
|
||||
if (G_TEST_LOG_FUN) LogInstance().PushBackCallback(G_TEST_LOG_FUN);
|
||||
InitLogging(*m_node.args);
|
||||
AppInitParameterInteraction(*m_node.args);
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
// Copyright (c) 2022 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_TEST_UTIL_XOROSHIRO128PLUSPLUS_H
|
||||
#define BITCOIN_TEST_UTIL_XOROSHIRO128PLUSPLUS_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
/** xoroshiro128++ PRNG. Extremely fast, not appropriate for cryptographic purposes.
|
||||
*
|
||||
* Memory footprint is 128bit, period is 2^128 - 1.
|
||||
* This class is not thread-safe.
|
||||
*
|
||||
* Reference implementation available at https://prng.di.unimi.it/xoroshiro128plusplus.c
|
||||
* See https://prng.di.unimi.it/
|
||||
*/
|
||||
class XoRoShiRo128PlusPlus
|
||||
{
|
||||
uint64_t m_s0;
|
||||
uint64_t m_s1;
|
||||
|
||||
[[nodiscard]] constexpr static uint64_t rotl(uint64_t x, int n)
|
||||
{
|
||||
return (x << n) | (x >> (64 - n));
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr static uint64_t SplitMix64(uint64_t& seedval) noexcept
|
||||
{
|
||||
uint64_t z = (seedval += UINT64_C(0x9e3779b97f4a7c15));
|
||||
z = (z ^ (z >> 30U)) * UINT64_C(0xbf58476d1ce4e5b9);
|
||||
z = (z ^ (z >> 27U)) * UINT64_C(0x94d049bb133111eb);
|
||||
return z ^ (z >> 31U);
|
||||
}
|
||||
|
||||
public:
|
||||
using result_type = uint64_t;
|
||||
|
||||
constexpr explicit XoRoShiRo128PlusPlus(uint64_t seedval) noexcept
|
||||
: m_s0(SplitMix64(seedval)), m_s1(SplitMix64(seedval))
|
||||
{
|
||||
}
|
||||
|
||||
// no copy - that is dangerous, we don't want accidentally copy the RNG and then have two streams
|
||||
// with exactly the same results. If you need a copy, call copy().
|
||||
XoRoShiRo128PlusPlus(const XoRoShiRo128PlusPlus&) = delete;
|
||||
XoRoShiRo128PlusPlus& operator=(const XoRoShiRo128PlusPlus&) = delete;
|
||||
|
||||
// allow moves
|
||||
XoRoShiRo128PlusPlus(XoRoShiRo128PlusPlus&&) = default;
|
||||
XoRoShiRo128PlusPlus& operator=(XoRoShiRo128PlusPlus&&) = default;
|
||||
|
||||
~XoRoShiRo128PlusPlus() = default;
|
||||
|
||||
constexpr result_type operator()() noexcept
|
||||
{
|
||||
uint64_t s0 = m_s0, s1 = m_s1;
|
||||
const uint64_t result = rotl(s0 + s1, 17) + s0;
|
||||
s1 ^= s0;
|
||||
m_s0 = rotl(s0, 49) ^ s1 ^ (s1 << 21);
|
||||
m_s1 = rotl(s1, 28);
|
||||
return result;
|
||||
}
|
||||
|
||||
static constexpr result_type min() noexcept { return std::numeric_limits<result_type>::min(); }
|
||||
static constexpr result_type max() noexcept { return std::numeric_limits<result_type>::max(); }
|
||||
static constexpr double entropy() noexcept { return 0.0; }
|
||||
};
|
||||
|
||||
#endif // BITCOIN_TEST_UTIL_XOROSHIRO128PLUSPLUS_H
|
|
@ -459,7 +459,7 @@ BOOST_AUTO_TEST_CASE(util_IsHexNumber)
|
|||
|
||||
BOOST_AUTO_TEST_CASE(util_seed_insecure_rand)
|
||||
{
|
||||
SeedInsecureRand(SeedRand::ZEROS);
|
||||
SeedRandomForTest(SeedRand::ZEROS);
|
||||
for (int mod=2;mod<11;mod++)
|
||||
{
|
||||
int mask = 1;
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
// Copyright (c) 2022 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <test/util/setup_common.h>
|
||||
#include <test/util/xoroshiro128plusplus.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(xoroshiro128plusplus_tests, BasicTestingSetup)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(reference_values)
|
||||
{
|
||||
// numbers generated from reference implementation
|
||||
XoRoShiRo128PlusPlus rng(0);
|
||||
BOOST_TEST(0x6f68e1e7e2646ee1 == rng());
|
||||
BOOST_TEST(0xbf971b7f454094ad == rng());
|
||||
BOOST_TEST(0x48f2de556f30de38 == rng());
|
||||
BOOST_TEST(0x6ea7c59f89bbfc75 == rng());
|
||||
|
||||
// seed with a random number
|
||||
rng = XoRoShiRo128PlusPlus(0x1a26f3fa8546b47a);
|
||||
BOOST_TEST(0xc8dc5e08d844ac7d == rng());
|
||||
BOOST_TEST(0x5b5f1f6d499dad1b == rng());
|
||||
BOOST_TEST(0xbeb0031f93313d6f == rng());
|
||||
BOOST_TEST(0xbfbcf4f43a264497 == rng());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -657,7 +657,7 @@ void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendhei
|
|||
{
|
||||
if (m_opts.check_ratio == 0) return;
|
||||
|
||||
if (GetRand(m_opts.check_ratio) >= 1) return;
|
||||
if (FastRandomContext().randrange(m_opts.check_ratio) >= 1) return;
|
||||
|
||||
AssertLockHeld(::cs_main);
|
||||
LOCK(cs);
|
||||
|
|
|
@ -113,8 +113,8 @@ class PriorityComputer {
|
|||
const uint64_t m_k0, m_k1;
|
||||
public:
|
||||
explicit PriorityComputer(bool deterministic) :
|
||||
m_k0{deterministic ? 0 : GetRand(0xFFFFFFFFFFFFFFFF)},
|
||||
m_k1{deterministic ? 0 : GetRand(0xFFFFFFFFFFFFFFFF)} {}
|
||||
m_k0{deterministic ? 0 : FastRandomContext().rand64()},
|
||||
m_k1{deterministic ? 0 : FastRandomContext().rand64()} {}
|
||||
|
||||
Priority operator()(const uint256& txhash, NodeId peer, bool preferred) const
|
||||
{
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
#include <vector>
|
||||
|
||||
ByteVectorHash::ByteVectorHash() :
|
||||
m_k0(GetRand<uint64_t>()),
|
||||
m_k1(GetRand<uint64_t>())
|
||||
m_k0(FastRandomContext().rand64()),
|
||||
m_k1(FastRandomContext().rand64())
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -7,14 +7,18 @@
|
|||
#include <span.h>
|
||||
#include <util/hasher.h>
|
||||
|
||||
SaltedTxidHasher::SaltedTxidHasher() : k0(GetRand<uint64_t>()), k1(GetRand<uint64_t>()) {}
|
||||
SaltedTxidHasher::SaltedTxidHasher() :
|
||||
k0{FastRandomContext().rand64()},
|
||||
k1{FastRandomContext().rand64()} {}
|
||||
|
||||
SaltedOutpointHasher::SaltedOutpointHasher(bool deterministic) :
|
||||
k0(deterministic ? 0x8e819f2607a18de6 : GetRand<uint64_t>()),
|
||||
k1(deterministic ? 0xf4020d2e3983b0eb : GetRand<uint64_t>())
|
||||
k0{deterministic ? 0x8e819f2607a18de6 : FastRandomContext().rand64()},
|
||||
k1{deterministic ? 0xf4020d2e3983b0eb : FastRandomContext().rand64()}
|
||||
{}
|
||||
|
||||
SaltedSipHasher::SaltedSipHasher() : m_k0(GetRand<uint64_t>()), m_k1(GetRand<uint64_t>()) {}
|
||||
SaltedSipHasher::SaltedSipHasher() :
|
||||
m_k0{FastRandomContext().rand64()},
|
||||
m_k1{FastRandomContext().rand64()} {}
|
||||
|
||||
size_t SaltedSipHasher::operator()(const Span<const unsigned char>& script) const
|
||||
{
|
||||
|
|
|
@ -5193,7 +5193,7 @@ bool ChainstateManager::ShouldCheckBlockIndex() const
|
|||
{
|
||||
// Assert to verify Flatten() has been called.
|
||||
if (!*Assert(m_options.check_block_index)) return false;
|
||||
if (GetRand(*m_options.check_block_index) >= 1) return false;
|
||||
if (FastRandomContext().randrange(*m_options.check_block_index) >= 1) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,8 @@ unsigned-integer-overflow:CBlockPolicyEstimator::processBlockTx
|
|||
unsigned-integer-overflow:TxConfirmStats::EstimateMedianVal
|
||||
unsigned-integer-overflow:prevector.h
|
||||
unsigned-integer-overflow:EvalScript
|
||||
unsigned-integer-overflow:xoroshiro128plusplus.h
|
||||
unsigned-integer-overflow:InsecureRandomContext::rand64
|
||||
unsigned-integer-overflow:InsecureRandomContext::SplitMix64
|
||||
unsigned-integer-overflow:bitset_detail::PopCount
|
||||
implicit-integer-sign-change:CBlockPolicyEstimator::processBlockTx
|
||||
implicit-integer-sign-change:SetStdinEcho
|
||||
|
@ -73,4 +74,6 @@ shift-base:arith_uint256.cpp
|
|||
shift-base:crypto/
|
||||
shift-base:streams.h
|
||||
shift-base:FormatHDKeypath
|
||||
shift-base:xoroshiro128plusplus.h
|
||||
shift-base:InsecureRandomContext::rand64
|
||||
shift-base:RandomMixin<*>::randbits
|
||||
shift-base:RandomMixin<*>::randbits<*>
|
||||
|
|
Loading…
Add table
Reference in a new issue