Merge #14624: Some simple improvements to the RNG code
e414486d56
Do not permit copying FastRandomContexts (Pieter Wuille)022cf47dd7
Simplify testing RNG code (Pieter Wuille)fd3e7973ff
Make unit tests use the insecure_rand_ctx exclusively (Pieter Wuille)8d98d42611
Bugfix: randbytes should seed when needed (non reachable issue) (Pieter Wuille)273d02580a
Use a FastRandomContext in LimitOrphanTxSize (Pieter Wuille)3db746beb4
Introduce a Shuffle for FastRandomContext and use it in wallet and coinselection (Pieter Wuille)8098379be5
Use a local FastRandomContext in a few more places in net (Pieter Wuille)9695f31d75
Make addrman use its local RNG exclusively (Pieter Wuille) Pull request description: This improves a few minor issues with the RNG code: * Avoid calling `GetRand*()` functions (which currently invoke OpenSSL, later may switch to using our own RNG pool) inside loops in addrman, networking code, `KnapsackSolver`, and `LimitOrphanSize` * Fix a currently unreachable bug in `FastRandomContext::randbytes`. * Make a number of simplifications to the unit tests' randomness code (some tests unnecessarily used their own RNG or the OpenSSL one, instead of using the unit test specific `insecure_rand_ctx`). * As a precaution, make it illegal to copy a `FastRandomContext`. Tree-SHA512: 084c70b533ea68ca7adc0186c39f0b3e0a5c0ae43a12c37286e5d42086e056a8cd026dde61b12c0a296dc80f87fdc87fe303b9e8e6161b460ac2086cf7615f9d
This commit is contained in:
commit
378fdfabba
16 changed files with 134 additions and 82 deletions
|
@ -217,7 +217,7 @@ void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// find a bucket it is in now
|
// find a bucket it is in now
|
||||||
int nRnd = RandomInt(ADDRMAN_NEW_BUCKET_COUNT);
|
int nRnd = insecure_rand.randrange(ADDRMAN_NEW_BUCKET_COUNT);
|
||||||
int nUBucket = -1;
|
int nUBucket = -1;
|
||||||
for (unsigned int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
|
for (unsigned int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
|
||||||
int nB = (n + nRnd) % ADDRMAN_NEW_BUCKET_COUNT;
|
int nB = (n + nRnd) % ADDRMAN_NEW_BUCKET_COUNT;
|
||||||
|
@ -291,7 +291,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP
|
||||||
int nFactor = 1;
|
int nFactor = 1;
|
||||||
for (int n = 0; n < pinfo->nRefCount; n++)
|
for (int n = 0; n < pinfo->nRefCount; n++)
|
||||||
nFactor *= 2;
|
nFactor *= 2;
|
||||||
if (nFactor > 1 && (RandomInt(nFactor) != 0))
|
if (nFactor > 1 && (insecure_rand.randrange(nFactor) != 0))
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
pinfo = Create(addr, source, &nId);
|
pinfo = Create(addr, source, &nId);
|
||||||
|
@ -356,12 +356,12 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
|
||||||
|
|
||||||
// Use a 50% chance for choosing between tried and new table entries.
|
// Use a 50% chance for choosing between tried and new table entries.
|
||||||
if (!newOnly &&
|
if (!newOnly &&
|
||||||
(nTried > 0 && (nNew == 0 || RandomInt(2) == 0))) {
|
(nTried > 0 && (nNew == 0 || insecure_rand.randbool() == 0))) {
|
||||||
// use a tried node
|
// use a tried node
|
||||||
double fChanceFactor = 1.0;
|
double fChanceFactor = 1.0;
|
||||||
while (1) {
|
while (1) {
|
||||||
int nKBucket = RandomInt(ADDRMAN_TRIED_BUCKET_COUNT);
|
int nKBucket = insecure_rand.randrange(ADDRMAN_TRIED_BUCKET_COUNT);
|
||||||
int nKBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
|
int nKBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
|
||||||
while (vvTried[nKBucket][nKBucketPos] == -1) {
|
while (vvTried[nKBucket][nKBucketPos] == -1) {
|
||||||
nKBucket = (nKBucket + insecure_rand.randbits(ADDRMAN_TRIED_BUCKET_COUNT_LOG2)) % ADDRMAN_TRIED_BUCKET_COUNT;
|
nKBucket = (nKBucket + insecure_rand.randbits(ADDRMAN_TRIED_BUCKET_COUNT_LOG2)) % ADDRMAN_TRIED_BUCKET_COUNT;
|
||||||
nKBucketPos = (nKBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
|
nKBucketPos = (nKBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
|
||||||
|
@ -369,7 +369,7 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
|
||||||
int nId = vvTried[nKBucket][nKBucketPos];
|
int nId = vvTried[nKBucket][nKBucketPos];
|
||||||
assert(mapInfo.count(nId) == 1);
|
assert(mapInfo.count(nId) == 1);
|
||||||
CAddrInfo& info = mapInfo[nId];
|
CAddrInfo& info = mapInfo[nId];
|
||||||
if (RandomInt(1 << 30) < fChanceFactor * info.GetChance() * (1 << 30))
|
if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30))
|
||||||
return info;
|
return info;
|
||||||
fChanceFactor *= 1.2;
|
fChanceFactor *= 1.2;
|
||||||
}
|
}
|
||||||
|
@ -377,8 +377,8 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
|
||||||
// use a new node
|
// use a new node
|
||||||
double fChanceFactor = 1.0;
|
double fChanceFactor = 1.0;
|
||||||
while (1) {
|
while (1) {
|
||||||
int nUBucket = RandomInt(ADDRMAN_NEW_BUCKET_COUNT);
|
int nUBucket = insecure_rand.randrange(ADDRMAN_NEW_BUCKET_COUNT);
|
||||||
int nUBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
|
int nUBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
|
||||||
while (vvNew[nUBucket][nUBucketPos] == -1) {
|
while (vvNew[nUBucket][nUBucketPos] == -1) {
|
||||||
nUBucket = (nUBucket + insecure_rand.randbits(ADDRMAN_NEW_BUCKET_COUNT_LOG2)) % ADDRMAN_NEW_BUCKET_COUNT;
|
nUBucket = (nUBucket + insecure_rand.randbits(ADDRMAN_NEW_BUCKET_COUNT_LOG2)) % ADDRMAN_NEW_BUCKET_COUNT;
|
||||||
nUBucketPos = (nUBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
|
nUBucketPos = (nUBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
|
||||||
|
@ -386,7 +386,7 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
|
||||||
int nId = vvNew[nUBucket][nUBucketPos];
|
int nId = vvNew[nUBucket][nUBucketPos];
|
||||||
assert(mapInfo.count(nId) == 1);
|
assert(mapInfo.count(nId) == 1);
|
||||||
CAddrInfo& info = mapInfo[nId];
|
CAddrInfo& info = mapInfo[nId];
|
||||||
if (RandomInt(1 << 30) < fChanceFactor * info.GetChance() * (1 << 30))
|
if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30))
|
||||||
return info;
|
return info;
|
||||||
fChanceFactor *= 1.2;
|
fChanceFactor *= 1.2;
|
||||||
}
|
}
|
||||||
|
@ -482,7 +482,7 @@ void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr)
|
||||||
if (vAddr.size() >= nNodes)
|
if (vAddr.size() >= nNodes)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
int nRndPos = RandomInt(vRandom.size() - n) + n;
|
int nRndPos = insecure_rand.randrange(vRandom.size() - n) + n;
|
||||||
SwapRandom(n, nRndPos);
|
SwapRandom(n, nRndPos);
|
||||||
assert(mapInfo.count(vRandom[n]) == 1);
|
assert(mapInfo.count(vRandom[n]) == 1);
|
||||||
|
|
||||||
|
@ -530,10 +530,6 @@ void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
|
||||||
info.nServices = nServices;
|
info.nServices = nServices;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CAddrMan::RandomInt(int nMax){
|
|
||||||
return GetRandInt(nMax);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAddrMan::ResolveCollisions_()
|
void CAddrMan::ResolveCollisions_()
|
||||||
{
|
{
|
||||||
for (std::set<int>::iterator it = m_tried_collisions.begin(); it != m_tried_collisions.end();) {
|
for (std::set<int>::iterator it = m_tried_collisions.begin(); it != m_tried_collisions.end();) {
|
||||||
|
@ -593,7 +589,7 @@ CAddrInfo CAddrMan::SelectTriedCollision_()
|
||||||
std::set<int>::iterator it = m_tried_collisions.begin();
|
std::set<int>::iterator it = m_tried_collisions.begin();
|
||||||
|
|
||||||
// Selects a random element from m_tried_collisions
|
// Selects a random element from m_tried_collisions
|
||||||
std::advance(it, GetRandInt(m_tried_collisions.size()));
|
std::advance(it, insecure_rand.randrange(m_tried_collisions.size()));
|
||||||
int id_new = *it;
|
int id_new = *it;
|
||||||
|
|
||||||
// If id_new not found in mapInfo remove it from m_tried_collisions
|
// If id_new not found in mapInfo remove it from m_tried_collisions
|
||||||
|
|
|
@ -266,9 +266,6 @@ protected:
|
||||||
//! Return a random to-be-evicted tried table address.
|
//! Return a random to-be-evicted tried table address.
|
||||||
CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
|
CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||||
|
|
||||||
//! Wraps GetRandInt to allow tests to override RandomInt and make it determinismistic.
|
|
||||||
virtual int RandomInt(int nMax);
|
|
||||||
|
|
||||||
#ifdef DEBUG_ADDRMAN
|
#ifdef DEBUG_ADDRMAN
|
||||||
//! Perform consistency check. Returns an error code or zero.
|
//! Perform consistency check. Returns an error code or zero.
|
||||||
int Check_() EXCLUSIVE_LOCKS_REQUIRED(cs);
|
int Check_() EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||||
|
@ -473,7 +470,7 @@ public:
|
||||||
{
|
{
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
std::vector<int>().swap(vRandom);
|
std::vector<int>().swap(vRandom);
|
||||||
nKey = GetRandHash();
|
nKey = insecure_rand.rand256();
|
||||||
for (size_t bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
|
for (size_t bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
|
||||||
for (size_t entry = 0; entry < ADDRMAN_BUCKET_SIZE; entry++) {
|
for (size_t entry = 0; entry < ADDRMAN_BUCKET_SIZE; entry++) {
|
||||||
vvNew[bucket][entry] = -1;
|
vvNew[bucket][entry] = -1;
|
||||||
|
|
|
@ -134,11 +134,12 @@ static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn
|
||||||
const int64_t nOneWeek = 7*24*60*60;
|
const int64_t nOneWeek = 7*24*60*60;
|
||||||
std::vector<CAddress> vSeedsOut;
|
std::vector<CAddress> vSeedsOut;
|
||||||
vSeedsOut.reserve(vSeedsIn.size());
|
vSeedsOut.reserve(vSeedsIn.size());
|
||||||
|
FastRandomContext rng;
|
||||||
for (const auto& seed_in : vSeedsIn) {
|
for (const auto& seed_in : vSeedsIn) {
|
||||||
struct in6_addr ip;
|
struct in6_addr ip;
|
||||||
memcpy(&ip, seed_in.addr, sizeof(ip));
|
memcpy(&ip, seed_in.addr, sizeof(ip));
|
||||||
CAddress addr(CService(ip, seed_in.port), GetDesirableServiceFlags(NODE_NONE));
|
CAddress addr(CService(ip, seed_in.port), GetDesirableServiceFlags(NODE_NONE));
|
||||||
addr.nTime = GetTime() - GetRand(nOneWeek) - nOneWeek;
|
addr.nTime = GetTime() - rng.randrange(nOneWeek) - nOneWeek;
|
||||||
vSeedsOut.push_back(addr);
|
vSeedsOut.push_back(addr);
|
||||||
}
|
}
|
||||||
return vSeedsOut;
|
return vSeedsOut;
|
||||||
|
@ -189,16 +190,16 @@ void AdvertiseLocal(CNode *pnode)
|
||||||
// If discovery is enabled, sometimes give our peer the address it
|
// If discovery is enabled, sometimes give our peer the address it
|
||||||
// tells us that it sees us as in case it has a better idea of our
|
// tells us that it sees us as in case it has a better idea of our
|
||||||
// address than we do.
|
// address than we do.
|
||||||
|
FastRandomContext rng;
|
||||||
if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() ||
|
if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() ||
|
||||||
GetRand((GetnScore(addrLocal) > LOCAL_MANUAL) ? 8:2) == 0))
|
rng.randbits((GetnScore(addrLocal) > LOCAL_MANUAL) ? 3 : 1) == 0))
|
||||||
{
|
{
|
||||||
addrLocal.SetIP(pnode->GetAddrLocal());
|
addrLocal.SetIP(pnode->GetAddrLocal());
|
||||||
}
|
}
|
||||||
if (addrLocal.IsRoutable() || gArgs.GetBoolArg("-addrmantest", false))
|
if (addrLocal.IsRoutable() || gArgs.GetBoolArg("-addrmantest", false))
|
||||||
{
|
{
|
||||||
LogPrint(BCLog::NET, "AdvertiseLocal: advertising address %s\n", addrLocal.ToString());
|
LogPrint(BCLog::NET, "AdvertiseLocal: advertising address %s\n", addrLocal.ToString());
|
||||||
FastRandomContext insecure_rand;
|
pnode->PushAddress(addrLocal, rng);
|
||||||
pnode->PushAddress(addrLocal, insecure_rand);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -779,10 +779,11 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
|
||||||
nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
|
nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
|
||||||
if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx due to expiration\n", nErased);
|
if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx due to expiration\n", nErased);
|
||||||
}
|
}
|
||||||
|
FastRandomContext rng;
|
||||||
while (mapOrphanTransactions.size() > nMaxOrphans)
|
while (mapOrphanTransactions.size() > nMaxOrphans)
|
||||||
{
|
{
|
||||||
// Evict a random orphan:
|
// Evict a random orphan:
|
||||||
uint256 randomhash = GetRandHash();
|
uint256 randomhash = rng.rand256();
|
||||||
std::map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
|
std::map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
|
||||||
if (it == mapOrphanTransactions.end())
|
if (it == mapOrphanTransactions.end())
|
||||||
it = mapOrphanTransactions.begin();
|
it = mapOrphanTransactions.begin();
|
||||||
|
|
|
@ -398,6 +398,7 @@ uint256 FastRandomContext::rand256()
|
||||||
|
|
||||||
std::vector<unsigned char> FastRandomContext::randbytes(size_t len)
|
std::vector<unsigned char> FastRandomContext::randbytes(size_t len)
|
||||||
{
|
{
|
||||||
|
if (requires_seed) RandomSeed();
|
||||||
std::vector<unsigned char> ret(len);
|
std::vector<unsigned char> ret(len);
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
rng.Output(&ret[0], len);
|
rng.Output(&ret[0], len);
|
||||||
|
@ -463,6 +464,20 @@ FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDete
|
||||||
rng.SetKey(seed.begin(), 32);
|
rng.SetKey(seed.begin(), 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FastRandomContext& FastRandomContext::operator=(FastRandomContext&& from) noexcept
|
||||||
|
{
|
||||||
|
requires_seed = from.requires_seed;
|
||||||
|
rng = from.rng;
|
||||||
|
std::copy(std::begin(from.bytebuf), std::end(from.bytebuf), std::begin(bytebuf));
|
||||||
|
bytebuf_size = from.bytebuf_size;
|
||||||
|
bitbuf = from.bitbuf;
|
||||||
|
bitbuf_size = from.bitbuf_size;
|
||||||
|
from.requires_seed = true;
|
||||||
|
from.bytebuf_size = 0;
|
||||||
|
from.bitbuf_size = 0;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
void RandomInit()
|
void RandomInit()
|
||||||
{
|
{
|
||||||
RDRandInit();
|
RDRandInit();
|
||||||
|
|
31
src/random.h
31
src/random.h
|
@ -76,6 +76,14 @@ public:
|
||||||
/** Initialize with explicit seed (only for testing) */
|
/** Initialize with explicit seed (only for testing) */
|
||||||
explicit FastRandomContext(const uint256& seed);
|
explicit FastRandomContext(const uint256& seed);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
/** Generate a random 64-bit integer. */
|
/** Generate a random 64-bit integer. */
|
||||||
uint64_t rand64()
|
uint64_t rand64()
|
||||||
{
|
{
|
||||||
|
@ -130,6 +138,29 @@ public:
|
||||||
inline uint64_t operator()() { return rand64(); }
|
inline uint64_t operator()() { return rand64(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** More efficient than using std::shuffle on a FastRandomContext.
|
||||||
|
*
|
||||||
|
* This is more efficient as std::shuffle will consume entropy in groups of
|
||||||
|
* 64 bits at the time and throw away most.
|
||||||
|
*
|
||||||
|
* This also works around a bug in libstdc++ std::shuffle that may cause
|
||||||
|
* type::operator=(type&&) to be invoked on itself, which the library's
|
||||||
|
* 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>
|
||||||
|
void Shuffle(I first, I last, R&& rng)
|
||||||
|
{
|
||||||
|
while (first != last) {
|
||||||
|
size_t j = rng.randrange(last - first);
|
||||||
|
if (j) {
|
||||||
|
using std::swap;
|
||||||
|
swap(*first, *(first + j));
|
||||||
|
}
|
||||||
|
++first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Number of random bytes returned by GetOSRand.
|
/* Number of random bytes returned by GetOSRand.
|
||||||
* When changing this constant make sure to change all call sites, and make
|
* 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.
|
* sure that the underlying OS APIs for all platforms support the number.
|
||||||
|
|
|
@ -32,12 +32,6 @@ public:
|
||||||
insecure_rand = FastRandomContext(true);
|
insecure_rand = FastRandomContext(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
int RandomInt(int nMax) override
|
|
||||||
{
|
|
||||||
state = (CHashWriter(SER_GETHASH, 0) << state).GetCheapHash();
|
|
||||||
return (unsigned int)(state % nMax);
|
|
||||||
}
|
|
||||||
|
|
||||||
CAddrInfo* Find(const CNetAddr& addr, int* pnId = nullptr)
|
CAddrInfo* Find(const CNetAddr& addr, int* pnId = nullptr)
|
||||||
{
|
{
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
|
|
|
@ -21,40 +21,23 @@
|
||||||
* using BOOST_CHECK_CLOSE to fail.
|
* using BOOST_CHECK_CLOSE to fail.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
FastRandomContext local_rand_ctx(true);
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE(cuckoocache_tests);
|
BOOST_AUTO_TEST_SUITE(cuckoocache_tests);
|
||||||
|
|
||||||
|
|
||||||
/** insecure_GetRandHash fills in a uint256 from local_rand_ctx
|
|
||||||
*/
|
|
||||||
static void insecure_GetRandHash(uint256& t)
|
|
||||||
{
|
|
||||||
uint32_t* ptr = (uint32_t*)t.begin();
|
|
||||||
for (uint8_t j = 0; j < 8; ++j)
|
|
||||||
*(ptr++) = local_rand_ctx.rand32();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Test that no values not inserted into the cache are read out of it.
|
/* 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 insecure_GetRandHash calls
|
||||||
*/
|
*/
|
||||||
BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes)
|
BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes)
|
||||||
{
|
{
|
||||||
local_rand_ctx = FastRandomContext(true);
|
SeedInsecureRand(true);
|
||||||
CuckooCache::cache<uint256, SignatureCacheHasher> cc{};
|
CuckooCache::cache<uint256, SignatureCacheHasher> cc{};
|
||||||
size_t megabytes = 4;
|
size_t megabytes = 4;
|
||||||
cc.setup_bytes(megabytes << 20);
|
cc.setup_bytes(megabytes << 20);
|
||||||
uint256 v;
|
|
||||||
for (int x = 0; x < 100000; ++x) {
|
for (int x = 0; x < 100000; ++x) {
|
||||||
insecure_GetRandHash(v);
|
cc.insert(InsecureRand256());
|
||||||
cc.insert(v);
|
|
||||||
}
|
}
|
||||||
for (int x = 0; x < 100000; ++x) {
|
for (int x = 0; x < 100000; ++x) {
|
||||||
insecure_GetRandHash(v);
|
BOOST_CHECK(!cc.contains(InsecureRand256(), false));
|
||||||
BOOST_CHECK(!cc.contains(v, false));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -64,7 +47,7 @@ BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes)
|
||||||
template <typename Cache>
|
template <typename Cache>
|
||||||
static double test_cache(size_t megabytes, double load)
|
static double test_cache(size_t megabytes, double load)
|
||||||
{
|
{
|
||||||
local_rand_ctx = FastRandomContext(true);
|
SeedInsecureRand(true);
|
||||||
std::vector<uint256> hashes;
|
std::vector<uint256> hashes;
|
||||||
Cache set{};
|
Cache set{};
|
||||||
size_t bytes = megabytes * (1 << 20);
|
size_t bytes = megabytes * (1 << 20);
|
||||||
|
@ -74,7 +57,7 @@ static double test_cache(size_t megabytes, double load)
|
||||||
for (uint32_t i = 0; i < n_insert; ++i) {
|
for (uint32_t i = 0; i < n_insert; ++i) {
|
||||||
uint32_t* ptr = (uint32_t*)hashes[i].begin();
|
uint32_t* ptr = (uint32_t*)hashes[i].begin();
|
||||||
for (uint8_t j = 0; j < 8; ++j)
|
for (uint8_t j = 0; j < 8; ++j)
|
||||||
*(ptr++) = local_rand_ctx.rand32();
|
*(ptr++) = InsecureRand32();
|
||||||
}
|
}
|
||||||
/** We make a copy of the hashes because future optimizations of the
|
/** We make a copy of the hashes because future optimizations of the
|
||||||
* cuckoocache may overwrite the inserted element, so the test is
|
* cuckoocache may overwrite the inserted element, so the test is
|
||||||
|
@ -135,7 +118,7 @@ template <typename Cache>
|
||||||
static void test_cache_erase(size_t megabytes)
|
static void test_cache_erase(size_t megabytes)
|
||||||
{
|
{
|
||||||
double load = 1;
|
double load = 1;
|
||||||
local_rand_ctx = FastRandomContext(true);
|
SeedInsecureRand(true);
|
||||||
std::vector<uint256> hashes;
|
std::vector<uint256> hashes;
|
||||||
Cache set{};
|
Cache set{};
|
||||||
size_t bytes = megabytes * (1 << 20);
|
size_t bytes = megabytes * (1 << 20);
|
||||||
|
@ -145,7 +128,7 @@ static void test_cache_erase(size_t megabytes)
|
||||||
for (uint32_t i = 0; i < n_insert; ++i) {
|
for (uint32_t i = 0; i < n_insert; ++i) {
|
||||||
uint32_t* ptr = (uint32_t*)hashes[i].begin();
|
uint32_t* ptr = (uint32_t*)hashes[i].begin();
|
||||||
for (uint8_t j = 0; j < 8; ++j)
|
for (uint8_t j = 0; j < 8; ++j)
|
||||||
*(ptr++) = local_rand_ctx.rand32();
|
*(ptr++) = InsecureRand32();
|
||||||
}
|
}
|
||||||
/** We make a copy of the hashes because future optimizations of the
|
/** We make a copy of the hashes because future optimizations of the
|
||||||
* cuckoocache may overwrite the inserted element, so the test is
|
* cuckoocache may overwrite the inserted element, so the test is
|
||||||
|
@ -198,7 +181,7 @@ template <typename Cache>
|
||||||
static void test_cache_erase_parallel(size_t megabytes)
|
static void test_cache_erase_parallel(size_t megabytes)
|
||||||
{
|
{
|
||||||
double load = 1;
|
double load = 1;
|
||||||
local_rand_ctx = FastRandomContext(true);
|
SeedInsecureRand(true);
|
||||||
std::vector<uint256> hashes;
|
std::vector<uint256> hashes;
|
||||||
Cache set{};
|
Cache set{};
|
||||||
size_t bytes = megabytes * (1 << 20);
|
size_t bytes = megabytes * (1 << 20);
|
||||||
|
@ -208,7 +191,7 @@ static void test_cache_erase_parallel(size_t megabytes)
|
||||||
for (uint32_t i = 0; i < n_insert; ++i) {
|
for (uint32_t i = 0; i < n_insert; ++i) {
|
||||||
uint32_t* ptr = (uint32_t*)hashes[i].begin();
|
uint32_t* ptr = (uint32_t*)hashes[i].begin();
|
||||||
for (uint8_t j = 0; j < 8; ++j)
|
for (uint8_t j = 0; j < 8; ++j)
|
||||||
*(ptr++) = local_rand_ctx.rand32();
|
*(ptr++) = InsecureRand32();
|
||||||
}
|
}
|
||||||
/** We make a copy of the hashes because future optimizations of the
|
/** We make a copy of the hashes because future optimizations of the
|
||||||
* cuckoocache may overwrite the inserted element, so the test is
|
* cuckoocache may overwrite the inserted element, so the test is
|
||||||
|
@ -300,7 +283,7 @@ static void test_cache_generations()
|
||||||
// iterations with non-deterministic values, so it isn't "overfit" to the
|
// iterations with non-deterministic values, so it isn't "overfit" to the
|
||||||
// specific entropy in FastRandomContext(true) and implementation of the
|
// specific entropy in FastRandomContext(true) and implementation of the
|
||||||
// cache.
|
// cache.
|
||||||
local_rand_ctx = FastRandomContext(true);
|
SeedInsecureRand(true);
|
||||||
|
|
||||||
// block_activity models a chunk of network activity. n_insert elements are
|
// 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
|
// added to the cache. The first and last n/4 are stored for removal later
|
||||||
|
@ -317,7 +300,7 @@ static void test_cache_generations()
|
||||||
for (uint32_t i = 0; i < n_insert; ++i) {
|
for (uint32_t i = 0; i < n_insert; ++i) {
|
||||||
uint32_t* ptr = (uint32_t*)inserts[i].begin();
|
uint32_t* ptr = (uint32_t*)inserts[i].begin();
|
||||||
for (uint8_t j = 0; j < 8; ++j)
|
for (uint8_t j = 0; j < 8; ++j)
|
||||||
*(ptr++) = local_rand_ctx.rand32();
|
*(ptr++) = InsecureRand32();
|
||||||
}
|
}
|
||||||
for (uint32_t i = 0; i < n_insert / 4; ++i)
|
for (uint32_t i = 0; i < n_insert / 4; ++i)
|
||||||
reads.push_back(inserts[i]);
|
reads.push_back(inserts[i]);
|
||||||
|
|
|
@ -111,7 +111,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
|
||||||
|
|
||||||
static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerLogicValidation &peerLogic)
|
static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerLogicValidation &peerLogic)
|
||||||
{
|
{
|
||||||
CAddress addr(ip(GetRandInt(0xffffffff)), NODE_NONE);
|
CAddress addr(ip(insecure_rand_ctx.randbits(32)), NODE_NONE);
|
||||||
vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK|NODE_WITNESS), 0, INVALID_SOCKET, addr, 0, 0, CAddress(), "", /*fInboundIn=*/ false));
|
vNodes.emplace_back(new CNode(id++, ServiceFlags(NODE_NETWORK|NODE_WITNESS), 0, INVALID_SOCKET, addr, 0, 0, CAddress(), "", /*fInboundIn=*/ false));
|
||||||
CNode &node = *vNodes.back();
|
CNode &node = *vNodes.back();
|
||||||
node.SetSendVersion(PROTOCOL_VERSION);
|
node.SetSendVersion(PROTOCOL_VERSION);
|
||||||
|
|
|
@ -189,8 +189,8 @@ public:
|
||||||
|
|
||||||
prevector_tester() {
|
prevector_tester() {
|
||||||
SeedInsecureRand();
|
SeedInsecureRand();
|
||||||
rand_seed = insecure_rand_seed;
|
rand_seed = InsecureRand256();
|
||||||
rand_cache = insecure_rand_ctx;
|
rand_cache = FastRandomContext(rand_seed);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -38,11 +38,18 @@ BOOST_AUTO_TEST_CASE(fastrandom_tests)
|
||||||
BOOST_CHECK(ctx1.randbytes(50) == ctx2.randbytes(50));
|
BOOST_CHECK(ctx1.randbytes(50) == ctx2.randbytes(50));
|
||||||
|
|
||||||
// Check that a nondeterministic ones are not
|
// Check that a nondeterministic ones are not
|
||||||
FastRandomContext ctx3;
|
{
|
||||||
FastRandomContext ctx4;
|
FastRandomContext ctx3, ctx4;
|
||||||
BOOST_CHECK(ctx3.rand64() != ctx4.rand64()); // extremely unlikely to be equal
|
BOOST_CHECK(ctx3.rand64() != ctx4.rand64()); // extremely unlikely to be equal
|
||||||
|
}
|
||||||
|
{
|
||||||
|
FastRandomContext ctx3, ctx4;
|
||||||
BOOST_CHECK(ctx3.rand256() != ctx4.rand256());
|
BOOST_CHECK(ctx3.rand256() != ctx4.rand256());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
FastRandomContext ctx3, ctx4;
|
||||||
BOOST_CHECK(ctx3.randbytes(7) != ctx4.randbytes(7));
|
BOOST_CHECK(ctx3.randbytes(7) != ctx4.randbytes(7));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(fastrandom_randbits)
|
BOOST_AUTO_TEST_CASE(fastrandom_randbits)
|
||||||
|
@ -75,8 +82,42 @@ BOOST_AUTO_TEST_CASE(stdrandom_test)
|
||||||
for (int j = 1; j <= 10; ++j) {
|
for (int j = 1; j <= 10; ++j) {
|
||||||
BOOST_CHECK(std::find(test.begin(), test.end(), j) != test.end());
|
BOOST_CHECK(std::find(test.begin(), test.end(), j) != test.end());
|
||||||
}
|
}
|
||||||
|
Shuffle(test.begin(), test.end(), ctx);
|
||||||
|
for (int j = 1; j <= 10; ++j) {
|
||||||
|
BOOST_CHECK(std::find(test.begin(), test.end(), j) != test.end());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Test that Shuffle reaches every permutation with equal probability. */
|
||||||
|
BOOST_AUTO_TEST_CASE(shuffle_stat_test)
|
||||||
|
{
|
||||||
|
FastRandomContext ctx(true);
|
||||||
|
uint32_t counts[5 * 5 * 5 * 5 * 5] = {0};
|
||||||
|
for (int i = 0; i < 12000; ++i) {
|
||||||
|
int data[5] = {0, 1, 2, 3, 4};
|
||||||
|
Shuffle(std::begin(data), std::end(data), ctx);
|
||||||
|
int pos = data[0] + data[1] * 5 + data[2] * 25 + data[3] * 125 + data[4] * 625;
|
||||||
|
++counts[pos];
|
||||||
|
}
|
||||||
|
unsigned int sum = 0;
|
||||||
|
double chi_score = 0.0;
|
||||||
|
for (int i = 0; i < 5 * 5 * 5 * 5 * 5; ++i) {
|
||||||
|
int i1 = i % 5, i2 = (i / 5) % 5, i3 = (i / 25) % 5, i4 = (i / 125) % 5, i5 = i / 625;
|
||||||
|
uint32_t count = counts[i];
|
||||||
|
if (i1 == i2 || i1 == i3 || i1 == i4 || i1 == i5 || i2 == i3 || i2 == i4 || i2 == i5 || i3 == i4 || i3 == i5 || i4 == i5) {
|
||||||
|
BOOST_CHECK(count == 0);
|
||||||
|
} else {
|
||||||
|
chi_score += ((count - 100.0) * (count - 100.0)) / 100.0;
|
||||||
|
BOOST_CHECK(count > 50);
|
||||||
|
BOOST_CHECK(count < 150);
|
||||||
|
sum += count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BOOST_CHECK(chi_score > 58.1411); // 99.9999% confidence interval
|
||||||
|
BOOST_CHECK(chi_score < 210.275);
|
||||||
|
BOOST_CHECK_EQUAL(sum, 12000);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
|
@ -36,8 +36,7 @@ void CConnmanTest::ClearNodes()
|
||||||
g_connman->vNodes.clear();
|
g_connman->vNodes.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 insecure_rand_seed = GetRandHash();
|
FastRandomContext insecure_rand_ctx;
|
||||||
FastRandomContext insecure_rand_ctx(insecure_rand_seed);
|
|
||||||
|
|
||||||
extern bool fPrintToConsole;
|
extern bool fPrintToConsole;
|
||||||
extern void noui_connect();
|
extern void noui_connect();
|
||||||
|
|
|
@ -26,17 +26,11 @@ std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value, std::os
|
||||||
return stream << static_cast<typename std::underlying_type<T>::type>(e);
|
return stream << static_cast<typename std::underlying_type<T>::type>(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern uint256 insecure_rand_seed;
|
|
||||||
extern FastRandomContext insecure_rand_ctx;
|
extern FastRandomContext insecure_rand_ctx;
|
||||||
|
|
||||||
static inline void SeedInsecureRand(bool fDeterministic = false)
|
static inline void SeedInsecureRand(bool deterministic = false)
|
||||||
{
|
{
|
||||||
if (fDeterministic) {
|
insecure_rand_ctx = FastRandomContext(deterministic);
|
||||||
insecure_rand_seed = uint256();
|
|
||||||
} else {
|
|
||||||
insecure_rand_seed = GetRandHash();
|
|
||||||
}
|
|
||||||
insecure_rand_ctx = FastRandomContext(insecure_rand_seed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uint32_t InsecureRand32() { return insecure_rand_ctx.rand32(); }
|
static inline uint32_t InsecureRand32() { return insecure_rand_ctx.rand32(); }
|
||||||
|
|
|
@ -104,8 +104,8 @@ void BuildChain(const uint256& root, int height, const unsigned int invalid_rate
|
||||||
{
|
{
|
||||||
if (height <= 0 || blocks.size() >= max_size) return;
|
if (height <= 0 || blocks.size() >= max_size) return;
|
||||||
|
|
||||||
bool gen_invalid = GetRand(100) < invalid_rate;
|
bool gen_invalid = InsecureRandRange(100) < invalid_rate;
|
||||||
bool gen_fork = GetRand(100) < branch_rate;
|
bool gen_fork = InsecureRandRange(100) < branch_rate;
|
||||||
|
|
||||||
const std::shared_ptr<const CBlock> pblock = gen_invalid ? BadBlock(root) : GoodBlock(root);
|
const std::shared_ptr<const CBlock> pblock = gen_invalid ? BadBlock(root) : GoodBlock(root);
|
||||||
blocks.push_back(pblock);
|
blocks.push_back(pblock);
|
||||||
|
@ -157,7 +157,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
|
||||||
threads.create_thread([&blocks]() {
|
threads.create_thread([&blocks]() {
|
||||||
bool ignored;
|
bool ignored;
|
||||||
for (int i = 0; i < 1000; i++) {
|
for (int i = 0; i < 1000; i++) {
|
||||||
auto block = blocks[GetRand(blocks.size() - 1)];
|
auto block = blocks[InsecureRandRange(blocks.size() - 1)];
|
||||||
ProcessNewBlock(Params(), block, true, &ignored);
|
ProcessNewBlock(Params(), block, true, &ignored);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -223,7 +223,7 @@ bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& group
|
||||||
std::vector<OutputGroup> applicable_groups;
|
std::vector<OutputGroup> applicable_groups;
|
||||||
CAmount nTotalLower = 0;
|
CAmount nTotalLower = 0;
|
||||||
|
|
||||||
random_shuffle(groups.begin(), groups.end(), GetRandInt);
|
Shuffle(groups.begin(), groups.end(), FastRandomContext());
|
||||||
|
|
||||||
for (const OutputGroup& group : groups) {
|
for (const OutputGroup& group : groups) {
|
||||||
if (group.m_value == nTargetValue) {
|
if (group.m_value == nTargetValue) {
|
||||||
|
|
|
@ -2462,7 +2462,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
|
||||||
// Cases where we have 11+ outputs all pointing to the same destination may result in
|
// Cases where we have 11+ outputs all pointing to the same destination may result in
|
||||||
// privacy leaks as they will potentially be deterministically sorted. We solve that by
|
// privacy leaks as they will potentially be deterministically sorted. We solve that by
|
||||||
// explicitly shuffling the outputs before processing
|
// explicitly shuffling the outputs before processing
|
||||||
std::shuffle(vCoins.begin(), vCoins.end(), FastRandomContext());
|
Shuffle(vCoins.begin(), vCoins.end(), FastRandomContext());
|
||||||
}
|
}
|
||||||
std::vector<OutputGroup> groups = GroupOutputs(vCoins, !coin_control.m_avoid_partial_spends);
|
std::vector<OutputGroup> groups = GroupOutputs(vCoins, !coin_control.m_avoid_partial_spends);
|
||||||
|
|
||||||
|
@ -2922,7 +2922,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
|
||||||
// Shuffle selected coins and fill in final vin
|
// Shuffle selected coins and fill in final vin
|
||||||
txNew.vin.clear();
|
txNew.vin.clear();
|
||||||
std::vector<CInputCoin> selected_coins(setCoins.begin(), setCoins.end());
|
std::vector<CInputCoin> selected_coins(setCoins.begin(), setCoins.end());
|
||||||
std::shuffle(selected_coins.begin(), selected_coins.end(), FastRandomContext());
|
Shuffle(selected_coins.begin(), selected_coins.end(), FastRandomContext());
|
||||||
|
|
||||||
// Note how the sequence number is set to non-maxint so that
|
// Note how the sequence number is set to non-maxint so that
|
||||||
// the nLockTime set above actually works.
|
// the nLockTime set above actually works.
|
||||||
|
|
Loading…
Reference in a new issue