Skip to content

Commit 87e0ef9

Browse files
committed
wallet: use GetChange() in tx building
1 parent 15e97a6 commit 87e0ef9

File tree

1 file changed

+22
-44
lines changed

1 file changed

+22
-44
lines changed

src/wallet/spend.cpp

Lines changed: 22 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -902,23 +902,20 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
902902
}
903903
TRACE5(coin_selection, selected_coins, wallet.GetName().c_str(), GetAlgorithmName(result->GetAlgo()).c_str(), result->GetTarget(), result->GetWaste(), result->GetSelectedValue());
904904

905-
// Always make a change output
906-
// We will reduce the fee from this change output later, and remove the output if it is too small.
907-
const CAmount change_and_fee = result->GetSelectedValue() - recipients_sum;
908-
assert(change_and_fee >= 0);
909-
CTxOut newTxOut(change_and_fee, scriptChange);
910-
911-
if (nChangePosInOut == -1) {
912-
// Insert change txn at random position:
913-
nChangePosInOut = rng_fast.randrange(txNew.vout.size() + 1);
914-
}
915-
else if ((unsigned int)nChangePosInOut > txNew.vout.size()) {
916-
return util::Error{_("Transaction change output index out of range")};
905+
const CAmount change_amount = result->GetChange(coin_selection_params.min_viable_change, coin_selection_params.m_change_fee);
906+
if (change_amount > 0) {
907+
CTxOut newTxOut(change_amount, scriptChange);
908+
if (nChangePosInOut == -1) {
909+
// Insert change txn at random position:
910+
nChangePosInOut = rng_fast.randrange(txNew.vout.size() + 1);
911+
} else if ((unsigned int)nChangePosInOut > txNew.vout.size()) {
912+
return util::Error{_("Transaction change output index out of range")};
913+
}
914+
txNew.vout.insert(txNew.vout.begin() + nChangePosInOut, newTxOut);
915+
} else {
916+
nChangePosInOut = -1;
917917
}
918918

919-
assert(nChangePosInOut != -1);
920-
auto change_position = txNew.vout.insert(txNew.vout.begin() + nChangePosInOut, newTxOut);
921-
922919
// Shuffle selected coins and fill in final vin
923920
std::vector<COutput> selected_coins = result->GetShuffledInputVector();
924921

@@ -942,42 +939,23 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
942939
if (nBytes == -1) {
943940
return util::Error{_("Missing solving data for estimating transaction size")};
944941
}
945-
nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes);
946-
947-
// Subtract fee from the change output if not subtracting it from recipient outputs
948-
CAmount fee_needed = nFeeRet;
949-
if (!coin_selection_params.m_subtract_fee_outputs) {
950-
change_position->nValue -= fee_needed;
951-
}
952-
953-
// We want to drop the change to fees if:
954-
// 1. The change output would be dust
955-
// 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)
956-
CAmount change_amount = change_position->nValue;
957-
if (IsDust(*change_position, coin_selection_params.m_discard_feerate) || change_amount <= coin_selection_params.m_cost_of_change)
958-
{
959-
nChangePosInOut = -1;
960-
change_amount = 0;
961-
txNew.vout.erase(change_position);
942+
CAmount fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
943+
nFeeRet = result->GetSelectedValue() - recipients_sum - change_amount;
962944

963-
// Because we have dropped this change, the tx size and required fee will be different, so let's recalculate those
964-
tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, &coin_control);
965-
nBytes = tx_sizes.vsize;
966-
fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes);
967-
}
968-
969-
// The only time that fee_needed should be less than the amount available for fees (in change_and_fee - change_amount) is when
945+
// The only time that fee_needed should be less than the amount available for fees is when
970946
// we are subtracting the fee from the outputs. If this occurs at any other time, it is a bug.
971-
assert(coin_selection_params.m_subtract_fee_outputs || fee_needed <= change_and_fee - change_amount);
947+
assert(coin_selection_params.m_subtract_fee_outputs || fee_needed <= nFeeRet);
972948

973-
// Update nFeeRet in case fee_needed changed due to dropping the change output
974-
if (fee_needed <= change_and_fee - change_amount) {
975-
nFeeRet = change_and_fee - change_amount;
949+
// If there is a change output and we overpay the fees then increase the change to match the fee needed
950+
if (nChangePosInOut != -1 && fee_needed < nFeeRet) {
951+
auto& change = txNew.vout.at(nChangePosInOut);
952+
change.nValue += nFeeRet - fee_needed;
953+
nFeeRet = fee_needed;
976954
}
977955

978956
// Reduce output values for subtractFeeFromAmount
979957
if (coin_selection_params.m_subtract_fee_outputs) {
980-
CAmount to_reduce = fee_needed + change_amount - change_and_fee;
958+
CAmount to_reduce = fee_needed - nFeeRet;
981959
int i = 0;
982960
bool fFirst = true;
983961
for (const auto& recipient : vecSend)

0 commit comments

Comments
 (0)