|
|
|
@ -902,23 +902,20 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
|
|
|
|
|
}
|
|
|
|
|
TRACE5(coin_selection, selected_coins, wallet.GetName().c_str(), GetAlgorithmName(result->GetAlgo()).c_str(), result->GetTarget(), result->GetWaste(), result->GetSelectedValue());
|
|
|
|
|
|
|
|
|
|
// Always make a change output
|
|
|
|
|
// We will reduce the fee from this change output later, and remove the output if it is too small.
|
|
|
|
|
const CAmount change_and_fee = result->GetSelectedValue() - recipients_sum;
|
|
|
|
|
assert(change_and_fee >= 0);
|
|
|
|
|
CTxOut newTxOut(change_and_fee, scriptChange);
|
|
|
|
|
|
|
|
|
|
if (nChangePosInOut == -1) {
|
|
|
|
|
// Insert change txn at random position:
|
|
|
|
|
nChangePosInOut = rng_fast.randrange(txNew.vout.size() + 1);
|
|
|
|
|
}
|
|
|
|
|
else if ((unsigned int)nChangePosInOut > txNew.vout.size()) {
|
|
|
|
|
return util::Error{_("Transaction change output index out of range")};
|
|
|
|
|
const CAmount change_amount = result->GetChange(coin_selection_params.min_viable_change, coin_selection_params.m_change_fee);
|
|
|
|
|
if (change_amount > 0) {
|
|
|
|
|
CTxOut newTxOut(change_amount, scriptChange);
|
|
|
|
|
if (nChangePosInOut == -1) {
|
|
|
|
|
// Insert change txn at random position:
|
|
|
|
|
nChangePosInOut = rng_fast.randrange(txNew.vout.size() + 1);
|
|
|
|
|
} else if ((unsigned int)nChangePosInOut > txNew.vout.size()) {
|
|
|
|
|
return util::Error{_("Transaction change output index out of range")};
|
|
|
|
|
}
|
|
|
|
|
txNew.vout.insert(txNew.vout.begin() + nChangePosInOut, newTxOut);
|
|
|
|
|
} else {
|
|
|
|
|
nChangePosInOut = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(nChangePosInOut != -1);
|
|
|
|
|
auto change_position = txNew.vout.insert(txNew.vout.begin() + nChangePosInOut, newTxOut);
|
|
|
|
|
|
|
|
|
|
// Shuffle selected coins and fill in final vin
|
|
|
|
|
std::vector<COutput> selected_coins = result->GetShuffledInputVector();
|
|
|
|
|
|
|
|
|
@ -942,42 +939,23 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
|
|
|
|
|
if (nBytes == -1) {
|
|
|
|
|
return util::Error{_("Missing solving data for estimating transaction size")};
|
|
|
|
|
}
|
|
|
|
|
nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes);
|
|
|
|
|
|
|
|
|
|
// Subtract fee from the change output if not subtracting it from recipient outputs
|
|
|
|
|
CAmount fee_needed = nFeeRet;
|
|
|
|
|
if (!coin_selection_params.m_subtract_fee_outputs) {
|
|
|
|
|
change_position->nValue -= fee_needed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We want to drop the change to fees if:
|
|
|
|
|
// 1. The change output would be dust
|
|
|
|
|
// 2. The change is within the (almost) exact match window, i.e. it is less than or equal to the cost of the change output (cost_of_change)
|
|
|
|
|
CAmount change_amount = change_position->nValue;
|
|
|
|
|
if (IsDust(*change_position, coin_selection_params.m_discard_feerate) || change_amount <= coin_selection_params.m_cost_of_change)
|
|
|
|
|
{
|
|
|
|
|
nChangePosInOut = -1;
|
|
|
|
|
change_amount = 0;
|
|
|
|
|
txNew.vout.erase(change_position);
|
|
|
|
|
CAmount fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
|
|
|
|
|
nFeeRet = result->GetSelectedValue() - recipients_sum - change_amount;
|
|
|
|
|
|
|
|
|
|
// Because we have dropped this change, the tx size and required fee will be different, so let's recalculate those
|
|
|
|
|
tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, &coin_control);
|
|
|
|
|
nBytes = tx_sizes.vsize;
|
|
|
|
|
fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The only time that fee_needed should be less than the amount available for fees (in change_and_fee - change_amount) is when
|
|
|
|
|
// The only time that fee_needed should be less than the amount available for fees is when
|
|
|
|
|
// we are subtracting the fee from the outputs. If this occurs at any other time, it is a bug.
|
|
|
|
|
assert(coin_selection_params.m_subtract_fee_outputs || fee_needed <= change_and_fee - change_amount);
|
|
|
|
|
assert(coin_selection_params.m_subtract_fee_outputs || fee_needed <= nFeeRet);
|
|
|
|
|
|
|
|
|
|
// Update nFeeRet in case fee_needed changed due to dropping the change output
|
|
|
|
|
if (fee_needed <= change_and_fee - change_amount) {
|
|
|
|
|
nFeeRet = change_and_fee - change_amount;
|
|
|
|
|
// If there is a change output and we overpay the fees then increase the change to match the fee needed
|
|
|
|
|
if (nChangePosInOut != -1 && fee_needed < nFeeRet) {
|
|
|
|
|
auto& change = txNew.vout.at(nChangePosInOut);
|
|
|
|
|
change.nValue += nFeeRet - fee_needed;
|
|
|
|
|
nFeeRet = fee_needed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reduce output values for subtractFeeFromAmount
|
|
|
|
|
if (coin_selection_params.m_subtract_fee_outputs) {
|
|
|
|
|
CAmount to_reduce = fee_needed + change_amount - change_and_fee;
|
|
|
|
|
CAmount to_reduce = fee_needed - nFeeRet;
|
|
|
|
|
int i = 0;
|
|
|
|
|
bool fFirst = true;
|
|
|
|
|
for (const auto& recipient : vecSend)
|
|
|
|
|