Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 2,863 Lines • ▼ Show 20 Lines | bool CWallet::CreateTransaction(const std::vector<CRecipient> &vecSend, | ||||
{ | { | ||||
std::set<CInputCoin> setCoins; | std::set<CInputCoin> setCoins; | ||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
std::vector<COutput> vAvailableCoins; | std::vector<COutput> vAvailableCoins; | ||||
AvailableCoins(vAvailableCoins, true, coinControl); | AvailableCoins(vAvailableCoins, true, coinControl); | ||||
// Create change script that will be used if we need change | |||||
// TODO: pass in scriptChange instead of reservekey so | |||||
// change transaction isn't always pay-to-bitcoin-address | |||||
CScript scriptChange; | |||||
// coin control: send change to custom address | |||||
if (coinControl && | |||||
!boost::get<CNoDestination>(&coinControl->destChange)) { | |||||
scriptChange = GetScriptForDestination(coinControl->destChange); | |||||
// no coin control: send change to newly generated address | |||||
} else { | |||||
// Note: We use a new key here to keep it from being obvious | |||||
// which side is the change. | |||||
// The drawback is that by not reusing a previous key, the | |||||
// change may be lost if a backup is restored, if the backup | |||||
// doesn't have the new private key for the change. If we | |||||
// reused the old key, it would be possible to add code to look | |||||
// for and rediscover unknown transactions that were written | |||||
// with keys of ours to recover post-backup change. | |||||
// Reserve a new key pair from key pool | |||||
CPubKey vchPubKey; | |||||
bool ret; | |||||
ret = reservekey.GetReservedKey(vchPubKey, true); | |||||
if (!ret) { | |||||
strFailReason = | |||||
_("Keypool ran out, please call keypoolrefill first"); | |||||
return false; | |||||
} | |||||
scriptChange = GetScriptForDestination(vchPubKey.GetID()); | |||||
} | |||||
CTxOut change_prototype_txout(Amount::zero(), scriptChange); | |||||
size_t change_prototype_size = | |||||
GetSerializeSize(change_prototype_txout, SER_DISK, 0); | |||||
nFeeRet = Amount::zero(); | nFeeRet = Amount::zero(); | ||||
// Start with no fee and loop until there is enough fee. | bool pick_new_inputs = true; | ||||
Amount nValueIn = Amount::zero(); | |||||
// Start with no fee and loop until there is enough fee | |||||
while (true) { | while (true) { | ||||
nChangePosInOut = nChangePosRequest; | nChangePosInOut = nChangePosRequest; | ||||
txNew.vin.clear(); | txNew.vin.clear(); | ||||
txNew.vout.clear(); | txNew.vout.clear(); | ||||
wtxNew.fFromMe = true; | wtxNew.fFromMe = true; | ||||
bool fFirst = true; | bool fFirst = true; | ||||
Amount nValueToSelect = nValue; | Amount nValueToSelect = nValue; | ||||
Show All 34 Lines | assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
txNew.vout.push_back(txout); | txNew.vout.push_back(txout); | ||||
} | } | ||||
// Choose coins to use. | // Choose coins to use | ||||
Amount nValueIn = Amount::zero(); | if (pick_new_inputs) { | ||||
nValueIn = Amount::zero(); | |||||
setCoins.clear(); | setCoins.clear(); | ||||
if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, | if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, | ||||
nValueIn, coinControl)) { | nValueIn, coinControl)) { | ||||
strFailReason = _("Insufficient funds"); | strFailReason = _("Insufficient funds"); | ||||
return false; | return false; | ||||
} | } | ||||
} | |||||
for (const auto &pcoin : setCoins) { | for (const auto &pcoin : setCoins) { | ||||
Amount nCredit = pcoin.txout.nValue; | Amount nCredit = pcoin.txout.nValue; | ||||
// The coin age after the next block (depth+1) is used instead | // The coin age after the next block (depth+1) is used instead | ||||
// of the current, reflecting an assumption the user would | // of the current, reflecting an assumption the user would | ||||
// accept a bit more delay for a chance at a free transaction. | // accept a bit more delay for a chance at a free transaction. | ||||
// But mempool inputs might still be in the mempool, so their | // But mempool inputs might still be in the mempool, so their | ||||
// age stays 0. | // age stays 0. | ||||
int age = pcoin.wtx->GetDepthInMainChain(); | int age = pcoin.wtx->GetDepthInMainChain(); | ||||
assert(age >= 0); | assert(age >= 0); | ||||
if (age != 0) { | if (age != 0) { | ||||
age += 1; | age += 1; | ||||
} | } | ||||
dPriority += (age * nCredit) / SATOSHI; | dPriority += (age * nCredit) / SATOSHI; | ||||
} | } | ||||
const Amount nChange = nValueIn - nValueToSelect; | const Amount nChange = nValueIn - nValueToSelect; | ||||
if (nChange > Amount::zero()) { | if (nChange > Amount::zero()) { | ||||
// Fill a vout to ourself. | // Fill a vout to ourself. | ||||
// TODO: pass in scriptChange instead of reservekey so change | |||||
// transaction isn't always pay-to-bitcoin-address. | |||||
CScript scriptChange; | |||||
// Coin control: send change to custom address. | |||||
if (coinControl && | |||||
!boost::get<CNoDestination>(&coinControl->destChange)) { | |||||
scriptChange = | |||||
GetScriptForDestination(coinControl->destChange); | |||||
// No coin control: send change to newly generated address. | |||||
} else { | |||||
// Note: We use a new key here to keep it from being obvious | |||||
// which side is the change. The drawback is that by not | |||||
// reusing a previous key, the change may be lost if a | |||||
// backup is restored, if the backup doesn't have the new | |||||
// private key for the change. If we reused the old key, it | |||||
// would be possible to add code to look for and rediscover | |||||
// unknown transactions that were written with keys of ours | |||||
// to recover post-backup change. | |||||
// Reserve a new key pair from key pool. | |||||
CPubKey vchPubKey; | |||||
bool ret; | |||||
ret = reservekey.GetReservedKey(vchPubKey, true); | |||||
if (!ret) { | |||||
strFailReason = _("Keypool ran out, please call " | |||||
"keypoolrefill first"); | |||||
return false; | |||||
} | |||||
scriptChange = GetScriptForDestination(vchPubKey.GetID()); | |||||
} | |||||
CTxOut newTxOut(nChange, scriptChange); | CTxOut newTxOut(nChange, scriptChange); | ||||
// We do not move dust-change to fees, because the sender would | // We do not move dust-change to fees, because the sender would | ||||
// end up paying more than requested. This would be against the | // end up paying more than requested. This would be against the | ||||
// purpose of the all-inclusive feature. So instead we raise the | // purpose of the all-inclusive feature. So instead we raise the | ||||
// change and deduct from the recipient. | // change and deduct from the recipient. | ||||
if (nSubtractFeeFromAmount > 0 && | if (nSubtractFeeFromAmount > 0 && | ||||
newTxOut.IsDust(dustRelayFee)) { | newTxOut.IsDust(dustRelayFee)) { | ||||
Show All 18 Lines | assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | ||||
} | } | ||||
} | } | ||||
// Never create dust outputs; if we would, just add the dust to | // Never create dust outputs; if we would, just add the dust to | ||||
// the fee. | // the fee. | ||||
if (newTxOut.IsDust(dustRelayFee)) { | if (newTxOut.IsDust(dustRelayFee)) { | ||||
nChangePosInOut = -1; | nChangePosInOut = -1; | ||||
nFeeRet += nChange; | nFeeRet += nChange; | ||||
reservekey.ReturnKey(); | |||||
} else { | } else { | ||||
if (nChangePosInOut == -1) { | if (nChangePosInOut == -1) { | ||||
// Insert change txn at random position: | // Insert change txn at random position: | ||||
nChangePosInOut = GetRandInt(txNew.vout.size() + 1); | nChangePosInOut = GetRandInt(txNew.vout.size() + 1); | ||||
} else if ((unsigned int)nChangePosInOut > | } else if ((unsigned int)nChangePosInOut > | ||||
txNew.vout.size()) { | txNew.vout.size()) { | ||||
strFailReason = _("Change index out of range"); | strFailReason = _("Change index out of range"); | ||||
return false; | return false; | ||||
} | } | ||||
std::vector<CTxOut>::iterator position = | std::vector<CTxOut>::iterator position = | ||||
txNew.vout.begin() + nChangePosInOut; | txNew.vout.begin() + nChangePosInOut; | ||||
txNew.vout.insert(position, newTxOut); | txNew.vout.insert(position, newTxOut); | ||||
} | } | ||||
} else { | } else { | ||||
reservekey.ReturnKey(); | |||||
nChangePosInOut = -1; | nChangePosInOut = -1; | ||||
} | } | ||||
// Fill vin | // Fill vin | ||||
// | // | ||||
// Note how the sequence number is set to non-maxint so that the | // Note how the sequence number is set to non-maxint so that the | ||||
// nLockTime set above actually works. | // nLockTime set above actually works. | ||||
for (const auto &coin : setCoins) { | for (const auto &coin : setCoins) { | ||||
Show All 36 Lines | assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | ||||
// allowed fee. | // allowed fee. | ||||
Amount minFee = GetConfig().GetMinFeePerKB().GetFeeCeiling(nBytes); | Amount minFee = GetConfig().GetMinFeePerKB().GetFeeCeiling(nBytes); | ||||
if (nFeeNeeded < minFee) { | if (nFeeNeeded < minFee) { | ||||
strFailReason = _("Transaction too large for fee policy"); | strFailReason = _("Transaction too large for fee policy"); | ||||
return false; | return false; | ||||
} | } | ||||
if (nFeeRet >= nFeeNeeded) { | if (nFeeRet >= nFeeNeeded) { | ||||
// Reduce fee to only the needed amount if we have change output | // Reduce fee to only the needed amount if possible. This | ||||
// to increase. This prevents potential overpayment in fees if | // prevents potential overpayment in fees if the coins selected | ||||
// the coins selected to meet nFeeNeeded result in a transaction | // to meet nFeeNeeded result in a transaction that requires less | ||||
// that requires less fee than the prior iteration. | // fee than the prior iteration. | ||||
// TODO: The case where nSubtractFeeFromAmount > 0 remains to be | // TODO: The case where nSubtractFeeFromAmount > 0 remains to be | ||||
// addressed because it requires returning the fee to the payees | // addressed because it requires returning the fee to the payees | ||||
// and not the change output. | // 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 we have no change and a big enough excess fee, then try to | ||||
// construct transaction again only without picking new inputs. | |||||
// We now know we only need the smaller fee (because of reduced | |||||
// tx size) and so we should add a change output. Only try this | |||||
// once. | |||||
Amount fee_needed_for_change = | |||||
GetMinimumFee(change_prototype_size, | |||||
currentConfirmationTarget, g_mempool); | |||||
Amount minimum_value_for_change = | |||||
change_prototype_txout.GetDustThreshold(dustRelayFee); | |||||
Amount max_excess_fee = | |||||
fee_needed_for_change + minimum_value_for_change; | |||||
if (nFeeRet > nFeeNeeded + max_excess_fee && | |||||
nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && | |||||
pick_new_inputs) { | |||||
pick_new_inputs = false; | |||||
nFeeRet = nFeeNeeded + fee_needed_for_change; | |||||
continue; | |||||
} | |||||
// If we have change output already, just increase it | |||||
if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && | if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && | ||||
nSubtractFeeFromAmount == 0) { | nSubtractFeeFromAmount == 0) { | ||||
Amount extraFeePaid = nFeeRet - nFeeNeeded; | Amount extraFeePaid = nFeeRet - nFeeNeeded; | ||||
std::vector<CTxOut>::iterator change_position = | std::vector<CTxOut>::iterator change_position = | ||||
txNew.vout.begin() + nChangePosInOut; | txNew.vout.begin() + nChangePosInOut; | ||||
change_position->nValue += extraFeePaid; | change_position->nValue += extraFeePaid; | ||||
nFeeRet -= extraFeePaid; | nFeeRet -= extraFeePaid; | ||||
} | } | ||||
// Done, enough fee included. | // Done, enough fee included. | ||||
break; | break; | ||||
} else if (!pick_new_inputs) { | |||||
// This shouldn't happen, we should have had enough excess fee | |||||
// to pay for the new output and still meet nFeeNeeded | |||||
strFailReason = | |||||
_("Transaction fee and change calculation failed"); | |||||
return false; | |||||
} | } | ||||
// Try to reduce change to include necessary fee. | // Try to reduce change to include necessary fee. | ||||
if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { | if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { | ||||
Amount additionalFeeNeeded = nFeeNeeded - nFeeRet; | Amount additionalFeeNeeded = nFeeNeeded - nFeeRet; | ||||
std::vector<CTxOut>::iterator change_position = | std::vector<CTxOut>::iterator change_position = | ||||
txNew.vout.begin() + nChangePosInOut; | txNew.vout.begin() + nChangePosInOut; | ||||
// Only reduce change if remaining amount is still a large | // Only reduce change if remaining amount is still a large | ||||
// enough output. | // enough output. | ||||
if (change_position->nValue >= | if (change_position->nValue >= | ||||
MIN_FINAL_CHANGE + additionalFeeNeeded) { | MIN_FINAL_CHANGE + additionalFeeNeeded) { | ||||
change_position->nValue -= additionalFeeNeeded; | change_position->nValue -= additionalFeeNeeded; | ||||
nFeeRet += additionalFeeNeeded; | nFeeRet += additionalFeeNeeded; | ||||
// Done, able to increase fee from change. | // Done, able to increase fee from change. | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
// Include more fee and try again. | // Include more fee and try again. | ||||
nFeeRet = nFeeNeeded; | nFeeRet = nFeeNeeded; | ||||
continue; | continue; | ||||
} | } | ||||
if (nChangePosInOut == -1) { | |||||
// Return any reserved key if we don't have change | |||||
reservekey.ReturnKey(); | |||||
} | |||||
if (sign) { | if (sign) { | ||||
SigHashType sigHashType = SigHashType().withForkId(); | SigHashType sigHashType = SigHashType().withForkId(); | ||||
CTransaction txNewConst(txNew); | CTransaction txNewConst(txNew); | ||||
int nIn = 0; | int nIn = 0; | ||||
for (const auto &coin : setCoins) { | for (const auto &coin : setCoins) { | ||||
const CScript &scriptPubKey = coin.txout.scriptPubKey; | const CScript &scriptPubKey = coin.txout.scriptPubKey; | ||||
SignatureData sigdata; | SignatureData sigdata; | ||||
▲ Show 20 Lines • Show All 1,276 Lines • Show Last 20 Lines |