diff --git a/src/privatesend/privatesend-client.cpp b/src/privatesend/privatesend-client.cpp index efc8a073d4dc..34160286ad3a 100644 --- a/src/privatesend/privatesend-client.cpp +++ b/src/privatesend/privatesend-client.cpp @@ -888,12 +888,12 @@ bool CPrivateSendClientSession::DoAutomaticDenominating(CConnman& connman, bool // there are funds to denominate and denominated balance does not exceed // max amount to mix yet. if (nBalanceAnonimizableNonDenom >= nValueMin + CPrivateSend::GetCollateralAmount() && nBalanceToDenominate > 0) { - CreateDenominated(nBalanceToDenominate, connman); + CreateDenominated(nBalanceToDenominate); } //check if we have the collateral sized inputs if (!mixingWallet->HasCollateralInputs()) { - return !mixingWallet->HasCollateralInputs(false) && MakeCollateralAmounts(connman); + return !mixingWallet->HasCollateralInputs(false) && MakeCollateralAmounts(); } if (nSessionID) { @@ -1376,7 +1376,7 @@ bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds } // Create collaterals by looping through inputs grouped by addresses -bool CPrivateSendClientSession::MakeCollateralAmounts(CConnman& connman) +bool CPrivateSendClientSession::MakeCollateralAmounts() { if (!CPrivateSendClientOptions::IsEnabled() || !mixingWallet) return false; @@ -1400,13 +1400,13 @@ bool CPrivateSendClientSession::MakeCollateralAmounts(CConnman& connman) // First try to use only non-denominated funds for (const auto& item : vecTally) { - if (!MakeCollateralAmounts(item, false, connman)) continue; + if (!MakeCollateralAmounts(item, false)) continue; return true; } // There should be at least some denominated funds we should be able to break in pieces to continue mixing for (const auto& item : vecTally) { - if (!MakeCollateralAmounts(item, true, connman)) continue; + if (!MakeCollateralAmounts(item, true)) continue; return true; } @@ -1416,7 +1416,7 @@ bool CPrivateSendClientSession::MakeCollateralAmounts(CConnman& connman) } // Split up large inputs or create fee sized inputs -bool CPrivateSendClientSession::MakeCollateralAmounts(const CompactTallyItem& tallyItem, bool fTryDenominated, CConnman& connman) +bool CPrivateSendClientSession::MakeCollateralAmounts(const CompactTallyItem& tallyItem, bool fTryDenominated) { AssertLockHeld(cs_main); AssertLockHeld(mempool.cs); @@ -1424,8 +1424,8 @@ bool CPrivateSendClientSession::MakeCollateralAmounts(const CompactTallyItem& ta if (!CPrivateSendClientOptions::IsEnabled() || !mixingWallet) return false; - // Skip way too tiny amounts - if (tallyItem.nAmount < CPrivateSend::GetCollateralAmount()) { + // Denominated input is always a single one, so we can check its amount directly and return early + if (!fTryDenominated && tallyItem.vecOutPoints.size() == 1 && CPrivateSend::IsDenominatedAmount(tallyItem.nAmount)) { return false; } @@ -1434,88 +1434,77 @@ bool CPrivateSendClientSession::MakeCollateralAmounts(const CompactTallyItem& ta return false; } - // denominated input is always a single one, so we can check its amount directly and return early - if (!fTryDenominated && tallyItem.vecOutPoints.size() == 1 && CPrivateSend::IsDenominatedAmount(tallyItem.nAmount)) { + CTransactionBuilder txBuilder(mixingWallet, tallyItem); + + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- Start %s\n", __func__, txBuilder.ToString()); + + // Skip way too tiny amounts. Smallest we want is minimum collateral amount in a one output tx + if (!txBuilder.CouldAddOutput(CPrivateSend::GetCollateralAmount())) { return false; } - CWalletTx wtx; - CAmount nFeeRet = 0; - int nChangePosRet = -1; - std::string strFail; - std::vector vecSend; + int nCase{0}; // Just for debug logs + if (txBuilder.CouldAddOutputs({CPrivateSend::GetMaxCollateralAmount(), CPrivateSend::GetCollateralAmount()})) { + nCase = 1; + // , see TransactionRecord::decomposeTransaction + // Out1 == CPrivateSend::GetMaxCollateralAmount() + // Out2 >= CPrivateSend::GetCollateralAmount() - // make our collateral address - CReserveKey reservekeyCollateral(mixingWallet); - // make our change address - CReserveKey reservekeyChange(mixingWallet); + txBuilder.AddOutput(CPrivateSend::GetMaxCollateralAmount()); + // Note, here we first add a zero amount output to get the remainder after all fees and then assign it + CTransactionBuilderOutput* out = txBuilder.AddOutput(); + CAmount nAmountLeft = txBuilder.GetAmountLeft(); + // If remainder is denominated add one duff to the fee + out->UpdateAmount(CPrivateSend::IsDenominatedAmount(nAmountLeft) ? nAmountLeft - 1 : nAmountLeft); - CScript scriptCollateral; - CPubKey vchPubKey; - assert(reservekeyCollateral.GetReservedKey(vchPubKey, false)); // should never fail, as we just unlocked - scriptCollateral = GetScriptForDestination(vchPubKey.GetID()); + } else if (txBuilder.CouldAddOutputs({CPrivateSend::GetCollateralAmount(), CPrivateSend::GetCollateralAmount()})) { + nCase = 2; + // , see TransactionRecord::decomposeTransaction + // Out1 CPrivateSend::IsCollateralAmount() + // Out2 CPrivateSend::IsCollateralAmount() - CAmount nCollateralAmount{0}; - if (tallyItem.nAmount > CPrivateSend::GetMaxCollateralAmount() + CPrivateSend::GetCollateralAmount()*2) { - // Change output will be large enough to be valid as a collateral or a source input for another run - nCollateralAmount = CPrivateSend::GetMaxCollateralAmount(); - } else { - // Change output might be too small for another collateral if we try to create the largest collateral - // here, create a slightly smaller one instead - nCollateralAmount = CPrivateSend::GetMaxCollateralAmount() - CPrivateSend::GetCollateralAmount(); - } - vecSend.push_back((CRecipient){scriptCollateral, nCollateralAmount, false}); - - // try to use non-denominated and not mn-like funds first, select them explicitly - CCoinControl coinControl; - coinControl.fAllowOtherInputs = false; - coinControl.fAllowWatchOnly = false; - coinControl.nCoinType = CoinType::ONLY_NONDENOMINATED; - // send change to the same address so that we were able create more denoms out of it later - coinControl.destChange = tallyItem.txdest; - for (const auto& outpoint : tallyItem.vecOutPoints) { - coinControl.Select(outpoint); - } - - bool fSuccess = mixingWallet->CreateTransaction(vecSend, wtx, reservekeyChange, - nFeeRet, nChangePosRet, strFail, coinControl); - if (!fSuccess) { - LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::MakeCollateralAmounts -- ONLY_NONDENOMINATED: %s\n", strFail); - // If we failed then most likely there are not enough funds on this address. - if (fTryDenominated) { - // Try to also use denominated coins (we can't mix denominated without collaterals anyway). - coinControl.nCoinType = CoinType::ALL_COINS; - if (!mixingWallet->CreateTransaction(vecSend, wtx, reservekeyChange, - nFeeRet, nChangePosRet, strFail, coinControl)) { - LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::MakeCollateralAmounts -- ALL_COINS Error: %s\n", strFail); - reservekeyCollateral.ReturnKey(); - return false; - } - } else { - // Nothing else we can do. - reservekeyCollateral.ReturnKey(); - return false; - } + // First add two outputs to get the available value after all fees + CTransactionBuilderOutput* out1 = txBuilder.AddOutput(); + CTransactionBuilderOutput* out2 = txBuilder.AddOutput(); + + // Create two equal outputs from the available value. This adds one duff to the fee if txBuilder.GetAmountLeft() is odd. + CAmount nAmountOutputs = txBuilder.GetAmountLeft() / 2; + + assert(CPrivateSend::IsCollateralAmount(nAmountOutputs)); + + out1->UpdateAmount(nAmountOutputs); + out2->UpdateAmount(nAmountOutputs); + + } else { // still at least possible to add one CPrivateSend::GetCollateralAmount() output + nCase = 3; + // , see TransactionRecord::decomposeTransaction + // Out1 CPrivateSend::IsCollateralAmount() + // Out2 Skipped + CTransactionBuilderOutput* out = txBuilder.AddOutput(); + out->UpdateAmount(txBuilder.GetAmountLeft()); + + assert(CPrivateSend::IsCollateralAmount(out->GetAmount())); } - reservekeyCollateral.KeepKey(); + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- Done with case %d: %s\n", __func__, nCase, txBuilder.ToString()); - LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::MakeCollateralAmounts -- txid=%s\n", wtx.GetHash().GetHex()); + assert(txBuilder.IsDust(txBuilder.GetAmountLeft())); - // use the same nCachedLastSuccessBlock as for DS mixing to prevent race - CValidationState state; - if (!mixingWallet->CommitTransaction(wtx, reservekeyChange, &connman, state)) { - LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::MakeCollateralAmounts -- CommitTransaction failed! Reason given: %s\n", state.GetRejectReason()); + std::string strResult; + if (!txBuilder.Commit(strResult)) { + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- Commit failed: %s\n", __func__, strResult); return false; } privateSendClientManagers.at(mixingWallet->GetName())->UpdatedSuccessBlock(); + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- txid: %s\n", __func__, strResult); + return true; } // Create denominations by looping through inputs grouped by addresses -bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, CConnman& connman) +bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate) { if (!CPrivateSendClientOptions::IsEnabled() || !mixingWallet) return false; @@ -1540,7 +1529,7 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, bool fCreateMixingCollaterals = !mixingWallet->HasCollateralInputs(); for (const auto& item : vecTally) { - if (!CreateDenominated(nBalanceToDenominate, item, fCreateMixingCollaterals, connman)) continue; + if (!CreateDenominated(nBalanceToDenominate, item, fCreateMixingCollaterals)) continue; return true; } @@ -1549,7 +1538,7 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, } // Create denominations -bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, const CompactTallyItem& tallyItem, bool fCreateMixingCollaterals, CConnman& connman) +bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, const CompactTallyItem& tallyItem, bool fCreateMixingCollaterals) { AssertLockHeld(cs_main); AssertLockHeld(mempool.cs); @@ -1557,32 +1546,24 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, if (!CPrivateSendClientOptions::IsEnabled() || !mixingWallet) return false; - std::vector vecSend; - CKeyHolderStorage keyHolderStorageDenom; - - CCoinControl coinControl; - // Every input will require at least this much fees in duffs - const CAmount nInputFee = GetMinimumFee(148, coinControl, ::mempool, ::feeEstimator, nullptr /* feeCalc */); - // Every output will require at least this much fees in duffs - const CAmount nOutputFee = GetMinimumFee(34, coinControl, ::mempool, ::feeEstimator, nullptr /* feeCalc */); + // denominated input is always a single one, so we can check its amount directly and return early + if (tallyItem.vecOutPoints.size() == 1 && CPrivateSend::IsDenominatedAmount(tallyItem.nAmount)) { + return false; + } - CAmount nValueLeft = tallyItem.nAmount; - // Leave some room for fees, assuming we are going to spend all the outpoints - nValueLeft -= tallyItem.vecOutPoints.size() * nInputFee; + CTransactionBuilder txBuilder(mixingWallet, tallyItem); - LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::CreateDenominated -- 0 - %s nValueLeft: %f\n", EncodeDestination(tallyItem.txdest), (float)nValueLeft / COIN); + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- Start %s\n", __func__, txBuilder.ToString()); // ****** Add an output for mixing collaterals ************ / - if (fCreateMixingCollaterals) { - CScript scriptCollateral = keyHolderStorageDenom.AddKey(mixingWallet); - vecSend.push_back((CRecipient){scriptCollateral, CPrivateSend::GetMaxCollateralAmount(), false}); - nValueLeft -= CPrivateSend::GetMaxCollateralAmount() + nOutputFee; + if (fCreateMixingCollaterals && !txBuilder.AddOutput(CPrivateSend::GetMaxCollateralAmount())) { + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- Failed to add collateral output\n", __func__); + return false; } // ****** Add outputs for denoms ************ / - int nOutputsTotal = 0; bool fAddFinal = true; std::vector vecStandardDenoms = CPrivateSend::GetStandardDenominations(); @@ -1600,81 +1581,92 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, // Now, in this system, so long as we don't reach PRIVATESEND_DENOM_OUTPUTS_THRESHOLD outputs the process repeats in // the same transaction, creating up to nPrivateSendDenomsHardCap per denomination in a single transaction. - while (nValueLeft >= CPrivateSend::GetSmallestDenomination() && nOutputsTotal < PRIVATESEND_DENOM_OUTPUTS_THRESHOLD) { - + while (txBuilder.CouldAddOutput(CPrivateSend::GetSmallestDenomination()) && txBuilder.CountOutputs() < PRIVATESEND_DENOM_OUTPUTS_THRESHOLD) { for (auto it = vecStandardDenoms.rbegin(); it != vecStandardDenoms.rend(); ++it) { CAmount nDenomValue = *it; auto currentDenomIt = mapDenomCount.find(nDenomValue); int nOutputs = 0; + const auto& strFunc = __func__; auto needMoreOutputs = [&]() { - bool fRegular = ((nValueLeft >= nDenomValue + nOutputFee) && nBalanceToDenominate >= nDenomValue); - bool fFinal = (fAddFinal - && nValueLeft >= nDenomValue + nOutputFee - && nBalanceToDenominate > 0 - && nBalanceToDenominate < nDenomValue); - if (fFinal) { - fAddFinal = false; // add final denom only once, only the smalest possible one - LogPrint(BCLog::PRIVATESEND, /* Continued */ - "CPrivateSendClientSession::CreateDenominated -- 1 - FINAL - nDenomValue: %f, nValueLeft: %f, nBalanceToDenominate: %f\n", - (float) nDenomValue / COIN, (float) nValueLeft / COIN, (float) nBalanceToDenominate / COIN); + if (txBuilder.CouldAddOutput(nDenomValue)) { + if (fAddFinal && nBalanceToDenominate > 0 && nBalanceToDenominate < nDenomValue) { + fAddFinal = false; // add final denom only once, only the smalest possible one + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- 1 - FINAL - nDenomValue: %f, nBalanceToDenominate: %f, nOutputs: %d, %s\n", + strFunc, (float) nDenomValue / COIN, (float) nBalanceToDenominate / COIN, nOutputs, txBuilder.ToString()); + return true; + } else if (nBalanceToDenominate >= nDenomValue) { + return true; + } } - - return fRegular || fFinal; + return false; }; // add each output up to 11 times or until it can't be added again or until we reach nPrivateSendDenomsGoal while (needMoreOutputs() && nOutputs <= 10 && currentDenomIt->second < CPrivateSendClientOptions::GetDenomsGoal()) { - CScript scriptDenom = keyHolderStorageDenom.AddKey(mixingWallet); - - vecSend.push_back((CRecipient) {scriptDenom, nDenomValue, false}); - - // increment outputs and subtract denomination amount - nOutputs++; - currentDenomIt->second++; - nValueLeft -= nDenomValue + nOutputFee; - nBalanceToDenominate -= nDenomValue; - LogPrint(BCLog::PRIVATESEND, /* Continued */ - "CPrivateSendClientSession::CreateDenominated -- 1 - nDenomValue: %f, totalOutputs: %d, nOutputsTotal: %d, nOutputs: %d, nValueLeft: %f, nBalanceToDenominate: %f\n", - (float) nDenomValue / COIN, nOutputsTotal + nOutputs, nOutputsTotal, nOutputs, (float) nValueLeft / COIN, (float) nBalanceToDenominate / COIN); + // Add output and subtract denomination amount + if (txBuilder.AddOutput(nDenomValue)) { + ++nOutputs; + ++currentDenomIt->second; + nBalanceToDenominate -= nDenomValue; + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- 1 - nDenomValue: %f, nBalanceToDenominate: %f, nOutputs: %d, %s\n", + __func__, (float) nDenomValue / COIN, (float) nBalanceToDenominate / COIN, nOutputs, txBuilder.ToString()); + } else { + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- 1 - Error: AddOutput failed for nDenomValue: %f, nBalanceToDenominate: %f, nOutputs: %d, %s\n", + __func__, (float) nDenomValue / COIN, (float) nBalanceToDenominate / COIN, nOutputs, txBuilder.ToString()); + return false; + } + } - nOutputsTotal += nOutputs; - if (nValueLeft == 0 || nBalanceToDenominate <= 0) break; + if (txBuilder.GetAmountLeft() == 0 || nBalanceToDenominate <= 0) break; } bool finished = true; for (const auto it : mapDenomCount) { // Check if this specific denom could use another loop, check that there aren't nPrivateSendDenomsGoal of this // denom and that our nValueLeft/nBalanceToDenominate is enough to create one of these denoms, if so, loop again. - if (it.second < CPrivateSendClientOptions::GetDenomsGoal() && (nValueLeft >= it.first + nOutputFee) && nBalanceToDenominate > 0) { + if (it.second < CPrivateSendClientOptions::GetDenomsGoal() && txBuilder.CouldAddOutput(it.first) && nBalanceToDenominate > 0) { finished = false; - LogPrint(BCLog::PRIVATESEND, /* Continued */ - "CPrivateSendClientSession::CreateDenominated -- 1 - NOT finished - nDenomValue: %f, count: %d, nValueLeft: %f, nBalanceToDenominate: %f\n", - (float) it.first / COIN, it.second, (float) nValueLeft / COIN, (float) nBalanceToDenominate / COIN); + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- 1 - NOT finished - nDenomValue: %f, count: %d, nBalanceToDenominate: %f, %s\n", + __func__, (float) it.first / COIN, it.second, (float) nBalanceToDenominate / COIN, txBuilder.ToString()); break; } - LogPrint(BCLog::PRIVATESEND, /* Continued */ - "CPrivateSendClientSession::CreateDenominated -- 1 - FINSHED - nDenomValue: %f, count: %d, nValueLeft: %f, nBalanceToDenominate: %f\n", - (float) it.first / COIN, it.second, (float) nValueLeft / COIN, (float) nBalanceToDenominate / COIN); + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- 1 - FINSHED - nDenomValue: %f, count: %d, nBalanceToDenominate: %f, %s\n", + __func__, (float) it.first / COIN, it.second, (float) nBalanceToDenominate / COIN, txBuilder.ToString()); } if (finished) break; } // Now that nPrivateSendDenomsGoal worth of each denom have been created or the max number of denoms given the value of the input, do something with the remainder. - if ((nValueLeft >= CPrivateSend::GetSmallestDenomination() + nOutputFee) && nBalanceToDenominate >= CPrivateSend::GetSmallestDenomination() - && nOutputsTotal < PRIVATESEND_DENOM_OUTPUTS_THRESHOLD) { - + if (txBuilder.CouldAddOutput(CPrivateSend::GetSmallestDenomination()) && nBalanceToDenominate >= CPrivateSend::GetSmallestDenomination() && txBuilder.CountOutputs() < PRIVATESEND_DENOM_OUTPUTS_THRESHOLD) { CAmount nLargestDenomValue = vecStandardDenoms.front(); + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- 2 - Process remainder: %s\n", __func__, txBuilder.ToString()); + + auto countPossibleOutputs = [&](CAmount nAmount) -> int { + std::vector vecOutputs; + while (true) { + // Create an potential output + vecOutputs.push_back(nAmount); + if (!txBuilder.CouldAddOutputs(vecOutputs) || txBuilder.CountOutputs() + vecOutputs.size() > PRIVATESEND_DENOM_OUTPUTS_THRESHOLD) { + // If its not possible to add it due to insufficient amount left or total number of outputs exceeds + // PRIVATESEND_DENOM_OUTPUTS_THRESHOLD drop the output again and stop trying. + vecOutputs.pop_back(); + break; + } + } + return static_cast(vecOutputs.size()); + }; + // Go big to small for (auto nDenomValue : vecStandardDenoms) { int nOutputs = 0; // Number of denoms we can create given our denom and the amount of funds we have left - int denomsToCreateValue = nValueLeft / (nDenomValue + nOutputFee); + int denomsToCreateValue = countPossibleOutputs(nDenomValue); // Prefer overshooting the targed balance by larger denoms (hence `+1`) instead of a more // accurate approximation by many smaller denoms. This is ok because when we get here we // should have nPrivateSendDenomsGoal of each smaller denom already. Also, without `+1` @@ -1685,78 +1677,51 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate, int denomsToCreateBal = (nBalanceToDenominate / nDenomValue) + 1; // Use the smaller value int denomsToCreate = denomsToCreateValue > denomsToCreateBal ? denomsToCreateBal : denomsToCreateValue; + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- 2 - nBalanceToDenominate: %f, nDenomValue: %f, denomsToCreateValue: %d, denomsToCreateBal: %d\n", + __func__, (float) nBalanceToDenominate / COIN, (float) nDenomValue / COIN, denomsToCreateValue, denomsToCreateBal); auto it = mapDenomCount.find(nDenomValue); for (int i = 0; i < denomsToCreate; i++) { // Never go above the cap unless it's the largest denom if (nDenomValue != nLargestDenomValue && it->second >= CPrivateSendClientOptions::GetDenomsHardCap()) break; - CScript scriptDenom = keyHolderStorageDenom.AddKey(mixingWallet); - vecSend.push_back((CRecipient) {scriptDenom, nDenomValue, false}); - - // increment outputs and subtract denomination amount - nOutputs++; - it->second++; - nValueLeft -= nDenomValue + nOutputFee; - nBalanceToDenominate -= nDenomValue; - LogPrint(BCLog::PRIVATESEND, /* Continued */ - "CPrivateSendClientSession::CreateDenominated -- 2 - nDenomValue: %f, totalOutputs: %d, nOutputsTotal: %d, nOutputs: %d, nValueLeft: %f, nBalanceToDenominate: %f\n", - (float) nDenomValue / COIN, nOutputsTotal + nOutputs, nOutputsTotal, nOutputs, (float) nValueLeft / COIN, (float) nBalanceToDenominate / COIN); - if (nOutputs + nOutputsTotal >= PRIVATESEND_DENOM_OUTPUTS_THRESHOLD) break; + // Increment helpers, add output and subtract denomination amount + if (txBuilder.AddOutput(nDenomValue)) { + nOutputs++; + it->second++; + nBalanceToDenominate -= nDenomValue; + } else { + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- 2 - Error: AddOutput failed at %d/%d, %s\n", __func__, i + 1, denomsToCreate, txBuilder.ToString()); + break; + } + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- 2 - nDenomValue: %f, nBalanceToDenominate: %f, nOutputs: %d, %s\n", + __func__, (float) nDenomValue / COIN, (float) nBalanceToDenominate / COIN, nOutputs, txBuilder.ToString()); + if (txBuilder.CountOutputs() >= PRIVATESEND_DENOM_OUTPUTS_THRESHOLD) break; } - nOutputsTotal += nOutputs; - if (nOutputsTotal >= PRIVATESEND_DENOM_OUTPUTS_THRESHOLD) break; + if (txBuilder.CountOutputs() >= PRIVATESEND_DENOM_OUTPUTS_THRESHOLD) break; } } - LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::CreateDenominated -- 3 - nOutputsTotal: %d, nValueLeft: %f, nBalanceToDenominate: %f\n", - nOutputsTotal, (float)nValueLeft / COIN, (float)nBalanceToDenominate / COIN); + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- 3 - nBalanceToDenominate: %f, %s\n", __func__, (float) nBalanceToDenominate / COIN, txBuilder.ToString()); for (const auto it : mapDenomCount) { - LogPrint(BCLog::PRIVATESEND, /* Continued */ - "CPrivateSendClientSession::CreateDenominated -- 3 - DONE - nDenomValue: %f, count: %d\n", - (float) it.first / COIN, it.second); + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- 3 - DONE - nDenomValue: %f, count: %d\n", __func__, (float) it.first / COIN, it.second); } // No reasons to create mixing collaterals if we can't create denoms to mix - if (nOutputsTotal == 0) return false; - - // if we have anything left over, it will be automatically send back as change - there is no need to send it manually - - coinControl.fAllowOtherInputs = false; - coinControl.fAllowWatchOnly = false; - coinControl.nCoinType = CoinType::ONLY_NONDENOMINATED; - // send change to the same address so that we were able create more denoms out of it later - coinControl.destChange = tallyItem.txdest; - for (const auto& outpoint : tallyItem.vecOutPoints) { - coinControl.Select(outpoint); - } - - CWalletTx wtx; - CAmount nFeeRet = 0; - int nChangePosRet = -1; - std::string strFail; - // make our change address - CReserveKey reservekeyChange(mixingWallet); - - bool fSuccess = mixingWallet->CreateTransaction(vecSend, wtx, reservekeyChange, - nFeeRet, nChangePosRet, strFail, coinControl); - if (!fSuccess) { - LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::CreateDenominated -- Error: %s\n", strFail); - keyHolderStorageDenom.ReturnAll(); + if ((fCreateMixingCollaterals && txBuilder.CountOutputs() == 1) || txBuilder.CountOutputs() == 0) { return false; } - keyHolderStorageDenom.KeepAll(); - - CValidationState state; - if (!mixingWallet->CommitTransaction(wtx, reservekeyChange, &connman, state)) { - LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::CreateDenominated -- CommitTransaction failed! Reason given: %s\n", state.GetRejectReason()); + std::string strResult; + if (!txBuilder.Commit(strResult)) { + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- Commit failed: %s\n", __func__, strResult); return false; } // use the same nCachedLastSuccessBlock as for DS mixing to prevent race privateSendClientManagers.at(mixingWallet->GetName())->UpdatedSuccessBlock(); - LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::CreateDenominated -- txid=%s\n", wtx.GetHash().GetHex()); + + LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::%s -- txid: %s\n", __func__, strResult); return true; } diff --git a/src/privatesend/privatesend-client.h b/src/privatesend/privatesend-client.h index 3dc7a9b65d75..4268f2d42f0a 100644 --- a/src/privatesend/privatesend-client.h +++ b/src/privatesend/privatesend-client.h @@ -120,12 +120,12 @@ class CPrivateSendClientSession : public CPrivateSendBaseSession CWallet* mixingWallet; /// Create denominations - bool CreateDenominated(CAmount nBalanceToDenominate, CConnman& connman); - bool CreateDenominated(CAmount nBalanceToDenominate, const CompactTallyItem& tallyItem, bool fCreateMixingCollaterals, CConnman& connman); + bool CreateDenominated(CAmount nBalanceToDenominate); + bool CreateDenominated(CAmount nBalanceToDenominate, const CompactTallyItem& tallyItem, bool fCreateMixingCollaterals); /// Split up large inputs or make fee sized inputs - bool MakeCollateralAmounts(CConnman& connman); - bool MakeCollateralAmounts(const CompactTallyItem& tallyItem, bool fTryDenominated, CConnman& connman); + bool MakeCollateralAmounts(); + bool MakeCollateralAmounts(const CompactTallyItem& tallyItem, bool fTryDenominated); bool JoinExistingQueue(CAmount nBalanceNeedsAnonymized, CConnman& connman); bool StartNewQueue(CAmount nBalanceNeedsAnonymized, CConnman& connman); diff --git a/src/privatesend/privatesend-util.cpp b/src/privatesend/privatesend-util.cpp index 161fd7b4a490..fbfe55ac4d3e 100644 --- a/src/privatesend/privatesend-util.cpp +++ b/src/privatesend/privatesend-util.cpp @@ -2,7 +2,19 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include +#include +#include #include +#include