Added Darksend high precision matching engine
Darksend is now capable of taking queue objects (which show who wants to mix what) and looking at it's own inputs to see if it's at all possible to join their mixing session. This plus other improvements should make Darksend much faster for mixing coins.
This commit is contained in:
parent
762080140b
commit
b20581125d
@ -163,8 +163,7 @@ void ProcessMessageDarksend(CNode* pfrom, std::string& strCommand, CDataStream&
|
||||
}
|
||||
|
||||
if (fDebug) LogPrintf("darksend queue is ready - %s\n", addr.ToString().c_str());
|
||||
|
||||
darkSendPool.DoAutomaticDenominating(false, true);
|
||||
darkSendPool.PrepareDarksendDenominate();
|
||||
} else {
|
||||
BOOST_FOREACH(CDarksendQueue q, vecDarksendQueue){
|
||||
if(q.vin == dsq.vin) return;
|
||||
@ -1426,22 +1425,23 @@ bool CDarkSendPool::DoAutomaticDenominating(bool fDryRun, bool ready)
|
||||
return false;
|
||||
}
|
||||
|
||||
sessionMinRounds = -2; //non denominated funds are rounds of less than 0
|
||||
sessionMaxRounds = 2;
|
||||
|
||||
// ** find the coins we'll use
|
||||
std::vector<CTxIn> vCoins;
|
||||
int64_t nValueMin = 0.01*COIN;
|
||||
int64_t nValueMax = DARKSEND_POOL_MAX;
|
||||
int64_t nValueIn = 0;
|
||||
int minRounds = -2; //non denominated funds are rounds of less than 0
|
||||
int maxRounds = 2;
|
||||
int maxAmount = DARKSEND_POOL_MAX/COIN;
|
||||
bool hasFeeInput = false;
|
||||
int64_t lowestDenom = COIN*0.1;
|
||||
|
||||
// If we can find only denominated funds, switch to only-denom mode
|
||||
if (!pwalletMain->SelectCoinsDark(nValueMin, maxAmount*COIN, vCoins, nValueIn, -2, 2, hasFeeInput) &&
|
||||
pwalletMain->SelectCoinsDark(nValueMin, maxAmount*COIN, vCoins, nValueIn, 0, 8, hasFeeInput)) {
|
||||
minRounds = 0;
|
||||
maxRounds = nDarksendRounds;
|
||||
pwalletMain->SelectCoinsDark(nValueMin, maxAmount*COIN, vCoins, nValueIn, 0, nDarksendRounds, hasFeeInput)) {
|
||||
sessionMinRounds = 0;
|
||||
sessionMaxRounds = nDarksendRounds;
|
||||
}
|
||||
//if we're set to less than a thousand, don't submit for than that to the pool
|
||||
if(nAnonymizeDarkcoinAmount < DARKSEND_POOL_MAX/COIN) maxAmount = nAnonymizeDarkcoinAmount;
|
||||
@ -1461,13 +1461,13 @@ bool CDarkSendPool::DoAutomaticDenominating(bool fDryRun, bool ready)
|
||||
}
|
||||
|
||||
// select coins that should be given to the pool
|
||||
if (!pwalletMain->SelectCoinsDark(nValueMin, maxAmount*COIN, vCoins, nValueIn, minRounds, maxRounds, hasFeeInput))
|
||||
if (!pwalletMain->SelectCoinsDark(nValueMin, maxAmount*COIN, vCoins, nValueIn, sessionMinRounds, sessionMaxRounds, hasFeeInput))
|
||||
{
|
||||
nValueIn = 0;
|
||||
vCoins.clear();
|
||||
|
||||
// look for inputs larger than the max amount, if we find anything we need to split it up
|
||||
if (pwalletMain->SelectCoinsDark(maxAmount*COIN, 9999999*COIN, vCoins, nValueIn, minRounds, maxRounds, hasFeeInput))
|
||||
if (pwalletMain->SelectCoinsDark(maxAmount*COIN, 9999999*COIN, vCoins, nValueIn, sessionMinRounds, sessionMaxRounds, hasFeeInput))
|
||||
{
|
||||
if(!fDryRun) SplitUpMoney();
|
||||
return true;
|
||||
@ -1483,7 +1483,7 @@ bool CDarkSendPool::DoAutomaticDenominating(bool fDryRun, bool ready)
|
||||
(vecDisabledDenominations.size() > 0 && nValueIn < COIN*10)
|
||||
){
|
||||
//simply look for non-denominated coins
|
||||
if (pwalletMain->SelectCoinsDark(maxAmount*COIN, 9999999*COIN, vCoins, nValueIn, minRounds, maxRounds, hasFeeInput))
|
||||
if (pwalletMain->SelectCoinsDark(maxAmount*COIN, 9999999*COIN, vCoins, nValueIn, sessionMinRounds, sessionMaxRounds, hasFeeInput))
|
||||
{
|
||||
if(!fDryRun) SplitUpMoney();
|
||||
return true;
|
||||
@ -1517,38 +1517,6 @@ bool CDarkSendPool::DoAutomaticDenominating(bool fDryRun, bool ready)
|
||||
sessionTotalValue = pwalletMain->GetTotalValue(vCoins);
|
||||
|
||||
//randomize the amounts we mix
|
||||
// if we have minRounds set, or if our non-demon is less than 5% of denom coins
|
||||
if(minRounds == 0 ||
|
||||
pwalletMain->GetDenominatedBalance(true) * 0.05 > pwalletMain->GetDenominatedBalance(false)) {
|
||||
for(int a = 0; a < 10; a++){ //try 10 amounts and see if we match a queue
|
||||
int r = (rand()%(maxAmount-(nValueMin/COIN)))+(nValueMin/COIN);
|
||||
|
||||
vCoins.clear();
|
||||
nValueIn = 0;
|
||||
if (pwalletMain->SelectCoinsDark(nValueMin, r*COIN, vCoins, nValueIn, minRounds, maxRounds, hasFeeInput)){
|
||||
sessionTotalValue = pwalletMain->GetTotalValue(vCoins);
|
||||
|
||||
// if it's in the queue, take it
|
||||
if(nUseQueue > 33 && vecDarksendQueue.size() > 0){
|
||||
BOOST_FOREACH(CDarksendQueue& dsq, vecDarksendQueue){
|
||||
CService addr;
|
||||
if(dsq.time == 0) continue;
|
||||
if(!dsq.GetAddress(addr)) continue;
|
||||
|
||||
std::vector<int64_t> vecAmounts;
|
||||
pwalletMain->ConvertList(vCoins, vecAmounts);
|
||||
|
||||
if(dsq.nDenom == GetDenominationsByAmounts(vecAmounts)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if(sessionTotalValue > maxAmount*COIN) sessionTotalValue = maxAmount*COIN;
|
||||
|
||||
double fDarkcoinSubmitted = (sessionTotalValue / CENT);
|
||||
@ -1567,6 +1535,7 @@ bool CDarkSendPool::DoAutomaticDenominating(bool fDryRun, bool ready)
|
||||
BOOST_FOREACH(CDarksendQueue& dsq, vecDarksendQueue){
|
||||
CService addr;
|
||||
if(dsq.time == 0) continue;
|
||||
|
||||
if(!dsq.GetAddress(addr)) continue;
|
||||
if(dsq.IsExpired()) continue;
|
||||
|
||||
@ -1581,12 +1550,18 @@ bool CDarkSendPool::DoAutomaticDenominating(bool fDryRun, bool ready)
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't match the denominations, we don't want to submit our inputs
|
||||
if(dsq.nDenom != GetDenominationsByAmount(sessionTotalValue)) {
|
||||
if(fDebug) LogPrintf(" dsq.nDenom != GetDenominationsByAmount %d %d \n", dsq.nDenom, GetDenominationsByAmount(sessionTotalValue));
|
||||
continue;
|
||||
// Try to match their denominations if possible
|
||||
if (!pwalletMain->SelectCoinsByDenominations(dsq.nDenom, nValueMin, maxAmount*COIN, vCoins, nValueIn, -2, 2)){
|
||||
if (!pwalletMain->SelectCoinsByDenominations(dsq.nDenom, nValueMin, maxAmount*COIN, vCoins, nValueIn, 2, nDarksendRounds)){
|
||||
LogPrintf("DoAutomaticDenominating - Couldn't match denominations\n");
|
||||
continue;
|
||||
}
|
||||
sessionMinRounds = 2;
|
||||
sessionMaxRounds = nDarksendRounds;
|
||||
} else {
|
||||
sessionMinRounds = -2;
|
||||
sessionMaxRounds = 2;
|
||||
}
|
||||
dsq.time = 0; //remove node
|
||||
|
||||
// connect to masternode and submit the queue request
|
||||
if(ConnectNode((CAddress)addr, NULL, true)){
|
||||
@ -1605,16 +1580,7 @@ bool CDarkSendPool::DoAutomaticDenominating(bool fDryRun, bool ready)
|
||||
}
|
||||
|
||||
vecMasternodesUsed.push_back(dsq.vin);
|
||||
|
||||
if(minRounds >= 0){
|
||||
//use same denominations
|
||||
std::vector<int64_t> vecAmounts;
|
||||
pwalletMain->ConvertList(vCoins, vecAmounts);
|
||||
sessionDenom = GetDenominationsByAmounts(vecAmounts);
|
||||
} else {
|
||||
//use all possible denominations
|
||||
sessionDenom = GetDenominationsByAmount(sessionTotalValue);
|
||||
}
|
||||
sessionDenom = dsq.nDenom;
|
||||
|
||||
pnode->PushMessage("dsa", sessionDenom, txCollateral);
|
||||
LogPrintf("DoAutomaticDenominating --- connected (from queue), sending dsa for %d %d - %s\n", sessionDenom, GetDenominationsByAmount(sessionTotalValue), pnode->addr.ToString().c_str());
|
||||
@ -1626,6 +1592,8 @@ bool CDarkSendPool::DoAutomaticDenominating(bool fDryRun, bool ready)
|
||||
strAutoDenomResult = "Error connecting to masternode";
|
||||
return DoAutomaticDenominating();
|
||||
}
|
||||
|
||||
dsq.time = 0; //remove node
|
||||
}
|
||||
}
|
||||
|
||||
@ -1670,7 +1638,7 @@ bool CDarkSendPool::DoAutomaticDenominating(bool fDryRun, bool ready)
|
||||
|
||||
vecMasternodesUsed.push_back(darkSendMasterNodes[i].vin);
|
||||
|
||||
if(minRounds >= 0){
|
||||
if(sessionMinRounds >= 0){
|
||||
//use same denominations
|
||||
std::vector<int64_t> vecAmounts;
|
||||
pwalletMain->ConvertList(vCoins, vecAmounts);
|
||||
@ -1699,13 +1667,18 @@ bool CDarkSendPool::DoAutomaticDenominating(bool fDryRun, bool ready)
|
||||
if(!ready) return true;
|
||||
|
||||
if(sessionDenom == 0) return true;
|
||||
}
|
||||
|
||||
|
||||
bool CDarkSendPool::PrepareDarksendDenominate()
|
||||
{
|
||||
// Submit transaction to the pool if we get here, use sessionDenom so we use the same amount of money
|
||||
std::string strError = pwalletMain->PrepareDarksendDenominate(minRounds, maxRounds, sessionTotalValue);
|
||||
std::string strError = pwalletMain->PrepareDarksendDenominate(sessionMinRounds, sessionMaxRounds);
|
||||
LogPrintf("DoAutomaticDenominating : Running darksend denominate. Return '%s'\n", strError.c_str());
|
||||
|
||||
if(strError == "") return true;
|
||||
|
||||
strAutoDenomResult = strError;
|
||||
LogPrintf("DoAutomaticDenominating : Error running denominate, %s\n", strError.c_str());
|
||||
return false;
|
||||
}
|
||||
@ -1912,7 +1885,7 @@ void CDarkSendPool::GetDenominationsToString(int nDenom, std::string& strDenom){
|
||||
// bit 0 - 100DRK+1 ( bit on if present )
|
||||
// bit 1 - 10DRK+1
|
||||
// bit 2 - 1DRK+1
|
||||
// bit 2 - .1DRK+1
|
||||
// bit 3 - .1DRK+1
|
||||
// bit 3 - non-denom
|
||||
|
||||
|
||||
|
@ -257,6 +257,8 @@ public:
|
||||
int sessionUsers; //N Users have said they'll join
|
||||
bool sessionFoundMasternode; //If we've found a compatible masternode
|
||||
int sessionTries;
|
||||
int sessionMinRounds;
|
||||
int sessionMaxRounds;
|
||||
int64_t sessionTotalValue; //used for autoDenom
|
||||
std::vector<CTransaction> vecSessionCollateral;
|
||||
|
||||
@ -296,6 +298,8 @@ public:
|
||||
minBlockSpacing = 1;
|
||||
nDsqCount = 0;
|
||||
vecDisabledDenominations.clear();
|
||||
sessionMinRounds = 0;
|
||||
sessionMaxRounds = 0;
|
||||
|
||||
SetCollateralAddress(strAddress);
|
||||
SetNull();
|
||||
@ -383,6 +387,7 @@ public:
|
||||
|
||||
// Passively run Darksend in the background according to the configuration in settings (only for QT)
|
||||
bool DoAutomaticDenominating(bool fDryRun=false, bool ready=false);
|
||||
bool PrepareDarksendDenominate();
|
||||
|
||||
|
||||
// check for process in Darksend
|
||||
|
@ -28,13 +28,13 @@ Value darksend(const Array& params, bool fHelp)
|
||||
"<amount> is a real and is rounded to the nearest 0.00000001"
|
||||
+ HelpRequiringPassphrase());
|
||||
|
||||
if(fMasterNode)
|
||||
return "DarkSend is not supported from masternodes";
|
||||
|
||||
if (pwalletMain->IsLocked())
|
||||
throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
|
||||
|
||||
if(params[0].get_str() == "auto"){
|
||||
if(fMasterNode)
|
||||
return "DarkSend is not supported from masternodes";
|
||||
|
||||
darkSendPool.DoAutomaticDenominating();
|
||||
return "DoAutomaticDenominating";
|
||||
}
|
||||
|
106
src/wallet.cpp
106
src/wallet.cpp
@ -1450,10 +1450,95 @@ struct CompareByPriority
|
||||
}
|
||||
};
|
||||
|
||||
bool CWallet::SelectCoinsByDenominations(int nDenom, int64_t nValueMin, int64_t nValueMax, std::vector<CTxIn>& setCoinsRet, int64_t& nValueRet, int nDarksendRoundsMin, int nDarksendRoundsMax)
|
||||
{
|
||||
CCoinControl *coinControl=NULL;
|
||||
|
||||
setCoinsRet.clear();
|
||||
nValueRet = 0;
|
||||
|
||||
vector<COutput> vCoins;
|
||||
AvailableCoins(vCoins, false, coinControl, ALL_COINS);
|
||||
|
||||
set<pair<const CWalletTx*,unsigned int> > setCoinsRet2;
|
||||
|
||||
//order the array so fees are first, then denominated money, then the rest.
|
||||
std::random_shuffle(vCoins.rbegin(), vCoins.rend());
|
||||
|
||||
//keep track of each denomination that we have
|
||||
int fFound100 = false;
|
||||
int fFound10 = false;
|
||||
int fFound1 = false;
|
||||
int fFoundDot1 = false;
|
||||
int fFoundND = false;
|
||||
|
||||
//Check to see if any of the denomination are off, in that case mark them as fulfilled
|
||||
if(!(nDenom & (1 << 0))) fFound100 = true;
|
||||
if(!(nDenom & (1 << 1))) fFound10 = true;
|
||||
if(!(nDenom & (1 << 2))) fFound1 = true;
|
||||
if(!(nDenom & (1 << 3))) fFoundDot1 = true;
|
||||
if(!(nDenom & (1 << 4))) fFoundND = true;
|
||||
|
||||
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(fMasterNode && out.tx->vout[out.i].nValue == 1000*COIN) continue; //masternode input
|
||||
|
||||
if(nValueRet + out.tx->vout[out.i].nValue <= nValueMax){
|
||||
bool fAccepted = false;
|
||||
|
||||
// Function returns as follows:
|
||||
//
|
||||
// bit 0 - 100DRK+1 ( bit on if present )
|
||||
// bit 1 - 10DRK+1
|
||||
// bit 2 - 1DRK+1
|
||||
// bit 3 - .1DRK+1
|
||||
// bit 4 - non-denom
|
||||
|
||||
CTxIn vin = CTxIn(out.tx->GetHash(),out.i);
|
||||
|
||||
int rounds = GetInputDarksendRounds(vin);
|
||||
if(rounds >= nDarksendRoundsMax) continue;
|
||||
if(rounds < nDarksendRoundsMin) continue;
|
||||
|
||||
if(fFound100 && fFound10 && fFound1 && fFoundDot1 && fFoundND){ //if fulfilled
|
||||
//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;}
|
||||
else if((nDenom & (1 << 4))) {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) && !fFound100) {fAccepted = true; fFound100 = true;}
|
||||
else if((nDenom & (1 << 1)) && out.tx->vout[out.i].nValue == ((10*COIN)+1) && !fFound10) {fAccepted = true; fFound10 = true;}
|
||||
else if((nDenom & (1 << 2)) && out.tx->vout[out.i].nValue == ((1*COIN)+1) && !fFound1) {fAccepted = true; fFound1 = true;}
|
||||
else if((nDenom & (1 << 3)) && out.tx->vout[out.i].nValue == ((.1*COIN)+1) && !fFoundDot1) {fAccepted = true; fFoundDot1 = true;}
|
||||
else if((nDenom & (1 << 4)) && !fFoundND) {fAccepted = true; fFoundND = true;}
|
||||
}
|
||||
if(!fAccepted) continue;
|
||||
|
||||
vin.prevPubKey = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey
|
||||
nValueRet += out.tx->vout[out.i].nValue;
|
||||
setCoinsRet.push_back(vin);
|
||||
setCoinsRet2.insert(make_pair(out.tx, out.i));
|
||||
}
|
||||
}
|
||||
|
||||
if(nValueRet >= nValueMin && fFound100 && fFound10 && fFound1 && fFoundDot1 && fFoundND) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool CWallet::SelectCoinsDark(int64_t nValueMin, int64_t nValueMax, std::vector<CTxIn>& setCoinsRet, int64_t& nValueRet, int nDarksendRoundsMin, int nDarksendRoundsMax, bool& hasFeeInput) const
|
||||
{
|
||||
CCoinControl *coinControl=NULL;
|
||||
|
||||
setCoinsRet.clear();
|
||||
nValueRet = 0;
|
||||
|
||||
vector<COutput> vCoins;
|
||||
AvailableCoins(vCoins, false, coinControl, ALL_COINS);
|
||||
|
||||
@ -1962,40 +2047,29 @@ int64_t CWallet::GetTotalValue(std::vector<CTxIn> vCoins) {
|
||||
return nTotalValue;
|
||||
}
|
||||
|
||||
string CWallet::PrepareDarksendDenominate(int minRounds, int maxRounds, int64_t maxAmount)
|
||||
string CWallet::PrepareDarksendDenominate(int minRounds, int maxRounds)
|
||||
{
|
||||
if (IsLocked())
|
||||
return _("Error: Wallet locked, unable to create transaction!");
|
||||
|
||||
if(darkSendPool.GetState() != POOL_STATUS_ERROR && darkSendPool.GetState() != POOL_STATUS_SUCCESS){
|
||||
if(darkSendPool.GetMyTransactionCount() > 0){
|
||||
if(darkSendPool.GetState() != POOL_STATUS_ERROR && darkSendPool.GetState() != POOL_STATUS_SUCCESS)
|
||||
if(darkSendPool.GetMyTransactionCount() > 0)
|
||||
return _("Error: You already have pending entries in the Darksend pool");
|
||||
}
|
||||
}
|
||||
|
||||
// ** find the coins we'll use
|
||||
std::vector<CTxIn> vCoins;
|
||||
int64_t nValueIn = 0;
|
||||
CReserveKey reservekey(this);
|
||||
|
||||
//make sure we
|
||||
bool hasFeeInput = false;
|
||||
|
||||
//select coins we'll use
|
||||
if (!SelectCoinsDark(0.1*COIN, maxAmount, vCoins, nValueIn, minRounds, maxRounds, hasFeeInput))
|
||||
{
|
||||
vCoins.clear();
|
||||
|
||||
if (!SelectCoinsByDenominations(darkSendPool.sessionDenom, 0.1*COIN, DARKSEND_POOL_MAX, vCoins, nValueIn, minRounds, maxRounds))
|
||||
return _("Insufficient funds");
|
||||
}
|
||||
|
||||
// calculate total value out
|
||||
int64_t nTotalValue = GetTotalValue(vCoins);
|
||||
|
||||
LogPrintf("PrepareDarksendDenominate - preparing darksend denominate . Asked for: %d, Got: %d \n", maxAmount, nTotalValue);
|
||||
LogPrintf("PrepareDarksendDenominate - preparing darksend denominate . Got: %d \n", nTotalValue);
|
||||
|
||||
//--------------
|
||||
|
||||
BOOST_FOREACH(CTxIn v, vCoins)
|
||||
LockCoin(v.prevout);
|
||||
|
||||
|
@ -133,6 +133,8 @@ private:
|
||||
public:
|
||||
bool SelectCoins(int64_t nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64_t& nValueRet, const CCoinControl *coinControl = NULL, AvailableCoinsType coin_type=ALL_COINS) const;
|
||||
bool SelectCoinsDark(int64_t nValueMin, int64_t nValueMax, std::vector<CTxIn>& setCoinsRet, int64_t& nValueRet, int nDarksendRoundsMin, int nDarksendRoundsMax, bool& hasFeeInput) const;
|
||||
bool SelectCoinsByDenominations(int nDenom, int64_t nValueMin, int64_t nValueMax, std::vector<CTxIn>& setCoinsRet, int64_t& nValueRet, int nDarksendRoundsMin, int nDarksendRoundsMax);
|
||||
bool SelectCoinsByDenominations(int nDenom, int64_t nValueMin, int64_t nValueMax, int nDarksendRoundsMin, int nDarksendRoundsMax);
|
||||
bool SelectCoinsDarkDenominated(int64_t nTargetValue, std::vector<CTxIn>& setCoinsRet, int64_t& nValueRet) const;
|
||||
bool SelectCoinsMasternode(CTxIn& vin, int64_t& nValueRet, CScript& pubScript) const;
|
||||
bool HasDarksendFeeInputs() const;
|
||||
@ -286,7 +288,7 @@ public:
|
||||
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, std::string strCommand="tx");
|
||||
std::string SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, AvailableCoinsType coin_type=ALL_COINS);
|
||||
std::string SendMoneyToDestination(const CTxDestination &address, int64_t nValue, CWalletTx& wtxNew, AvailableCoinsType coin_type=ALL_COINS);
|
||||
std::string PrepareDarksendDenominate(int minRounds, int maxRounds, int64_t maxAmount);
|
||||
std::string PrepareDarksendDenominate(int minRounds, int maxRounds);
|
||||
bool CreateCollateralTransaction(CTransaction& txCollateral, std::string strReason);
|
||||
bool ConvertList(std::vector<CTxIn> vCoins, std::vector<int64_t>& vecAmounts);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user