@@ -1577,9 +1577,11 @@ int CWallet::GetRealOutpointPrivateSendRounds(const COutPoint& outpoint, int nRo
15771577{
15781578 LOCK (cs_wallet);
15791579
1580- if (nRounds >= MAX_PRIVATESEND_ROUNDS) {
1581- // there can only be MAX_PRIVATESEND_ROUNDS rounds max
1582- return MAX_PRIVATESEND_ROUNDS - 1 ;
1580+ const int nRoundsMax = MAX_PRIVATESEND_ROUNDS + privateSendClient.nPrivateSendRandomRounds ;
1581+
1582+ if (nRounds >= nRoundsMax) {
1583+ // there can only be nRoundsMax rounds max
1584+ return nRoundsMax - 1 ;
15831585 }
15841586
15851587 auto pair = mapOutpointRoundsCache.emplace (outpoint, -10 );
@@ -1645,7 +1647,7 @@ int CWallet::GetRealOutpointPrivateSendRounds(const COutPoint& outpoint, int nRo
16451647 }
16461648 }
16471649 *nRoundsRef = fDenomFound
1648- ? (nShortest >= MAX_PRIVATESEND_ROUNDS - 1 ? MAX_PRIVATESEND_ROUNDS : nShortest + 1 ) // good, we a +1 to the shortest one but only MAX_PRIVATESEND_ROUNDS rounds max allowed
1650+ ? (nShortest >= nRoundsMax - 1 ? nRoundsMax : nShortest + 1 ) // good, we a +1 to the shortest one but only nRoundsMax rounds max allowed
16491651 : 0 ; // too bad, we are the fist one in that chain
16501652 LogPrint (BCLog::PRIVATESEND, " %s UPDATED %-70s %3d\n " , __func__, outpoint.ToStringShort (), *nRoundsRef);
16511653 return *nRoundsRef;
@@ -1675,6 +1677,29 @@ bool CWallet::IsDenominated(const COutPoint& outpoint) const
16751677 return CPrivateSend::IsDenominatedAmount (it->second .tx ->vout [outpoint.n ].nValue );
16761678}
16771679
1680+ bool CWallet::IsFullyMixed (const COutPoint& outpoint) const
1681+ {
1682+ int nRounds = GetRealOutpointPrivateSendRounds (outpoint);
1683+ // Mix again if we don't have N rounds yet
1684+ if (nRounds < privateSendClient.nPrivateSendRounds ) return false ;
1685+
1686+ // Try to mix a "random" number of rounds more than minimum.
1687+ // If we have already mixed N + MaxOffset rounds, don't mix again.
1688+ // Otherwise, we should mix again 50% of the time, this results in an exponential decay
1689+ // N rounds 50% N+1 25% N+2 12.5%... until we reach N + GetRandomRounds() rounds where we stop.
1690+ if (nRounds < privateSendClient.nPrivateSendRounds + privateSendClient.nPrivateSendRandomRounds ) {
1691+ CDataStream ss (SER_GETHASH, PROTOCOL_VERSION);
1692+ ss << outpoint << nPrivateSendSalt;
1693+ uint256 nHash;
1694+ CSHA256 ().Write ((const unsigned char *)ss.data (), ss.size ()).Finalize (nHash.begin ());
1695+ if (nHash.GetCheapHash () % 2 == 0 ) {
1696+ return false ;
1697+ }
1698+ }
1699+
1700+ return true ;
1701+ }
1702+
16781703isminetype CWallet::IsMine (const CTxOut& txout) const
16791704{
16801705 return ::IsMine (*this , txout.scriptPubKey );
@@ -2317,8 +2342,7 @@ CAmount CWalletTx::GetAnonymizedCredit(const CCoinControl* coinControl) const
23172342
23182343 if (pwallet->IsSpent (hashTx, i) || !CPrivateSend::IsDenominatedAmount (txout.nValue )) continue ;
23192344
2320- const int nRounds = pwallet->GetCappedOutpointPrivateSendRounds (outpoint);
2321- if (nRounds >= privateSendClient.nPrivateSendRounds ){
2345+ if (pwallet->IsFullyMixed (outpoint)) {
23222346 nCredit += pwallet->GetCredit (txout, ISMINE_SPENDABLE);
23232347 if (!MoneyRange (nCredit))
23242348 throw std::runtime_error (std::string (__func__) + " : value out of range" );
@@ -2787,12 +2811,10 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const
27872811 bool found = false ;
27882812 if (nCoinType == CoinType::ONLY_FULLY_MIXED) {
27892813 if (!CPrivateSend::IsDenominatedAmount (pcoin->tx ->vout [i].nValue )) continue ;
2790- int nRounds = GetCappedOutpointPrivateSendRounds (COutPoint (wtxid, i));
2791- found = nRounds >= privateSendClient.nPrivateSendRounds ;
2814+ found = IsFullyMixed (COutPoint (wtxid, i));
27922815 } else if (nCoinType == CoinType::ONLY_READY_TO_MIX) {
27932816 if (!CPrivateSend::IsDenominatedAmount (pcoin->tx ->vout [i].nValue )) continue ;
2794- int nRounds = GetCappedOutpointPrivateSendRounds (COutPoint (wtxid, i));
2795- found = nRounds < privateSendClient.nPrivateSendRounds ;
2817+ found = !IsFullyMixed (COutPoint (wtxid, i));
27962818 } else if (nCoinType == CoinType::ONLY_NONDENOMINATED) {
27972819 if (CPrivateSend::IsCollateralAmount (pcoin->tx ->vout [i].nValue )) continue ; // do not use collateral amounts
27982820 found = !CPrivateSend::IsDenominatedAmount (pcoin->tx ->vout [i].nValue );
@@ -2909,6 +2931,21 @@ const CTxOut& CWallet::FindNonChangeParentOutput(const CTransaction& tx, int out
29092931 return ptx->vout [n];
29102932}
29112933
2934+ void CWallet::InitPrivateSendSalt ()
2935+ {
2936+ // Avoid fetching it multiple times
2937+ assert (nPrivateSendSalt.IsNull ());
2938+
2939+ WalletBatch batch (*database);
2940+ batch.ReadPrivateSendSalt (nPrivateSendSalt);
2941+
2942+ while (nPrivateSendSalt.IsNull ()) {
2943+ // We never generated/saved it
2944+ nPrivateSendSalt = GetRandHash ();
2945+ batch.WritePrivateSendSalt (nPrivateSendSalt);
2946+ }
2947+ }
2948+
29122949static void ApproximateBestSubset (const std::vector<CInputCoin>& vValue, const CAmount& nTotalLower, const CAmount& nTargetValue,
29132950 std::vector<char >& vfBest, CAmount& nBest, int iterations = 1000 )
29142951{
@@ -3174,8 +3211,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
31743211 if (nCoinType == CoinType::ONLY_FULLY_MIXED) {
31753212 // Make sure to include mixed preset inputs only,
31763213 // even if some non-mixed inputs were manually selected via CoinControl
3177- int nRounds = GetRealOutpointPrivateSendRounds (outpoint);
3178- if (nRounds < privateSendClient.nPrivateSendRounds ) continue ;
3214+ if (!IsFullyMixed (outpoint)) continue ;
31793215 }
31803216 nValueFromPresetInputs += pcoin->tx ->vout [outpoint.n ].nValue ;
31813217 setPresetCoins.insert (CInputCoin (pcoin, outpoint.n ));
@@ -3376,7 +3412,7 @@ bool CWallet::SelectCoinsGroupedByAddresses(std::vector<CompactTallyItem>& vecTa
33763412 // otherwise they will just lead to higher fee / lower priority
33773413 if (wtx.tx ->vout [i].nValue <= nSmallestDenom/10 ) continue ;
33783414 // ignore mixed
3379- if ( GetCappedOutpointPrivateSendRounds (COutPoint (outpoint.hash , i)) >= privateSendClient. nPrivateSendRounds ) continue ;
3415+ if ( IsFullyMixed (COutPoint (outpoint.hash , i))) continue ;
33803416 }
33813417
33823418 if (itTallyItem == mapTally.end ()) {
@@ -4155,6 +4191,8 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
41554191 }
41564192 }
41574193
4194+ InitPrivateSendSalt ();
4195+
41584196 if (nLoadWalletRet != DB_LOAD_OK)
41594197 return nLoadWalletRet;
41604198
0 commit comments