Skip to content

Commit

Permalink
merge bitcoin#14624: Some simple improvements to the RNG code
Browse files Browse the repository at this point in the history
  • Loading branch information
kwvg committed Jul 15, 2021
1 parent 791c7c7 commit 7dd3e9a
Show file tree
Hide file tree
Showing 14 changed files with 73 additions and 77 deletions.
26 changes: 11 additions & 15 deletions src/addrman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime
return;

// 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;
for (unsigned int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
int nB = (n + nRnd) % ADDRMAN_NEW_BUCKET_COUNT;
Expand Down Expand Up @@ -319,7 +319,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP
int nFactor = 1;
for (int n = 0; n < pinfo->nRefCount; n++)
nFactor *= 2;
if (nFactor > 1 && (RandomInt(nFactor) != 0))
if (nFactor > 1 && (insecure_rand.randrange(nFactor) != 0))
return false;
} else {
pinfo = Create(addr, source, &nId);
Expand Down Expand Up @@ -384,37 +384,37 @@ CAddrInfo CAddrMan::Select_(bool newOnly)

// Use a 50% chance for choosing between tried and new table entries.
if (!newOnly &&
(nTried > 0 && (nNew == 0 || RandomInt(2) == 0))) {
(nTried > 0 && (nNew == 0 || insecure_rand.randbool() == 0))) {
// use a tried node
double fChanceFactor = 1.0;
while (1) {
int nKBucket = RandomInt(ADDRMAN_TRIED_BUCKET_COUNT);
int nKBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
int nKBucket = insecure_rand.randrange(ADDRMAN_TRIED_BUCKET_COUNT);
int nKBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
while (vvTried[nKBucket][nKBucketPos] == -1) {
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;
}
int nId = vvTried[nKBucket][nKBucketPos];
assert(mapInfo.count(nId) == 1);
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;
fChanceFactor *= 1.2;
}
} else {
// use a new node
double fChanceFactor = 1.0;
while (1) {
int nUBucket = RandomInt(ADDRMAN_NEW_BUCKET_COUNT);
int nUBucketPos = RandomInt(ADDRMAN_BUCKET_SIZE);
int nUBucket = insecure_rand.randrange(ADDRMAN_NEW_BUCKET_COUNT);
int nUBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
while (vvNew[nUBucket][nUBucketPos] == -1) {
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;
}
int nId = vvNew[nUBucket][nUBucketPos];
assert(mapInfo.count(nId) == 1);
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;
fChanceFactor *= 1.2;
}
Expand Down Expand Up @@ -510,7 +510,7 @@ void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr)
if (vAddr.size() >= nNodes)
break;

int nRndPos = RandomInt(vRandom.size() - n) + n;
int nRndPos = insecure_rand.randrange(vRandom.size() - n) + n;
SwapRandom(n, nRndPos);
assert(mapInfo.count(vRandom[n]) == 1);

Expand Down Expand Up @@ -575,10 +575,6 @@ CAddrInfo CAddrMan::GetAddressInfo_(const CService& addr)
return *pinfo;
}

int CAddrMan::RandomInt(int nMax){
return GetRandInt(nMax);
}

