Auto merge of #1431 - bitcartel:master_1373_taddr_coinbase_error, r=bitcartel
Return a more informative error message when trying to spend coinbase; select non-coinbase inputs when sending to a transparent output if needed For #1373 and #1519 Code change: - Extra parameter added to AvailableCoins to include or exclude Coinbase coins. Default value of parameter is 'true' as current behaviour is to include Coinbase coins. - SelectCoins, used for sending taddr->taddr, will now exclude Coinbase coins. Unit test: Tried to write a test to focus on the extra parameter added to AvailableCoins but could not. Empirical testing on Testnet: Current behaviour is that upstream RPC commands sendfrom and sendtoaddress try to spend coinbase coins returned by AvailableCoins. So the user will see: ``` ./zcash-cli sendtoaddress mrEGRmGJhmwAa4MQjzGd86ry63vrvovu9b 1000.0 error: {"code":-6,"message":"Insufficient funds"} ./zcash-cli sendtoaddress mrEGRmGJhmwAa4MQjzGd86ry63vrvovu9b 0.00003000 error: {"code":-4,"message":"Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."} ./zcash-cli sendfrom "" mrEGRmGJhmwAa4MQjzGd86ry63vrvovu9b 0.00003000 error: {"code":-4,"message":"Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."} ``` After fix is applied: ``` ./zcash-cli sendtoaddress mrEGRmGJhmwAa4MQjzGd86ry63vrvovu9b 1000.0 error: {"code":-6,"message":"Insufficient funds"} ./zcash-cli sendtoaddress mrEGRmGJhmwAa4MQjzGd86ry63vrvovu9b 0.00003000 error: {"code":-4,"message":"Coinbase funds can only be sent to a zaddr"} ``` When non-coinbase UTXOs exist, they will now be selected and used: ``` ./zcash-cli z_sendmany tnPJZHeVxegCg91utaquBRPEDBGjozfz9iLDHt7zvphFbZdspNgkTVLCGjDcadQBKNyUwKs8pNjDXuEZKrE1aNLpFwHgz4t '[{"address":"mx5fTRhLZwbYE7ZqhAPueZgQGSnwTbdvKU", "amount":0.01}]' ./zcash-cli sendtoaddress mrEGRmGJhmwAa4MQjzGd86ry63vrvovu9b 1000.0 error: {"code":-6,"message":"Insufficient funds"} ./zcash-cli sendtoaddress mrEGRmGJhmwAa4MQjzGd86ry63vrvovu9b 0.00003000 9818e543ac2f689d4ce8b52087607d73fecd771d45d316a1d9db092f0485aff2 ./zcash-cli sendfrom "" mrEGRmGJhmwAa4MQjzGd86ry63vrvovu9b 0.00003000 899f2894823f51f15fc73b5e0871ac943edbe0ff88e1635f86906087b72caf30 ```
This commit is contained in:
@@ -2102,7 +2102,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();
|
||||
|
||||
@@ -2119,6 +2119,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;
|
||||
|
||||
@@ -2284,10 +2287,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())
|
||||
@@ -2407,9 +2438,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