Skip to content

Commit 5928d0b

Browse files
committed
[PHR-8] Added select coin strategy. Sorting coins by amount if the transaction size is too large.
1 parent a6f2527 commit 5928d0b

File tree

2 files changed

+107
-11
lines changed

2 files changed

+107
-11
lines changed

src/wallet.cpp

Lines changed: 89 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ struct CompareValueOnly {
7373
}
7474
};
7575

76+
enum class CWallet::CoinSelectStrategy
77+
{
78+
random,
79+
descentByAmount,
80+
};
81+
7682
std::string COutput::ToString() const
7783
{
7884
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->vout[i].nValue));
@@ -2138,7 +2144,7 @@ bool CWallet::MintableCoins()
21382144
return false;
21392145
}
21402146

2141-
bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, vector<COutput> vCoins, set<pair<const CWalletTx*, unsigned int> >& setCoinsRet, CAmount& nValueRet) const
2147+
bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, vector<COutput> vCoins, set<pair<const CWalletTx*, unsigned int> >& setCoinsRet, CAmount& nValueRet, CoinSelectStrategy coinSelectStrategy) const
21422148
{
21432149
setCoinsRet.clear();
21442150
nValueRet = 0;
@@ -2150,7 +2156,18 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
21502156
vector<pair<CAmount, pair<const CWalletTx*, unsigned int> > > vValue;
21512157
CAmount nTotalLower = 0;
21522158

2153-
random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt);
2159+
switch(coinSelectStrategy) {
2160+
case CoinSelectStrategy::descentByAmount:
2161+
std::sort(vCoins.begin(), vCoins.end(), [](const COutput & a, const COutput & b) -> bool {
2162+
return a.Value() > b.Value();
2163+
});
2164+
break;
2165+
2166+
case CoinSelectStrategy::random:
2167+
default:
2168+
random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt);
2169+
break;
2170+
}
21542171

21552172
// move denoms down on the list
21562173
sort(vCoins.begin(), vCoins.end(), less_then_denom);
@@ -2245,7 +2262,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
22452262
return true;
22462263
}
22472264

2248-
bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*, unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl, AvailableCoinsType coin_type, bool useIX) const
2265+
bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*, unsigned int> >& setCoinsRet, CAmount& nValueRet, CoinSelectStrategy coinSelectStrategy, const CCoinControl* coinControl, AvailableCoinsType coin_type, bool useIX) const
22492266
{
22502267
// Note: this function should never be used for "always free" tx types like dstx
22512268

@@ -2291,9 +2308,9 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
22912308
return (nValueRet >= nTargetValue);
22922309
}
22932310

2294-
return (SelectCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet) ||
2295-
SelectCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet) ||
2296-
(bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet)));
2311+
return (SelectCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet, coinSelectStrategy) ||
2312+
SelectCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet, coinSelectStrategy) ||
2313+
(bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet, coinSelectStrategy)));
22972314
}
22982315