void CAddrMan::ResolveCollisions_()
{
for (std::set<int>::iterator it = m_tried_collisions.begin(); it != m_tried_collisions.end();) {
Expand Down Expand Up @@ -645,7 +641,7 @@ CAddrInfo CAddrMan::SelectTriedCollision_()
std::set<int>::iterator it = m_tried_collisions.begin();

// 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;

// If id_new not found in mapInfo remove it from m_tried_collisions
Expand Down
5 changes: 1 addition & 4 deletions src/addrman.h
Original file line number Diff line number Diff line change
Expand Up @@ -276,9 +276,6 @@ class CAddrMan
//! Return a random to-be-evicted tried table address.
CAddrInfo SelectTriedCollision_();

//! Wraps GetRandInt to allow tests to override RandomInt and make it determinismistic.
virtual int RandomInt(int nMax);

#ifdef DEBUG_ADDRMAN
//! Perform consistency check. Returns an error code or zero.
int Check_();
Expand Down Expand Up @@ -561,7 +558,7 @@ class CAddrMan
{
LOCK(cs);
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 entry = 0; entry < ADDRMAN_BUCKET_SIZE; entry++) {
vvNew[bucket][entry] = -1;
Expand Down
9 changes: 5 additions & 4 deletions src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,12 @@ static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn
const int64_t nOneWeek = 7*24*60*60;
std::vector<CAddress> vSeedsOut;
vSeedsOut.reserve(vSeedsIn.size());
FastRandomContext rng;
for (const auto& seed_in : vSeedsIn) {
struct in6_addr ip;
memcpy(&ip, seed_in.addr, sizeof(ip));
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);
}
return vSeedsOut;
Expand Down Expand Up @@ -229,16 +230,16 @@ void AdvertiseLocal(CNode *pnode)
// 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
// address than we do.
FastRandomContext rng;
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());
}
if (addrLocal.IsRoutable() || gArgs.GetBoolArg("-addrmantest", false))
{
LogPrint(BCLog::NET, "AdvertiseLocal: advertising address %s\n", addrLocal.ToString());
FastRandomContext insecure_rand;
pnode->PushAddress(addrLocal, insecure_rand);
pnode->PushAddress(addrLocal, rng);
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/net_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1036,10 +1036,11 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphansSize)
nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx due to expiration\n", nErased);
}
FastRandomContext rng;
while (!mapOrphanTransactions.empty() && nMapOrphanTransactionsSize > nMaxOrphansSize)
{
// Evict a random orphan:
uint256 randomhash = GetRandHash();
uint256 randomhash = rng.rand256();
std::map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
if (it == mapOrphanTransactions.end())
it = mapOrphanTransactions.begin();
Expand Down
15 changes: 15 additions & 0 deletions src/random.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ uint256 FastRandomContext::rand256()

std::vector<unsigned char> FastRandomContext::randbytes(size_t len)
{
if (requires_seed) RandomSeed();
std::vector<unsigned char> ret(len);
if (len > 0) {
rng.Keystream(&ret[0], len);
Expand Down Expand Up @@ -489,6 +490,20 @@ FastRandomContext::FastRandomContext(bool fDeterministic) : requires_seed(!fDete
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()
{
RDRandInit();
Expand Down
8 changes: 8 additions & 0 deletions src/random.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ class FastRandomContext {
/** Initialize with explicit seed (only for testing) */
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. */
uint64_t rand64()
{
Expand Down
6 changes: 0 additions & 6 deletions src/test/addrman_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,6 @@ class CAddrManTest : public CAddrMan
insecure_rand = FastRandomContext(true);
}

int RandomInt(int nMax) override
{
state = (CHashWriter(SER_GETHASH, 0) << state).GetHash().GetCheapHash();
return (unsigned int)(state % nMax);
}

CAddrInfo* Find(const CService& addr, int* pnId = nullptr)
{
return CAddrMan::Find(addr, pnId);
Expand Down
39 changes: 11 additions & 28 deletions src/test/cuckoocache_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,40 +21,23 @@
* using BOOST_CHECK_CLOSE to fail.
*
*/
FastRandomContext local_rand_ctx(true);

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.
*
* There are no repeats in the first 200000 insecure_GetRandHash calls
*/
BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes)
{
local_rand_ctx = FastRandomContext(true);
SeedInsecureRand(true);
CuckooCache::cache<uint256, SignatureCacheHasher> cc{};
size_t megabytes = 4;
cc.setup_bytes(megabytes << 20);
uint256 v;
for (int x = 0; x < 100000; ++x) {
insecure_GetRandHash(v);
cc.insert(v);
cc.insert(InsecureRand256());
}
for (int x = 0; x < 100000; ++x) {
insecure_GetRandHash(v);
BOOST_CHECK(!cc.contains(v, false));
BOOST_CHECK(!cc.contains(InsecureRand256(), false));
}
};

Expand All @@ -64,7 +47,7 @@ BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes)
template <typename Cache>
static double test_cache(size_t megabytes, double load)
{
local_rand_ctx = FastRandomContext(true);
SeedInsecureRand(true);
std::vector<uint256> hashes;
Cache set{};
size_t bytes = megabytes * (1 << 20);
Expand All @@ -74,7 +57,7 @@ static double test_cache(size_t megabytes, double load)
for (uint32_t i = 0; i < n_insert; ++i) {
uint32_t* ptr = (uint32_t*)hashes[i].begin();
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
* cuckoocache may overwrite the inserted element, so the test is
Expand Down Expand Up @@ -135,7 +118,7 @@ template <typename Cache>
static void test_cache_erase(size_t megabytes)
{
double load = 1;
local_rand_ctx = FastRandomContext(true);
SeedInsecureRand(true);
std::vector<uint256> hashes;
Cache set{};
size_t bytes = megabytes * (1 << 20);
Expand All @@ -145,7 +128,7 @@ static void test_cache_erase(size_t megabytes)
for (uint32_t i = 0; i < n_insert; ++i) {
uint32_t* ptr = (uint32_t*)hashes[i].begin();
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
* cuckoocache may overwrite the inserted element, so the test is
Expand Down Expand Up @@ -198,7 +181,7 @@ template <typename Cache>
static void test_cache_erase_parallel(size_t megabytes)
{
double load = 1;
local_rand_ctx = FastRandomContext(true);
SeedInsecureRand(true);
std::vector<uint256> hashes;
Cache set{};
size_t bytes = megabytes * (1 << 20);
Expand All @@ -208,7 +191,7 @@ static void test_cache_erase_parallel(size_t megabytes)
for (uint32_t i = 0; i < n_insert; ++i) {
uint32_t* ptr = (uint32_t*)hashes[i].begin();
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
* cuckoocache may overwrite the inserted element, so the test is
Expand Down Expand Up @@ -300,7 +283,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.
local_rand_ctx = FastRandomContext(true);
SeedInsecureRand(true);

// 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
Expand All @@ -317,7 +300,7 @@ static void test_cache_generations()
for (uint32_t i = 0; i < n_insert; ++i) {
uint32_t* ptr = (uint32_t*)inserts[i].begin();
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)
reads.push_back(inserts[i]);
Expand Down
2 changes: 1 addition & 1 deletion src/test/denialofservice_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)

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), 0, INVALID_SOCKET, addr, 0, 0, CAddress(), "", /*fInboundIn=*/ false));
CNode &node = *vNodes.back();
node.SetSendVersion(PROTOCOL_VERSION);
Expand Down
4 changes: 2 additions & 2 deletions src/test/prevector_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,8 @@ class prevector_tester {

prevector_tester() {
SeedInsecureRand();
rand_seed = insecure_rand_seed;
rand_cache = insecure_rand_ctx;
rand_seed = InsecureRand256();
rand_cache = FastRandomContext(rand_seed);
}
};

Expand Down
17 changes: 12 additions & 5 deletions src/test/random_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,18 @@ BOOST_AUTO_TEST_CASE(fastrandom_tests)
BOOST_CHECK(GetRand(std::numeric_limits<uint64_t>::max()) != uint64_t{10393729187455219830U});
BOOST_CHECK(GetRandInt(std::numeric_limits<int>::max()) != int{769702006});
}
FastRandomContext ctx3;
FastRandomContext ctx4;
BOOST_CHECK(ctx3.rand64() != ctx4.rand64()); // extremely unlikely to be equal
BOOST_CHECK(ctx3.rand256() != ctx4.rand256());
BOOST_CHECK(ctx3.randbytes(7) != ctx4.randbytes(7));
{
FastRandomContext ctx3, ctx4;
BOOST_CHECK(ctx3.rand64() != ctx4.rand64()); // extremely unlikely to be equal
}
{
FastRandomContext ctx3, ctx4;
BOOST_CHECK(ctx3.rand256() != ctx4.rand256());
}
{
FastRandomContext ctx3, ctx4;
BOOST_CHECK(ctx3.randbytes(7) != ctx4.randbytes(7));
}
}

BOOST_AUTO_TEST_CASE(fastrandom_randbits)
Expand Down
3 changes: 1 addition & 2 deletions src/test/test_dash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ void CConnmanTest::ClearNodes()
g_connman->mapNodesWithDataToSend.clear();
}

uint256 insecure_rand_seed = GetRandHash();
FastRandomContext insecure_rand_ctx(insecure_rand_seed);
FastRandomContext insecure_rand_ctx;

extern bool fPrintToConsole;
extern void noui_connect();
Expand Down
7 changes: 1 addition & 6 deletions src/test/test_dash.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,7 @@ extern bool g_mock_deterministic_tests;

static inline void SeedInsecureRand(bool fDeterministic = false)
{
if (fDeterministic) {
insecure_rand_seed = uint256();
} else {
insecure_rand_seed = GetRandHash();
}
insecure_rand_ctx = FastRandomContext(insecure_rand_seed);
insecure_rand_ctx = FastRandomContext(fDeterministic);
}

static inline uint32_t InsecureRand32() { return insecure_rand_ctx.rand32(); }
Expand Down
Loading

0 comments on commit 7dd3e9a

Please sign in to comment.