aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/wallet.cpp')
-rw-r--r--src/wallet/wallet.cpp141
1 files changed, 95 insertions, 46 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 967683256..0b18be876 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1479,12 +1479,12 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
pindex = chainActive.Next(pindex);
ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
- double dProgressStart = Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex, false);
- double dProgressTip = Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip(), false);
+ double dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex);
+ double dProgressTip = GuessVerificationProgress(chainParams.TxData(), chainActive.Tip());
while (pindex)
{
if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0)
- ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex, false) - dProgressStart) / (dProgressTip - dProgressStart) * 100))));
+ ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((GuessVerificationProgress(chainParams.TxData(), pindex) - dProgressStart) / (dProgressTip - dProgressStart) * 100))));
CBlock block;
ReadBlockFromDisk(block, pindex, Params().GetConsensus());
@@ -1497,7 +1497,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
pindex = chainActive.Next(pindex);
if (GetTime() >= nNow + 60) {
nNow = GetTime();
- LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex));
+ LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex));
}
}
ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI
@@ -2192,14 +2192,15 @@ bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount&
return res;
}
-bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const CTxDestination& destChange)
+bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, const CTxDestination& destChange)
{
vector<CRecipient> vecSend;
// Turn the txout set into a CRecipient vector
- BOOST_FOREACH(const CTxOut& txOut, tx.vout)
+ for (size_t idx = 0; idx < tx.vout.size(); idx++)
{
- CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, false};
+ const CTxOut& txOut = tx.vout[idx];
+ CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, setSubtractFeeFromOutputs.count(idx) == 1};
vecSend.push_back(recipient);
}
@@ -2221,6 +2222,10 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool ov
if (nChangePosInOut != -1)
tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]);
+ // Copy output sizes from new transaction; they may have had the fee subtracted from them
+ for (unsigned int idx = 0; idx < tx.vout.size(); idx++)
+ tx.vout[idx].nValue = wtx.tx->vout[idx].nValue;
+
// Add new txins (keeping original txin scriptSig/order)
BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
{
@@ -2245,7 +2250,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
CAmount nValue = 0;
int nChangePosRequest = nChangePosInOut;
unsigned int nSubtractFeeFromAmount = 0;
- BOOST_FOREACH (const CRecipient& recipient, vecSend)
+ for (const auto& recipient : vecSend)
{
if (nValue < 0 || recipient.nAmount < 0)
{
@@ -2300,6 +2305,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
assert(txNew.nLockTime < LOCKTIME_THRESHOLD);
{
+ set<pair<const CWalletTx*,unsigned int> > setCoins;
LOCK2(cs_main, cs_wallet);
{
std::vector<COutput> vAvailableCoins;
@@ -2320,7 +2326,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
nValueToSelect += nFeeRet;
double dPriority = 0;
// vouts to the payees
- BOOST_FOREACH (const CRecipient& recipient, vecSend)
+ for (const auto& recipient : vecSend)
{
CTxOut txout(recipient.nAmount, recipient.scriptPubKey);
@@ -2335,7 +2341,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
}
}
- if (txout.IsDust(::minRelayTxFee))
+ if (txout.IsDust(dustRelayFee))
{
if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)
{
@@ -2352,14 +2358,14 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
}
// Choose coins to use
- set<pair<const CWalletTx*,unsigned int> > setCoins;
CAmount nValueIn = 0;
+ setCoins.clear();
if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl))
{
strFailReason = _("Insufficient funds");
return false;
}
- BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
+ for (const auto& pcoin : setCoins)
{
CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue;
//The coin age after the next block (depth+1) is used instead of the current,
@@ -2413,16 +2419,16 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
// We do not move dust-change to fees, because the sender would end up paying more than requested.
// This would be against the purpose of the all-inclusive feature.
// So instead we raise the change and deduct from the recipient.
- if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(::minRelayTxFee))
+ if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee))
{
- CAmount nDust = newTxOut.GetDustThreshold(::minRelayTxFee) - newTxOut.nValue;
+ CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue;
newTxOut.nValue += nDust; // raise change until no more dust
for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient
{
if (vecSend[i].fSubtractFeeFromAmount)
{
txNew.vout[i].nValue -= nDust;
- if (txNew.vout[i].IsDust(::minRelayTxFee))
+ if (txNew.vout[i].IsDust(dustRelayFee))
{
strFailReason = _("The transaction amount is too small to send after the fee has been deducted");
return false;
@@ -2434,7 +2440,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
// Never create dust outputs; if we would, just
// add the dust to the fee.
- if (newTxOut.IsDust(::minRelayTxFee))
+ if (newTxOut.IsDust(dustRelayFee))
{
nChangePosInOut = -1;
nFeeRet += nChange;
@@ -2470,24 +2476,18 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
// to avoid conflicting with other possible uses of nSequence,
// and in the spirit of "smallest posible change from prior
// behavior."
- BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
+ for (const auto& coin : setCoins)
txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second,CScript(),
std::numeric_limits<unsigned int>::max() - (fWalletRbf ? 2 : 1)));
- // Sign
+ // Fill in dummy signatures for fee calculation.
int nIn = 0;
- CTransaction txNewConst(txNew);
- BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
+ for (const auto& coin : setCoins)
{
- bool signSuccess;
const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey;
SignatureData sigdata;
- if (sign)
- signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata);
- else
- signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata);
- if (!signSuccess)
+ if (!ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata))
{
strFailReason = _("Signing transaction failed");
return false;
@@ -2500,26 +2500,15 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
unsigned int nBytes = GetVirtualTransactionSize(txNew);
- // Remove scriptSigs if we used dummy signatures for fee calculation
- if (!sign) {
- BOOST_FOREACH (CTxIn& vin, txNew.vin) {
- vin.scriptSig = CScript();
- vin.scriptWitness.SetNull();
- }
- }
-
- // Embed the constructed transaction data in wtxNew.
- wtxNew.SetTx(MakeTransactionRef(std::move(txNew)));
+ CTransaction txNewConst(txNew);
+ dPriority = txNewConst.ComputePriority(dPriority, nBytes);
- // Limit size
- if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT)
- {
- strFailReason = _("Transaction too large");
- return false;
+ // Remove scriptSigs to eliminate the fee calculation dummy signatures
+ for (auto& vin : txNew.vin) {
+ vin.scriptSig = CScript();
+ vin.scriptWitness.SetNull();
}
- dPriority = wtxNew.tx->ComputePriority(dPriority, nBytes);
-
// Allow to override the default confirmation target over the CoinControl instance
int currentConfirmationTarget = nTxConfirmTarget;
if (coinControl && coinControl->nConfirmTarget > 0)
@@ -2550,20 +2539,80 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
return false;
}
- if (nFeeRet >= nFeeNeeded)
+ if (nFeeRet >= nFeeNeeded) {
+ // Reduce fee to only the needed amount if we have change
+ // output to increase. This prevents potential overpayment
+ // in fees if the coins selected to meet nFeeNeeded result
+ // in a transaction that requires less fee than the prior
+ // iteration.
+ // TODO: The case where nSubtractFeeFromAmount > 0 remains
+ // to be addressed because it requires returning the fee to
+ // the payees and not the change output.
+ // TODO: The case where there is no change output remains
+ // to be addressed so we avoid creating too small an output.
+ if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) {
+ CAmount extraFeePaid = nFeeRet - nFeeNeeded;
+ vector<CTxOut>::iterator change_position = txNew.vout.begin()+nChangePosInOut;
+ change_position->nValue += extraFeePaid;
+ nFeeRet -= extraFeePaid;
+ }
break; // Done, enough fee included.
+ }
+
+ // Try to reduce change to include necessary fee
+ if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) {
+ CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet;
+ vector<CTxOut>::iterator change_position = txNew.vout.begin()+nChangePosInOut;
+ // Only reduce change if remaining amount is still a large enough output.
+ if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) {
+ change_position->nValue -= additionalFeeNeeded;
+ nFeeRet += additionalFeeNeeded;
+ break; // Done, able to increase fee from change
+ }
+ }
// Include more fee and try again.
nFeeRet = nFeeNeeded;
continue;
}
}
+
+ if (sign)
+ {
+ CTransaction txNewConst(txNew);
+ int nIn = 0;
+ for (const auto& coin : setCoins)
+ {
+ const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey;
+ SignatureData sigdata;
+
+ if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata))
+ {
+ strFailReason = _("Signing transaction failed");
+ return false;
+ } else {
+ UpdateTransaction(txNew, nIn, sigdata);
+ }
+
+ nIn++;
+ }
+ }
+
+ // Embed the constructed transaction data in wtxNew.
+ wtxNew.SetTx(MakeTransactionRef(std::move(txNew)));
+
+ // Limit size
+ if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT)
+ {
+ strFailReason = _("Transaction too large");
+ return false;
+ }
}
if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
// Lastly, ensure this tx will pass the mempool's chain limits
LockPoints lp;
- CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, false, 0, false, 0, lp);
+ CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp);
CTxMemPool::setEntries setAncestors;
size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000;
@@ -3803,5 +3852,5 @@ int CMerkleTx::GetBlocksToMaturity() const
bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state)
{
- return ::AcceptToMemoryPool(mempool, state, tx, true, NULL, false, nAbsurdFee);
+ return ::AcceptToMemoryPool(mempool, state, tx, true, NULL, NULL, false, nAbsurdFee);
}