From c2ec99ba8eacba4a248b219ba583ea8c5c1f7876 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Sun, 20 Nov 2016 10:52:45 +0400 Subject: [PATCH] thread safe rand (#1157) * thread safe InsecureRand class * remove GetInsecureRand, use GetRandInt for performance non-critical parts --- src/darksend.cpp | 14 +++++++------- src/masternodeman.cpp | 5 +++-- src/random.cpp | 18 ++++++++++++++++++ src/random.h | 29 +++++++++++++++++++++++++---- src/wallet/wallet.cpp | 5 +++-- 5 files changed, 56 insertions(+), 15 deletions(-) diff --git a/src/darksend.cpp b/src/darksend.cpp index 1c4aa813a..04d11ae38 100644 --- a/src/darksend.cpp +++ b/src/darksend.cpp @@ -664,7 +664,7 @@ void CDarksendPool::ChargeFees() if(!fMasterNode) return; //we don't need to charge collateral for every offence. - if(GetInsecureRand(100) > 33) return; + if(GetRandInt(100) > 33) return; std::vector vecOffendersCollaterals; @@ -699,7 +699,7 @@ void CDarksendPool::ChargeFees() if(vecOffendersCollaterals.empty()) return; //mostly offending? Charge sometimes - if((int)vecOffendersCollaterals.size() >= Params().PoolMaxTransactions() - 1 && GetInsecureRand(100) > 33) return; + if((int)vecOffendersCollaterals.size() >= Params().PoolMaxTransactions() - 1 && GetRandInt(100) > 33) return; //everyone is an offender? That's not right if((int)vecOffendersCollaterals.size() >= Params().PoolMaxTransactions()) return; @@ -740,7 +740,7 @@ void CDarksendPool::ChargeRandomFees() BOOST_FOREACH(const CTransaction& txCollateral, vecSessionCollaterals) { - if(GetInsecureRand(100) > 10) return; + if(GetRandInt(100) > 10) return; LogPrintf("CDarksendPool::ChargeRandomFees -- charging random fees, txCollateral=%s", txCollateral.ToString()); @@ -1480,7 +1480,7 @@ bool CDarksendPool::DoAutomaticDenominating(bool fDryRun) LogPrint("privatesend", " vecMasternodesUsed: new size: %d, threshold: %d\n", (int)vecMasternodesUsed.size(), nThreshold_high); } - bool fUseQueue = insecure_rand()%100 > 33; + bool fUseQueue = GetRandInt(100) > 33; // don't use the queues all of the time for mixing unless we are a liquidity provider if(nLiquidityProvider || fUseQueue) { @@ -2014,7 +2014,7 @@ bool CDarksendPool::CreateNewSession(int nDenom, CTransaction txCollateral, Pool // start new session nMessageIDRet = MSG_NOERR; - nSessionID = GetInsecureRand(999999)+1; + nSessionID = GetRandInt(999999)+1; nSessionDenom = nDenom; SetState(POOL_STATE_QUEUE); @@ -2145,7 +2145,7 @@ int CDarksendPool::GetDenominations(const std::vector& vecTxOut, bool fS int c = 0; // if the denomination is used, shift the bit on BOOST_FOREACH (PAIRTYPE(CAmount, int)& s, vecDenomUsed) { - int bit = (fSingleRandomDenom ? insecure_rand()%2 : 1) & s.second; + int bit = (fSingleRandomDenom ? GetRandInt(2) : 1) & s.second; nDenom |= bit << c++; if(fSingleRandomDenom && bit) break; // use just one random denomination } @@ -2462,7 +2462,7 @@ void ThreadCheckDarkSendPool() if(nDoAutoNextRun == nTick) { darkSendPool.DoAutomaticDenominating(); - nDoAutoNextRun = nTick + PRIVATESEND_AUTO_TIMEOUT_MIN + GetInsecureRand(PRIVATESEND_AUTO_TIMEOUT_MAX - PRIVATESEND_AUTO_TIMEOUT_MIN); + nDoAutoNextRun = nTick + PRIVATESEND_AUTO_TIMEOUT_MIN + GetRandInt(PRIVATESEND_AUTO_TIMEOUT_MAX - PRIVATESEND_AUTO_TIMEOUT_MIN); } } } diff --git a/src/masternodeman.cpp b/src/masternodeman.cpp index 8c496ca55..8abc31658 100644 --- a/src/masternodeman.cpp +++ b/src/masternodeman.cpp @@ -543,8 +543,9 @@ CMasternode* CMasternodeMan::FindRandomNotInVec(const std::vector &vecToE vpMasternodesShuffled.push_back(&mn); } + InsecureRand insecureRand; // shuffle pointers - std::random_shuffle(vpMasternodesShuffled.begin(), vpMasternodesShuffled.end(), GetInsecureRand); + std::random_shuffle(vpMasternodesShuffled.begin(), vpMasternodesShuffled.end(), insecureRand); bool fExclude; // loop through @@ -969,7 +970,7 @@ bool CMasternodeMan::SendVerifyRequest(const CAddress& addr, const std::vectornHeight - 1); + CMasternodeVerification mnv(addr, GetRandInt(999999), pCurrentBlockIndex->nHeight - 1); mWeAskedForVerification[addr] = mnv; LogPrintf("CMasternodeMan::SendVerifyRequest -- verifying using nonce %d addr=%s\n", mnv.nonce, addr.ToString()); pnode->PushMessage(NetMsgType::MNVERIFY, mnv); diff --git a/src/random.cpp b/src/random.cpp index 6155c0d8c..34fed0931 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -137,3 +137,21 @@ void seed_insecure_rand(bool fDeterministic) insecure_rand_Rw = tmp; } } + +InsecureRand::InsecureRand(bool _fDeterministic) + : nRz(11), + nRw(11), + fDeterministic(_fDeterministic) +{ + // The seed values have some unlikely fixed points which we avoid. + if(fDeterministic) return; + uint32_t nTmp; + do { + GetRandBytes((unsigned char*)&nTmp, 4); + } while (nTmp == 0 || nTmp == 0x9068ffffU); + nRz = nTmp; + do { + GetRandBytes((unsigned char*)&nTmp, 4); + } while (nTmp == 0 || nTmp == 0x464fffffU); + nRw = nTmp; +} diff --git a/src/random.h b/src/random.h index 2e72d57d3..00b150309 100644 --- a/src/random.h +++ b/src/random.h @@ -47,10 +47,31 @@ static inline uint32_t insecure_rand(void) } /** - * Function for std::random_shuffle + * PRNG initialized from secure entropy based RNG */ -static inline uint32_t GetInsecureRand(uint32_t i){ - return insecure_rand() % i; -} +class InsecureRand +{ +private: + uint32_t nRz; + uint32_t nRw; + bool fDeterministic; + +public: + InsecureRand(bool _fDeterministic = false); + + /** + * MWC RNG of George Marsaglia + * This is intended to be fast. It has a period of 2^59.3, though the + * least significant 16 bits only have a period of about 2^30.1. + * + * @return random value < nMax + */ + int64_t operator()(int64_t nMax) + { + nRz = 36969 * (nRz & 65535) + (nRz >> 16); + nRw = 18000 * (nRw & 65535) + (nRw >> 16); + return ((nRw << 16) + nRz) % nMax; + } +}; #endif // BITCOIN_RANDOM_H diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 20546daf0..38f030123 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2501,6 +2501,7 @@ bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount int nDenomResult = 0; + InsecureRand insecureRand; BOOST_FOREACH(const COutput& out, vCoins) { // masternode-like input should not be selected by AvailableCoins now anyway @@ -2517,9 +2518,9 @@ bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount if(out.tx->vout[out.i].nValue == vecPrivateSendDenominations[nBit]) { if(nValueRet >= nValueMin) { //randomly reduce the max amount we'll submit (for anonymity) - nValueMax -= (GetInsecureRand(nValueMax/5)); + nValueMax -= insecureRand(nValueMax/5); //on average use 50% of the inputs or less - int r = GetInsecureRand((int)vCoins.size()); + int r = insecureRand(vCoins.size()); if((int)vecTxInRet.size() > r) return true; } txin.prevPubKey = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey