Implemented DarkSend convertability

Darksend defaults to a new mode which enables inputs/outputs
of each session to be different. For example 10DRK can be input
and 1DRKx10 can be output. This strengthens the anonymity of
Darksend greatly, which also increasing the usability (Users who
run out of .1DRK denominations can simply turn on Darksend to
split up larger inputs).
This commit is contained in:
Evan Duffield 2015-01-21 06:02:59 -07:00
parent bbd869589c
commit 78fe35f06d
3 changed files with 127 additions and 35 deletions

View File

@ -793,7 +793,7 @@ void CDarkSendPool::ChargeRandomFees(){
// Check for various timeouts (queue objects, darksend, etc)
//
void CDarkSendPool::CheckTimeout(){
if(!fEnableDarksend) return;
if(!fEnableDarksend && !fMasterNode) return;
// catching hanging sessions
if(!fMasterNode) {
@ -2032,10 +2032,10 @@ int CDarkSendPool::GetDenominationsByAmount(int64_t nAmount, int nDenomTarget){
BOOST_REVERSE_FOREACH(int64_t v, darkSendDenominations){
if(nDenomTarget != 0){
bool fAccepted = false;
if((nDenomTarget & (1 << 0)) && v == ((100*COIN)+1)) {fAccepted = true;}
else if((nDenomTarget & (1 << 1)) && v == ((10*COIN)+1)) {fAccepted = true;}
else if((nDenomTarget & (1 << 2)) && v == ((1*COIN)+1)) {fAccepted = true;}
else if((nDenomTarget & (1 << 3)) && v == ((.1*COIN)+1)) {fAccepted = true;}
if((nDenomTarget & (1 << 0)) && v == ((100*COIN)+100000)) {fAccepted = true;}
else if((nDenomTarget & (1 << 1)) && v == ((10*COIN) +10000)) {fAccepted = true;}
else if((nDenomTarget & (1 << 2)) && v == ((1*COIN) +1000)) {fAccepted = true;}
else if((nDenomTarget & (1 << 3)) && v == ((.1*COIN) +100)) {fAccepted = true;}
if(!fAccepted) continue;
}

View File

@ -1175,10 +1175,23 @@ bool AppInit2(boost::thread_group& threadGroup)
LogPrintf("Darksend rounds %d\n", nDarksendRounds);
LogPrintf("Anonymize Darkcoin Amount %d\n", nAnonymizeDarkcoinAmount);
darkSendDenominations.push_back( (100 * COIN)+1 );
darkSendDenominations.push_back( (10 * COIN)+1 );
darkSendDenominations.push_back( (1 * COIN)+1 );
darkSendDenominations.push_back( (.1 * COIN)+1 );
/* Denominations
A note about convertability. Within Darksend pools, each denomination
is convertable to another.
For example:
1DRK+1000 == (.1DRK+100)*10
10DRK+10000 == (1DRK+1000)*10
*/
darkSendDenominations.push_back( (100 * COIN)+100000 );
darkSendDenominations.push_back( (10 * COIN)+10000 );
darkSendDenominations.push_back( (1 * COIN)+1000 );
darkSendDenominations.push_back( (.1 * COIN)+100 );
/* Disabled till we need them
darkSendDenominations.push_back( (.01 * COIN)+10 );
darkSendDenominations.push_back( (.001 * COIN)+1 );
*/
darkSendPool.InitCollateralAddress();

View File

@ -1468,9 +1468,9 @@ bool CWallet::SelectCoins(int64_t nTargetValue, set<pair<const CWalletTx*,unsign
int added = 0;
BOOST_FOREACH(const COutput& out, vCoins)
{
if(out.tx->vout[out.i].nValue == v //make sure it's the denom we're looking for
&& nValueRet + out.tx->vout[out.i].nValue < nTargetValue + (0.1*COIN)+1 //round the amount up to .1DRK over
&& added <= 50){ //don't add more than 50 of one denom type
if(out.tx->vout[out.i].nValue == v //make sure it's the denom we're looking for
&& nValueRet + out.tx->vout[out.i].nValue < nTargetValue + (0.1*COIN)+100 //round the amount up to .1DRK over
&& added <= 50){ //don't add more than 50 of one denom type
nValueRet += out.tx->vout[out.i].nValue;
setCoinsRet.insert(make_pair(out.tx, out.i));
added++;
@ -1534,7 +1534,7 @@ bool CWallet::SelectCoinsByDenominations(int nDenom, int64_t nValueMin, int64_t
BOOST_FOREACH(const COutput& out, vCoins)
{
//there's no reason to allow inputs less than 1 COIN into DS (other than denominations smaller than that amount)
if(out.tx->vout[out.i].nValue < 1*COIN && out.tx->vout[out.i].nValue != (.1*COIN)+1) continue;
if(out.tx->vout[out.i].nValue < 1*COIN && out.tx->vout[out.i].nValue != (.1*COIN)+100) continue;
if(fMasterNode && out.tx->vout[out.i].nValue == 1000*COIN) continue; //masternode input
if(nValueRet + out.tx->vout[out.i].nValue <= nValueMax){
bool fAccepted = false;
@ -1562,16 +1562,16 @@ bool CWallet::SelectCoinsByDenominations(int nDenom, int64_t nValueMin, int64_t
if((int)setCoinsRet.size() > r) return true;
}
//Denomination criterion has been met, we can take any matching denominations
if((nDenom & (1 << 0)) && out.tx->vout[out.i].nValue == ((100*COIN)+1)) {fAccepted = true;}
else if((nDenom & (1 << 1)) && out.tx->vout[out.i].nValue == ((10*COIN)+1)) {fAccepted = true;}
else if((nDenom & (1 << 2)) && out.tx->vout[out.i].nValue == ((1*COIN)+1)) {fAccepted = true;}
else if((nDenom & (1 << 3)) && out.tx->vout[out.i].nValue == ((.1*COIN)+1)) {fAccepted = true;}
if((nDenom & (1 << 0)) && out.tx->vout[out.i].nValue == ((100*COIN) +100000)) {fAccepted = true;}
else if((nDenom & (1 << 1)) && out.tx->vout[out.i].nValue == ((10*COIN)+10000)) {fAccepted = true;}
else if((nDenom & (1 << 2)) && out.tx->vout[out.i].nValue == ((1*COIN) +1000)) {fAccepted = true;}
else if((nDenom & (1 << 3)) && out.tx->vout[out.i].nValue == ((.1*COIN)+100)) {fAccepted = true;}
} else {
//Criterion has not been satisfied, we will only take 1 of each until it is.
if((nDenom & (1 << 0)) && out.tx->vout[out.i].nValue == ((100*COIN)+1)) {fAccepted = true; fFound100 = true;}
else if((nDenom & (1 << 1)) && out.tx->vout[out.i].nValue == ((10*COIN)+1)) {fAccepted = true; fFound10 = true;}
else if((nDenom & (1 << 2)) && out.tx->vout[out.i].nValue == ((1*COIN)+1)) {fAccepted = true; fFound1 = true;}
else if((nDenom & (1 << 3)) && out.tx->vout[out.i].nValue == ((.1*COIN)+1)) {fAccepted = true; fFoundDot1 = true;}
if((nDenom & (1 << 0)) && out.tx->vout[out.i].nValue == ((100*COIN) +100000)) {fAccepted = true; fFound100 = true;}
else if((nDenom & (1 << 1)) && out.tx->vout[out.i].nValue == ((10*COIN)+10000)) {fAccepted = true; fFound10 = true;}
else if((nDenom & (1 << 2)) && out.tx->vout[out.i].nValue == ((1*COIN) +1000)) {fAccepted = true; fFound1 = true;}
else if((nDenom & (1 << 3)) && out.tx->vout[out.i].nValue == ((.1*COIN)+100)) {fAccepted = true; fFoundDot1 = true;}
}
if(!fAccepted) continue;
@ -1604,7 +1604,7 @@ bool CWallet::SelectCoinsDark(int64_t nValueMin, int64_t nValueMax, std::vector<
BOOST_FOREACH(const COutput& out, vCoins)
{
//there's no reason to allow inputs less than 1 COIN into DS (other than denominations smaller than that amount)
if(out.tx->vout[out.i].nValue < 1*COIN && out.tx->vout[out.i].nValue != (.1*COIN)+1) continue;
if(out.tx->vout[out.i].nValue < 1*COIN && out.tx->vout[out.i].nValue != (.1*COIN)+100) continue;
if(fMasterNode && out.tx->vout[out.i].nValue == 1000*COIN) continue; //masternode input
if(nValueRet + out.tx->vout[out.i].nValue <= nValueMax){
@ -2136,31 +2136,110 @@ string CWallet::PrepareDarksendDenominate(int minRounds, int maxRounds)
// denominate our funds
int64_t nValueLeft = nTotalValue;
std::vector<CTxOut> vOut;
std::vector<int64_t> vDenoms;
// Make outputs by looping through denominations, from small to large
/*
TODO: Front load with needed denominations (e.g. .1, 1 )
*/
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.SetDestination(vchPubKey.GetID());
reservekey.KeepKey();
/*
Add all denominations once
CTxOut o(out.tx->vout[out.i].nValue, scriptChange);
vOut.push_back(o);
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);
}
//increment outputs and subtract denomination amount
nValueLeft -= out.tx->vout[out.i].nValue;
//randomize the order of these denominations
std::random_shuffle (vDenoms.begin(), vDenoms.end());
/*
Build a long list of denominations
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);
//randomize the order of inputs we get back
std::random_shuffle (vDenoms.begin() + (int)darkSendDenominations.size() + 1, vDenoms.end());
// 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
if(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.SetDestination(vchPubKey.GetID());
reservekey.KeepKey();
CTxOut o(v, scriptChange);
vOut.push_back(o);
//increment outputs and subtract denomination amount
nOutputs++;
nValueLeft -= v;
}
if(nValueLeft == 0) break;
}
// if we have anything left over, send it back as change
//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.SetDestination(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;
}
}
if(darkSendPool.GetDenominations(vOut) != darkSendPool.sessionDenom)
return "Error: can't make current denominated outputs";
// we don't support change at all
if(nValueLeft != 0)
return "Error: change left-over in pool. Must use denominations only";
//randomize the output order
std::random_shuffle (vOut.begin(), vOut.end());
darkSendPool.SendDarksendDenominate(vCoins, vOut, nValueIn);
return "";