22992316
struct CompareByPriority {
@@ -2621,6 +2638,18 @@ bool CWallet::ConvertList(std::vector<CTxIn> vCoins, std::vector<CAmount>& vecAm
26212638
return true;
26222639
}
26232640

2641+
enum class ErrorOfCreateTransaction
2642+
{
2643+
other,
2644+
transactionSizeTooLarge,
2645+
};
2646+
2647+
struct CWallet::ParamForCreateTransaction
2648+
{
2649+
CoinSelectStrategy coinSelectStrategy;
2650+
ErrorOfCreateTransaction error;
2651+
};
2652+
26242653
bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
26252654
CWalletTx& wtxNew,
26262655
CReserveKey& reservekey,
@@ -2630,6 +2659,57 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
26302659
AvailableCoinsType coin_type,
26312660
bool useIX,
26322661
CAmount nFeePay)
2662+
{
2663+
ParamForCreateTransaction param = {};
2664+
2665+
param.coinSelectStrategy = CoinSelectStrategy::random;
2666+
param.error = ErrorOfCreateTransaction::other;
2667+
bool result = CreateTransactionHelper(
2668+
vecSend,
2669+
wtxNew,
2670+
reservekey,
2671+
nFeeRet,
2672+
strFailReason,
2673+
coinControl,
2674+
coin_type,
2675+
useIX,
2676+
nFeePay,
2677+
&param
2678+
);
2679+
if(result) {
2680+
return result;
2681+
}
2682+
if(param.error == ErrorOfCreateTransaction::transactionSizeTooLarge) {
2683+
// If the transaction is too large for the maximum fee, let's try to use different strategy to select coins.
2684+
param.coinSelectStrategy = CoinSelectStrategy::descentByAmount;
2685+
param.error = ErrorOfCreateTransaction::other;
2686+
result = CreateTransactionHelper(
2687+
vecSend,
2688+
wtxNew,
2689+
reservekey,
2690+
nFeeRet,
2691+
strFailReason,
2692+
coinControl,
2693+
coin_type,
2694+
useIX,
2695+
nFeePay,
2696+
&param
2697+
);
2698+
}
2699+
2700+
return result;
2701+
}
2702+
2703+
bool CWallet::CreateTransactionHelper(const std::vector<std::pair<CScript, CAmount> >& vecSend,
2704+
CWalletTx& wtxNew,
2705+
CReserveKey& reservekey,
2706+
CAmount& nFeeRet,
2707+
std::string& strFailReason,
2708+
const CCoinControl* coinControl,
2709+
AvailableCoinsType coin_type,
2710+
bool useIX,
2711+
CAmount nFeePay,
2712+
ParamForCreateTransaction * param)
26332713
{
26342714
if (useIX && nFeePay < CENT) nFeePay = CENT;
26352715

@@ -2701,7 +2781,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
27012781
set<pair<const CWalletTx*, unsigned int> > setCoins;
27022782
CAmount nValueIn = 0;
27032783

2704-
if (!SelectCoins(nTotalValue, setCoins, nValueIn, coinControl, coin_type, useIX)) {
2784+
if (!SelectCoins(nTotalValue, setCoins, nValueIn, param->coinSelectStrategy, coinControl, coin_type, useIX)) {
27052785
if (coin_type == ALL_COINS) {
27062786
strFailReason = _("Insufficient funds.");
27072787
} else if (coin_type == ONLY_NOT10000IFMN) {
@@ -2831,6 +2911,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
28312911
// Limit size
28322912
if (GetTransactionCost(txNew) >= MAX_STANDARD_TX_COST) {
28332913
strFailReason = _("Transaction too large");
2914+
param->error = ErrorOfCreateTransaction::transactionSizeTooLarge;
28342915
return false;
28352916
}
28362917
unsigned int nBytes = GetVirtualTransactionSize(txNew);
@@ -4620,7 +4701,7 @@ bool CWallet::CreateZerocoinMintTransaction(const CAmount nValue, CMutableTransa
46204701
nValueIn = nValue;
46214702
} else {
46224703
// select UTXO's to use
4623-
if (!SelectCoins(nTotalValue, setCoins, nValueIn, coinControl)) {
4704+
if (!SelectCoins(nTotalValue, setCoins, nValueIn, CoinSelectStrategy::random, coinControl)) {
46244705
strFailReason = _("Insufficient or insufficient confirmed funds, you might need to wait a few minutes and try again.");
46254706
return false;
46264707
}

src/wallet.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,22 @@ class CAddressBookData
178178
class CWallet : public CCryptoKeyStore, public CValidationInterface
179179
{
180180
private:
181-
bool SelectCoins(const CAmount& nTargetValue, std::set<std::pair<const CWalletTx*, unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl = NULL, AvailableCoinsType coin_type = ALL_COINS, bool useIX = true) const;
181+
// ParamForCreateTransaction is introduced to control how CreateTransaction works.
182+
struct ParamForCreateTransaction;
183+
enum class CoinSelectStrategy;
184+
bool CreateTransactionHelper(const std::vector<std::pair<CScript, CAmount> >& vecSend,
185+
CWalletTx& wtxNew,
186+
CReserveKey& reservekey,
187+
CAmount& nFeeRet,
188+
std::string& strFailReason,
189+
const CCoinControl* coinControl,
190+
AvailableCoinsType coin_type,
191+
bool useIX,
192+
CAmount nFeePay,
193+
ParamForCreateTransaction * param);
194+
195+
196+
bool SelectCoins(const CAmount& nTargetValue, std::set<std::pair<const CWalletTx*, unsigned int> >& setCoinsRet, CAmount& nValueRet, CoinSelectStrategy coinSelectStrategy, const CCoinControl* coinControl = NULL, AvailableCoinsType coin_type = ALL_COINS, bool useIX = true) const;
182197
//it was 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, bool useIX = true) const;
183198

184199
CWalletDB* pwalletdbEncryption;
@@ -203,7 +218,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
203218
void AddToSpends(const uint256& wtxid);
204219

205220
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>);
206-
221+
207222
public:
208223
bool MintableCoins();
209224
bool SelectStakeCoins(std::set<std::pair<const CWalletTx*, unsigned int> >& setCoins, CAmount nTargetAmount) const;
@@ -399,7 +414,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
399414

400415
void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed = true, const CCoinControl* coinControl = NULL, bool fIncludeZeroValue = false, AvailableCoinsType nCoinType = ALL_COINS, bool fUseIX = false, int nWatchonlyConfig = 1) const;
401416
std::map<CTxDestination, std::vector<COutput> > AvailableCoinsByAddress(bool fConfirmed = true, CAmount maxCoinValue = 0);
402-
bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*, unsigned int> >& setCoinsRet, CAmount& nValueRet) const;
417+
bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*, unsigned int> >& setCoinsRet, CAmount& nValueRet, CoinSelectStrategy coinSelectStrategy) const;
403418

404419
/// Get 1000DASH output and keys which can be used for the Masternode
405420
bool GetMasternodeVinAndKeys(CTxIn& txinRet, CPubKey& pubKeyRet, CKey& keyRet, std::string strTxHash = "", std::string strOutputIndex = "");

0 commit comments

Comments
 (0)