Return improved error message when trying to spend Coinbase coins (#1373).
Extra parameter added to AvailableCoins to include or exclude Coinbase coins. SelectCoins, used for sending taddr->taddr, will exclude Coinbase coins. Added qa rpc test and a runtime parameter -regtestprotectcoinbase to enforce the coinbase->zaddr consensus rule in regtest mode.
This commit is contained in:
@@ -2050,7 +2050,7 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const
|
||||
/**
|
||||
* populate vCoins with vector of available COutputs.
|
||||
*/
|
||||
void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue) const
|
||||
void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue, bool fIncludeCoinBase) const
|
||||
{
|
||||
vCoins.clear();
|
||||
|
||||
@@ -2067,6 +2067,9 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
|
||||
if (fOnlyConfirmed && !pcoin->IsTrusted())
|
||||
continue;
|
||||
|
||||
if (pcoin->IsCoinBase() && !fIncludeCoinBase)
|
||||
continue;
|
||||
|
||||
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
|
||||
continue;
|
||||
|
||||
@@ -2232,10 +2235,38 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl) const
|
||||
bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, bool& fOnlyCoinbaseCoinsRet, bool& fNeedCoinbaseCoinsRet, const CCoinControl* coinControl) const
|
||||
{
|
||||
vector<COutput> vCoins;
|
||||
AvailableCoins(vCoins, true, coinControl);
|
||||
// Output parameter fOnlyCoinbaseCoinsRet is set to true when the only available coins are coinbase utxos.
|
||||
vector<COutput> vCoinsNoCoinbase, vCoinsWithCoinbase;
|
||||
AvailableCoins(vCoinsNoCoinbase, true, coinControl, false, false);
|
||||
AvailableCoins(vCoinsWithCoinbase, true, coinControl, false, true);
|
||||
fOnlyCoinbaseCoinsRet = vCoinsNoCoinbase.size() == 0 && vCoinsWithCoinbase.size() > 0;
|
||||
|
||||
// If coinbase utxos can only be sent to zaddrs, exclude any coinbase utxos from coin selection.
|
||||
bool fProtectCoinbase = Params().GetConsensus().fCoinbaseMustBeProtected;
|
||||
vector<COutput> vCoins = (fProtectCoinbase) ? vCoinsNoCoinbase : vCoinsWithCoinbase;
|
||||
|
||||
// Output parameter fNeedCoinbaseCoinsRet is set to true if coinbase utxos need to be spent to meet target amount
|
||||
if (fProtectCoinbase && vCoinsWithCoinbase.size() > vCoinsNoCoinbase.size()) {
|
||||
CAmount value = 0;
|
||||
for (const COutput& out : vCoinsNoCoinbase) {
|
||||
if (!out.fSpendable) {
|
||||
continue;
|
||||
}
|
||||
value += out.tx->vout[out.i].nValue;
|
||||
}
|
||||
if (value <= nTargetValue) {
|
||||
CAmount valueWithCoinbase = 0;
|
||||
for (const COutput& out : vCoinsWithCoinbase) {
|
||||
if (!out.fSpendable) {
|
||||
continue;
|
||||
}
|
||||
valueWithCoinbase += out.tx->vout[out.i].nValue;
|
||||
}
|
||||
fNeedCoinbaseCoinsRet = (valueWithCoinbase >= nTargetValue);
|
||||
}
|
||||
}
|
||||
|
||||
// coin control -> return all selected outputs (we want all selected to go into the transaction for sure)
|
||||
if (coinControl && coinControl->HasSelected())
|
||||
@@ -2355,9 +2386,17 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend,
|
||||
// Choose coins to use
|
||||
set<pair<const CWalletTx*,unsigned int> > setCoins;
|
||||
CAmount nValueIn = 0;
|
||||
if (!SelectCoins(nTotalValue, setCoins, nValueIn, coinControl))
|
||||
bool fOnlyCoinbaseCoins = false;
|
||||
bool fNeedCoinbaseCoins = false;
|
||||
if (!SelectCoins(nTotalValue, setCoins, nValueIn, fOnlyCoinbaseCoins, fNeedCoinbaseCoins, coinControl))
|
||||
{
|
||||
strFailReason = _("Insufficient funds");
|
||||
if (fOnlyCoinbaseCoins && Params().GetConsensus().fCoinbaseMustBeProtected) {
|
||||
strFailReason = _("Coinbase funds can only be sent to a zaddr");
|
||||
} else if (fNeedCoinbaseCoins) {
|
||||
strFailReason = _("Insufficient funds, coinbase funds can only be spent after they have been sent to a zaddr");
|
||||
} else {
|
||||
strFailReason = _("Insufficient funds");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
|
||||
|
||||
Reference in New Issue
Block a user