wallet: use GetChange() in tx building

This commit is contained in:
S3RK 2022-07-13 08:54:44 +02:00
parent 15e97a6886
commit 87e0ef9031

View file

@ -902,22 +902,19 @@ 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()); 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 const CAmount change_amount = result->GetChange(coin_selection_params.min_viable_change, coin_selection_params.m_change_fee);
// We will reduce the fee from this change output later, and remove the output if it is too small. if (change_amount > 0) {
const CAmount change_and_fee = result->GetSelectedValue() - recipients_sum; CTxOut newTxOut(change_amount, scriptChange);
assert(change_and_fee >= 0); if (nChangePosInOut == -1) {
CTxOut newTxOut(change_and_fee, scriptChange); // Insert change txn at random position:
nChangePosInOut = rng_fast.randrange(txNew.vout.size() + 1);
if (nChangePosInOut == -1) { } else if ((unsigned int)nChangePosInOut > txNew.vout.size()) {
// Insert change txn at random position: return util::Error{_("Transaction change output index out of range")};
nChangePosInOut = rng_fast.randrange(txNew.vout.size() + 1); }
txNew.vout.insert(txNew.vout.begin() + nChangePosInOut, newTxOut);
} else {
nChangePosInOut = -1;
} }
else if ((unsigned int)nChangePosInOut > txNew.vout.size()) {
return util::Error{_("Transaction change output index out of range")};
}
assert(nChangePosInOut != -1);
auto change_position = txNew.vout.insert(txNew.vout.begin() + nChangePosInOut, newTxOut);
// Shuffle selected coins and fill in final vin // Shuffle selected coins and fill in final vin
std::vector<COutput> selected_coins = result->GetShuffledInputVector(); std::vector<COutput> selected_coins = result->GetShuffledInputVector();
@ -942,42 +939,23 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
if (nBytes == -1) { if (nBytes == -1) {
return util::Error{_("Missing solving data for estimating transaction size")}; return util::Error{_("Missing solving data for estimating transaction size")};
} }
nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes); CAmount fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
nFeeRet = result->GetSelectedValue() - recipients_sum - change_amount;
// Subtract fee from the change output if not subtracting it from recipient outputs // The only time that fee_needed should be less than the amount available for fees is when
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);
// 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
// we are subtracting the fee from the outputs. If this occurs at any other time, it is a bug. // 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 there is a change output and we overpay the fees then increase the change to match the fee needed
if (fee_needed <= change_and_fee - change_amount) { if (nChangePosInOut != -1 && fee_needed < nFeeRet) {
nFeeRet = change_and_fee - change_amount; auto& change = txNew.vout.at(nChangePosInOut);
change.nValue += nFeeRet - fee_needed;
nFeeRet = fee_needed;
} }
// Reduce output values for subtractFeeFromAmount // Reduce output values for subtractFeeFromAmount
if (coin_selection_params.m_subtract_fee_outputs) { 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; int i = 0;
bool fFirst = true; bool fFirst = true;
for (const auto& recipient : vecSend) for (const auto& recipient : vecSend)