Implement significantly improved createdenominations algorithm (#3479)

* Allow the creation of denoms on regtest.
Ignore the fact there are no MNs on regtest

Signed-off-by: pasta <pasta@dashboost.org>

* implement significantly improved createdenominations algorithm

Signed-off-by: pasta <pasta@dashboost.org>

* change int denomsToCreate

Signed-off-by: pasta <pasta@dashboost.org>

* Remove debugging code and use a const iterator instead of calling find multiple times

Signed-off-by: pasta <pasta@dashboost.org>

* vecDenominationsSkipped and *DenomSkipped are nolonger used

Signed-off-by: Pasta <pasta@dashboost.org>
Signed-off-by: pasta <pasta@dashboost.org>

* inputs -> outputs

Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>

Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
This commit is contained in:
PastaPastaPasta 2020-05-15 09:34:12 +00:00 committed by GitHub
parent fe208c98e3
commit 926087aac6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 104 additions and 72 deletions

View File

@ -661,21 +661,6 @@ void CPrivateSendClientManager::UpdatedSuccessBlock()
nCachedLastSuccessBlock = nCachedBlockHeight;
}
bool CPrivateSendClientManager::IsDenomSkipped(const CAmount& nDenomValue)
{
return std::find(vecDenominationsSkipped.begin(), vecDenominationsSkipped.end(), nDenomValue) != vecDenominationsSkipped.end();
}
void CPrivateSendClientManager::AddSkippedDenom(const CAmount& nDenomValue)
{
vecDenominationsSkipped.push_back(nDenomValue);
}
void CPrivateSendClientManager::RemoveSkippedDenom(const CAmount& nDenomValue)
{
vecDenominationsSkipped.erase(std::remove(vecDenominationsSkipped.begin(), vecDenominationsSkipped.end(), nDenomValue), vecDenominationsSkipped.end());
}
bool CPrivateSendClientManager::WaitForAnotherBlock()
{
if (!masternodeSync.IsBlockchainSynced()) return true;
@ -789,7 +774,8 @@ bool CPrivateSendClientSession::DoAutomaticDenominating(CConnman& connman, bool
return false;
}
if (deterministicMNManager->GetListAtChainTip().GetValidMNsCount() == 0) {
if (deterministicMNManager->GetListAtChainTip().GetValidMNsCount() == 0 &&
Params().NetworkIDString() != CBaseChainParams::REGTEST) {
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::DoAutomaticDenominating -- No Masternodes detected\n");
strAutoDenomResult = _("No Masternodes detected.");
return false;
@ -1537,55 +1523,109 @@ bool CPrivateSendClientSession::CreateDenominated(CAmount nBalanceToDenominate,
bool fAddFinal = true;
std::vector<CAmount> vecStandardDenoms = CPrivateSend::GetStandardDenominations();
for (auto it = vecStandardDenoms.rbegin(); it != vecStandardDenoms.rend(); ++it) {
CAmount nDenomValue = *it;
// Note: denoms are skipped if there are already nPrivateSendDenoms of them
// and there are still larger denoms which can be used for mixing
// check skipped denoms
if (privateSendClient.IsDenomSkipped(nDenomValue)) {
strAutoDenomResult = strprintf(_("Too many %f denominations, skipping."), (float)nDenomValue / COIN);
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::CreateDenominated -- %s\n", strAutoDenomResult);
continue;
}
// find new denoms to skip if any (ignore the largest one)
if (nDenomValue != vecStandardDenoms.front() && GetWallets()[0]->CountInputsWithAmount(nDenomValue) > privateSendClient.nPrivateSendDenoms) {
strAutoDenomResult = strprintf(_("Too many %f denominations, removing."), (float)nDenomValue / COIN);
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::CreateDenominated -- %s\n", strAutoDenomResult);
privateSendClient.AddSkippedDenom(nDenomValue);
continue;
}
int nOutputs = 0;
auto needMoreOutputs = [&]() {
bool fRegular = (nValueLeft >= nDenomValue && nBalanceToDenominate >= nDenomValue);
bool fFinal = (fAddFinal
&& nValueLeft >= nDenomValue
&& nBalanceToDenominate > 0
&& nBalanceToDenominate < nDenomValue);
fAddFinal = false; // add final denom only once, only the smalest possible one
return fRegular || fFinal;
};
// add each output up to 11 times until it can't be added again
while (needMoreOutputs() && nOutputs <= 10) {
CScript scriptDenom = keyHolderStorageDenom.AddKey(GetWallets()[0]);
vecSend.push_back((CRecipient){scriptDenom, nDenomValue, false});
//increment outputs and subtract denomination amount
nOutputs++;
nValueLeft -= nDenomValue;
nBalanceToDenominate -= nDenomValue;
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::CreateDenominated -- 1 - totalOutputs: %d, nOutputsTotal: %d, nOutputs: %d, nValueLeft: %f\n", nOutputsTotal + nOutputs, nOutputsTotal, nOutputs, (float)nValueLeft / COIN);
}
nOutputsTotal += nOutputs;
if (nValueLeft == 0 || nBalanceToDenominate <= 0) break;
std::map<CAmount, int> mapDenomCount;
for (auto nDenomValue : vecStandardDenoms) {
mapDenomCount.insert(std::pair<CAmount, int>(nDenomValue, GetWallets()[0]->CountInputsWithAmount(nDenomValue)));
}
// NOTE: We do not allow txes larger than 100kB, so we have to limit the number of outputs here.
// We still want to create a lot of outputs though.
// Knowing that each CTxOut is ~35b big, 400 outputs should take 400 x ~35b = ~17.5kb.
// More than 500 outputs starts to make qt quite laggy.
// Additionally to need all 500 outputs (assuming a max per denom of 50) you'd need to be trying to
// create denominations for over 3000 dash!
// Will generate outputs for the createdenoms up to privatesendmaxdenoms per denom
// This works in the way creating PS denoms has traditionally worked, assuming enough funds,
// it will start with the smallest denom then create 11 of those, then go up to the next biggest denom create 11
// and repeat. Previously, once the largest denom was reached, as many would be created were created as possible and
// then any remaining was put into a change address and denominations were created in the same manner a block later.
// Now, in this system, so long as we don't reach 500 outputs the process repeats in the same transaction,
// creating up to privatesenddenoms per denomination in a single transaction.
while (nValueLeft >= CPrivateSend::GetSmallestDenomination() && nOutputsTotal <= 500) {
for (auto it = vecStandardDenoms.rbegin(); it != vecStandardDenoms.rend(); ++it) {
CAmount nDenomValue = *it;
auto currentDenomIt = mapDenomCount.find(nDenomValue);
int nOutputs = 0;
auto needMoreOutputs = [&]() {
bool fRegular = (nValueLeft >= nDenomValue && nBalanceToDenominate >= nDenomValue);
bool fFinal = (fAddFinal
&& nValueLeft >= nDenomValue
&& nBalanceToDenominate > 0
&& nBalanceToDenominate < nDenomValue);
fAddFinal = false; // add final denom only once, only the smalest possible one
return fRegular || fFinal;
};
// add each output up to 11 times or until it can't be added again or until we reach nPrivateSendDenoms
while (needMoreOutputs() && nOutputs <= 10 && currentDenomIt->second <= privateSendClient.nPrivateSendDenoms) {
CScript scriptDenom = keyHolderStorageDenom.AddKey(GetWallets()[0]);
vecSend.push_back((CRecipient) {scriptDenom, nDenomValue, false});
// increment outputs and subtract denomination amount
nOutputs++;
currentDenomIt->second++;
nValueLeft -= nDenomValue;
nBalanceToDenominate -= nDenomValue;
LogPrint(BCLog::PRIVATESEND,
"CPrivateSendClientSession::CreateDenominated -- 1 - totalOutputs: %d, nOutputsTotal: %d, nOutputs: %d, nValueLeft: %f\n",
nOutputsTotal + nOutputs, nOutputsTotal, nOutputs, (float) nValueLeft / COIN);
}
nOutputsTotal += nOutputs;
if (nValueLeft == 0 || nBalanceToDenominate <= 0) break;
}
bool finished = true;
for (auto it : mapDenomCount) {
// Check if this specific denom could use another loop, check that there aren't nPrivateSendDenoms of this
// denom and that our nValueLeft/nBalanceToDenominate is enough to create one of these denoms, if so, loop again.
if (it.second <= privateSendClient.nPrivateSendDenoms && nValueLeft >= it.first && nBalanceToDenominate >= it.first) {
finished = false;
break;
}
}
if (finished) break;
}
// Now that nPrivateSendDenoms worth of each denom have been created, do something with the remainder.
while (nValueLeft >= CPrivateSend::GetSmallestDenomination() && nBalanceToDenominate >= CPrivateSend::GetSmallestDenomination()
&& nOutputsTotal <= 500) {
// Go big to small
for (long 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;
int denomsToCreateBal = nBalanceToDenominate / nDenomValue;
// Use the smaller value
int denomsToCreate = denomsToCreateValue > denomsToCreateBal ? denomsToCreateBal : denomsToCreateValue;
for (int i = 0; i < denomsToCreate; i++) {
CScript scriptDenom = keyHolderStorageDenom.AddKey(GetWallets()[0]);
vecSend.push_back((CRecipient) {scriptDenom, nDenomValue, false});
// increment outputs and subtract denomination amount
nOutputs++;
mapDenomCount.find(nDenomValue)->second++;
nValueLeft -= nDenomValue;
nBalanceToDenominate -= nDenomValue;
LogPrint(BCLog::PRIVATESEND,
"CPrivateSendClientSession::CreateDenominated -- 1 - totalOutputs: %d, nOutputsTotal: %d, nOutputs: %d, nValueLeft: %f\n",
nOutputsTotal + nOutputs, nOutputsTotal, nOutputs, (float) nValueLeft / COIN);
}
nOutputsTotal += nOutputs;
}
}
LogPrint(BCLog::PRIVATESEND, "CPrivateSendClientSession::CreateDenominated -- 2 - nOutputsTotal: %d, nValueLeft: %f\n", nOutputsTotal, (float)nValueLeft / COIN);
// No reasons to create mixing collaterals if we can't create denoms to mix

View File

@ -172,8 +172,6 @@ private:
// Keep track of the used Masternodes
std::vector<COutPoint> vecMasternodesUsed;
std::vector<CAmount> vecDenominationsSkipped;
// TODO: or map<denom, CPrivateSendClientSession> ??
std::deque<CPrivateSendClientSession> deqSessions;
mutable CCriticalSection cs_deqsessions;
@ -204,7 +202,6 @@ public:
CPrivateSendClientManager() :
vecMasternodesUsed(),
vecDenominationsSkipped(),
deqSessions(),
nCachedLastSuccessBlock(0),
nMinBlocksToWait(1),
@ -223,10 +220,6 @@ public:
void ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman);
bool IsDenomSkipped(const CAmount& nDenomValue);
void AddSkippedDenom(const CAmount& nDenomValue);
void RemoveSkippedDenom(const CAmount& nDenomValue);
void ResetPool();
std::string GetStatuses();

View File

@ -4091,7 +4091,6 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon
coin.BindWallet(this);
NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED);
updated_hahes.insert(txin.prevout.hash);
privateSendClient.RemoveSkippedDenom(coin.tx->vout[txin.prevout.n].nValue);
}
}