207 lines
5.9 KiB
C++
207 lines
5.9 KiB
C++
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-2014 The Bitcoin Core developers
|
|
// Copyright (c) 2016-2023 The Zcash developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
|
|
|
|
#ifndef BITCOIN_RANDOM_H
|
|
#define BITCOIN_RANDOM_H
|
|
|
|
#include "crypto/chacha20.h"
|
|
#include "crypto/common.h"
|
|
#include "uint256.h"
|
|
|
|
#include <functional>
|
|
#include <limits>
|
|
|
|
#include "int128.h"
|
|
|
|
/** @file
|
|
* Functions to gather random data via the rand_core OsRng
|
|
*/
|
|
|
|
/** Fill the buffer buf of length num with random unformly distributed bytes, via the rand_core OsRng. */
|
|
void GetRandBytes(unsigned char* buf, size_t num);
|
|
|
|
/**
|
|
* Return a random value of type I uniformly distributed on [0, nMax), unless nMax is 0 in which case return 0.
|
|
* I must be an unsigned numeric type.
|
|
*/
|
|
template <typename I>
|
|
I GetRandGeneric(I nMax)
|
|
{
|
|
static_assert(std::numeric_limits<I>::min() == 0);
|
|
// I() for primitive integer types returns 0.
|
|
if (nMax == I())
|
|
return I();
|
|
|
|
// The range of the random source must be a multiple of the modulus
|
|
// to give every possible output value an equal possibility
|
|
I nRange = (std::numeric_limits<I>::max() / nMax) * nMax;
|
|
I nRand = I();
|
|
do {
|
|
GetRandBytes((unsigned char*)&nRand, sizeof(nRand));
|
|
} while (nRand >= nRange);
|
|
return (nRand % nMax);
|
|
}
|
|
|
|
/** Return a random uint128_t uniformly distributed on [0, nMax), unless nMax is 0 in which case return 0. */
|
|
uint128_t GetRandUInt128(uint128_t nMax);
|
|
/** Return a random int128_t uniformly distributed on [0, nMax), unless nMax is 0 in which case return 0. nMax must be >= 0. */
|
|
int128_t GetRandInt128(int128_t nMax);
|
|
/** Return a random uint64_t uniformly distributed on [0, nMax), unless nMax is 0 in which case return 0. */
|
|
uint64_t GetRand(uint64_t nMax);
|
|
/** Return a random int64_t uniformly distributed on [0, nMax), unless nMax is 0 in which case return 0. nMax must be >= 0. */
|
|
int64_t GetRandInt64(int64_t nMax);
|
|
/** Return a random int uniformly distributed on [0, nMax), unless nMax is 0 in which case return 0. nMax must be >= 0. */
|
|
int GetRandInt(int nMax);
|
|
/** Return a random uniformly distributed uint256. */
|
|
uint256 GetRandHash();
|
|
|
|
/**
|
|
* Implementation of a C++ Uniform Random Number Generator, backed by GetRandBytes.
|
|
*/
|
|
class ZcashRandomEngine
|
|
{
|
|
public:
|
|
typedef uint64_t result_type;
|
|
|
|
explicit ZcashRandomEngine() {}
|
|
|
|
static constexpr result_type min() {
|
|
return std::numeric_limits<result_type>::min();
|
|
}
|
|
static constexpr result_type max() {
|
|
return std::numeric_limits<result_type>::max();
|
|
}
|
|
|
|
result_type operator()() {
|
|
result_type nRand = 0;
|
|
GetRandBytes((unsigned char*)&nRand, sizeof(nRand));
|
|
return nRand;
|
|
}
|
|
|
|
double entropy() const noexcept {
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Identity function for MappedShuffle, so that elements retain their original order.
|
|
*/
|
|
int GenIdentity(int n);
|
|
|
|
/**
|
|
* Rearranges the elements in the range [first,first+len) randomly, assuming
|
|
* that gen is a uniform random number generator. Follows the same algorithm as
|
|
* std::shuffle in C++11 (a Durstenfeld shuffle).
|
|
*
|
|
* The elements in the range [mapFirst,mapFirst+len) are rearranged according to
|
|
* the same permutation, enabling the permutation to be tracked by the caller.
|
|
*
|
|
* gen takes an integer n and produces a uniform random output in [0,n).
|
|
*/
|
|
template <typename RandomAccessIterator, typename MapRandomAccessIterator>
|
|
void MappedShuffle(RandomAccessIterator first,
|
|
MapRandomAccessIterator mapFirst,
|
|
size_t len,
|
|
std::function<int(int)> gen)
|
|
{
|
|
for (size_t i = len-1; i > 0; --i) {
|
|
auto r = gen(i+1);
|
|
assert(r >= 0);
|
|
assert(r <= i);
|
|
std::swap(first[i], first[r]);
|
|
std::swap(mapFirst[i], mapFirst[r]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fast randomness source. This is seeded once with secure random data, but
|
|
* is completely deterministic and insecure after that.
|
|
* This class is not thread-safe.
|
|
*/
|
|
class FastRandomContext {
|
|
private:
|
|
bool requires_seed;
|
|
ChaCha20 rng;
|
|
|
|
unsigned char bytebuf[64];
|
|
int bytebuf_size;
|
|
|
|
uint64_t bitbuf;
|
|
int bitbuf_size;
|
|
|
|
void RandomSeed();
|
|
|
|
void FillByteBuffer()
|
|
{
|
|
if (requires_seed) {
|
|
RandomSeed();
|
|
}
|
|
rng.Output(bytebuf, sizeof(bytebuf));
|
|
bytebuf_size = sizeof(bytebuf);
|
|
}
|
|
|
|
void FillBitBuffer()
|
|
{
|
|
bitbuf = rand64();
|
|
bitbuf_size = 64;
|
|
}
|
|
|
|
public:
|
|
explicit FastRandomContext(bool fDeterministic = false);
|
|
|
|
/** Initialize with explicit seed (only for testing) */
|
|
explicit FastRandomContext(const uint256& seed);
|
|
|
|
/** Generate a random 64-bit integer. */
|
|
uint64_t rand64()
|
|
{
|
|
if (bytebuf_size < 8) FillByteBuffer();
|
|
uint64_t ret = ReadLE64(bytebuf + 64 - bytebuf_size);
|
|
bytebuf_size -= 8;
|
|
return ret;
|
|
}
|
|
|
|
/** Generate a random (bits)-bit integer. */
|
|
uint64_t randbits(int bits) {
|
|
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). */
|
|
uint64_t randrange(uint64_t range)
|
|
{
|
|
--range;
|
|
int bits = CountBits(range);
|
|
while (true) {
|
|
uint64_t ret = randbits(bits);
|
|
if (ret <= range) return ret;
|
|
}
|
|
}
|
|
|
|
/** Generate random bytes. */
|
|
std::vector<unsigned char> randbytes(size_t len);
|
|
|
|
/** Generate a random 32-bit integer. */
|
|
uint32_t rand32() { return randbits(32); }
|
|
|
|
/** generate a random uint256. */
|
|
uint256 rand256();
|
|
|
|
/** Generate a random boolean. */
|
|
bool randbool() { return randbits(1); }
|
|
};
|
|
|
|
#endif // BITCOIN_RANDOM_H
|