Implement safer version of CWallet::PrepareDarksendDenominate:

Make outputs by looping through denominations: try to add every needed denomination, repeat up to 5-10 times.
This way we can be pretty sure that it should have at least one of each needed denomination.
We also do not care about full amount as long as we have right denominations, just pass what we found.
This commit is contained in:
UdjinM6 2015-07-07 01:09:28 +03:00
parent 6c6a280df8
commit 05d93445a2

View File

@ -2318,6 +2318,7 @@ string CWallet::PrepareDarksendDenominate(int minRounds, int maxRounds)
// ** find the coins we'll use
std::vector<CTxIn> vCoins;
std::vector<CTxIn> vCoinsResult;
std::vector<COutput> vCoins2;
int64_t nValueIn = 0;
CReserveKey reservekey(this);
@ -2329,129 +2330,95 @@ string CWallet::PrepareDarksendDenominate(int minRounds, int maxRounds)
*/
if(minRounds >= 0){
if (!SelectCoinsByDenominations(darkSendPool.sessionDenom, 0.1*COIN, DARKSEND_POOL_MAX, vCoins, vCoins2, nValueIn, minRounds, maxRounds))
return _("Insufficient funds");
return _("Error: can't select current denominated inputs");
}
// calculate total value out
int64_t nTotalValue = GetTotalValue(vCoins);
LogPrintf("PrepareDarksendDenominate - preparing darksend denominate . Got: %d \n", nTotalValue);
LogPrintf("PrepareDarksendDenominate - preparing darksend denominate . Got: %d \n", nValueIn);
BOOST_FOREACH(CTxIn v, vCoins)
LockCoin(v.prevout);
// denominate our funds
int64_t nValueLeft = nTotalValue;
int64_t nValueLeft = nValueIn;
std::vector<CTxOut> vOut;
std::vector<int64_t> vDenoms;
/*
TODO: Front load with needed denominations (e.g. .1, 1 )
*/
/*
Add all denominations once
// Make outputs by looping through denominations: try to add every needed denomination, repeat up to 5-10 times.
// This way we can be pretty sure that it should have at least one of each needed denomination.
// NOTE: No need to randomize order of inputs because they were
// initially shuffled in CWallet::SelectCoinsByDenominations already.
int nStep = 0;
int nStepsMax = 5 + GetRandInt(5);
while(nStep < nStepsMax) {
The beginning of the list is front loaded with each possible
denomination in random order. This means we'll at least get 1
of each that is required as outputs.
*/
BOOST_FOREACH(int64_t d, darkSendDenominations){
vDenoms.push_back(d);
vDenoms.push_back(d);
}
BOOST_FOREACH(int64_t v, darkSendDenominations){
// only use the ones that are approved
bool fAccepted = false;
if((darkSendPool.sessionDenom & (1 << 0)) && v == ((100*COIN) +100000)) {fAccepted = true;}
else if((darkSendPool.sessionDenom & (1 << 1)) && v == ((10*COIN) +10000)) {fAccepted = true;}
else if((darkSendPool.sessionDenom & (1 << 2)) && v == ((1*COIN) +1000)) {fAccepted = true;}
else if((darkSendPool.sessionDenom & (1 << 3)) && v == ((.1*COIN) +100)) {fAccepted = true;}
if(!fAccepted) continue;
//randomize the order of these denominations
std::random_shuffle (vDenoms.begin(), vDenoms.end());
// try to add it
if(nValueLeft - v >= 0) {
// Note: this relies on a fact that both vectors MUST have same size
std::vector<CTxIn>::iterator it = vCoins.begin();
std::vector<COutput>::iterator it2 = vCoins2.begin();
while(it2 != vCoins2.end()) {
// we have matching inputs
if((*it2).tx->vout[(*it2).i].nValue == v) {
// add new input in resulting vector
vCoinsResult.push_back(*it);
// remove corresponting items from initial vectors
vCoins.erase(it);
vCoins2.erase(it2);
/*
Build a long list of denominations
CScript scriptChange;
CPubKey vchPubKey;
// use a unique change address
assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked
scriptChange = GetScriptForDestination(vchPubKey.GetID());
reservekey.KeepKey();
Next we'll build a long random list of denominations to add.
Eventually as the algorithm goes through these it'll find the ones
it nees to get exact change.
*/
for(int i = 0; i <= 500; i++)
BOOST_FOREACH(int64_t d, darkSendDenominations)
vDenoms.push_back(d);
// add new output
CTxOut o(v, scriptChange);
vOut.push_back(o);
//randomize the order of inputs we get back
std::random_shuffle (vDenoms.begin() + (int)darkSendDenominations.size() + 1, vDenoms.end());
// subtract denomination amount
nValueLeft -= v;
// Make outputs by looping through denominations randomly
BOOST_REVERSE_FOREACH(int64_t v, vDenoms){
//only use the ones that are approved
bool fAccepted = false;
if((darkSendPool.sessionDenom & (1 << 0)) && v == ((100*COIN) +100000)) {fAccepted = true;}
else if((darkSendPool.sessionDenom & (1 << 1)) && v == ((10*COIN) +10000)) {fAccepted = true;}
else if((darkSendPool.sessionDenom & (1 << 2)) && v == ((1*COIN) +1000)) {fAccepted = true;}
else if((darkSendPool.sessionDenom & (1 << 3)) && v == ((.1*COIN) +100)) {fAccepted = true;}
if(!fAccepted) continue;
int nOutputs = 0;
// add each output up to 10 times until it can't be added again
while(nValueLeft - v >= 0 && nOutputs <= 10) {
CScript scriptChange;
CPubKey vchPubKey;
//use a unique change address
assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked
scriptChange = GetScriptForDestination(vchPubKey.GetID());
reservekey.KeepKey();
CTxOut o(v, scriptChange);
vOut.push_back(o);
//increment outputs and subtract denomination amount
nOutputs++;
nValueLeft -= v;
break;
}
++it;
++it2;
}
}
}
nStep++;
if(nValueLeft == 0) break;
}
//back up mode , incase we couldn't successfully make the outputs for some reason
if(vOut.size() > 40 || darkSendPool.GetDenominations(vOut) != darkSendPool.sessionDenom || nValueLeft != 0){
vOut.clear();
nValueLeft = nTotalValue;
// Make outputs by looping through denominations, from small to large
BOOST_FOREACH(const COutput& out, vCoins2){
CScript scriptChange;
CPubKey vchPubKey;
//use a unique change address
assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked
scriptChange = GetScriptForDestination(vchPubKey.GetID());
reservekey.KeepKey();
CTxOut o(out.tx->vout[out.i].nValue, scriptChange);
vOut.push_back(o);
//increment outputs and subtract denomination amount
nValueLeft -= out.tx->vout[out.i].nValue;
if(nValueLeft == 0) break;
}
}
// unlock unused coins
BOOST_FOREACH(CTxIn v, vCoins)
UnlockCoin(v.prevout);
if(darkSendPool.GetDenominations(vOut) != darkSendPool.sessionDenom) {
BOOST_FOREACH(CTxIn v, vCoins)
// unlock used coins on failure
BOOST_FOREACH(CTxIn v, vCoinsResult)
UnlockCoin(v.prevout);
return "Error: can't make current denominated outputs";
}
// we don't support change at all
if(nValueLeft != 0) {
BOOST_FOREACH(CTxIn v, vCoins)
UnlockCoin(v.prevout);
return "Error: change left-over in pool. Must use denominations only";
}
//randomize the output order
// randomize the output order
std::random_shuffle (vOut.begin(), vOut.end());
darkSendPool.SendDarksendDenominate(vCoins, vOut, nValueIn);
// We also do not care about full amount as long as we have right denominations, just pass what we found
darkSendPool.SendDarksendDenominate(vCoinsResult, vOut, nValueIn - nValueLeft);
return "";
}