From c5a5c822e38d6e95ea889c9047ea196f11c6a84f Mon Sep 17 00:00:00 2001 From: Peter Shugalev Date: Fri, 7 Jun 2024 11:30:23 +0200 Subject: [PATCH] Fixed fee calculation --- src/qt/sendcoinsdialog.cpp | 27 +++++++++++++++++---------- src/wallet/coincontrol.h | 3 +++ src/wallet/wallet.cpp | 2 +- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index d5b32b8644..d37d4e3fe6 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -320,10 +320,12 @@ void SendCoinsDialog::on_sendButton_clicked() bool fGoThroughTransparentAddress = false; __decltype(recipients) exchangeRecipients; CScript intermediateAddressScript; + CAmount extraFee = 0; if (fAnonymousMode && exchangeAddressCount > 0) { CAmount exchangeAddressAmount = 0; - bool fSubtractFeeFromIntermediateAddressAmount = false; + // if the transaction is performed in two stages through the intermediate address we need to calculate the size of the second transaction + uint32_t secondTxSize = 8 /*CTransaction: nVersion, nLockTime*/ + 1 /*vinSize*/ + 148 /*vin[0]*/ + 20 /*safety*/ + 1 /*voutSize*/; fGoThroughTransparentAddress = true; @@ -331,13 +333,12 @@ void SendCoinsDialog::on_sendButton_clicked() for(int i = 0; i < recipients.size(); ){ if (model->validateExchangeAddress(recipients[i].address)) { exchangeAddressAmount += recipients[i].amount; - recipients[i].fSubtractFeeFromAmount = true; + // we use different fee calculation system and therefore can't reliably do the calculation + // of fee for the second transaction if some of recipients have this flag set + recipients[i].fSubtractFeeFromAmount = false; exchangeRecipients.push_back(recipients[i]); - // It's not clear if we should subtract fee from the intermediate address amount if - // there are conflicting "fSubractFeeFromAmount" flags in the exchangeRecipients array. - // For now, we use the flag from the last exchange recipient. - fSubtractFeeFromIntermediateAddressAmount = recipients[i].fSubtractFeeFromAmount; + secondTxSize += 8 /*amount*/ + 1 /*scriptSize*/ + 26 /*scriptPubKey*/; recipients.erase(recipients.begin() + i); } @@ -359,10 +360,12 @@ void SendCoinsDialog::on_sendButton_clicked() pwalletMain->SetAddressBook(newKey.GetID(), "", "receive"); intermediateAddressScript = GetScriptForDestination(newKey.GetID()); + extraFee = CWallet::GetMinimumFee(secondTxSize, 0, mempool); + SendCoinsRecipient newRecipient; newRecipient.address = CBitcoinAddress(newKey.GetID()).ToString().c_str(); - newRecipient.amount = exchangeAddressAmount; - newRecipient.fSubtractFeeFromAmount = fSubtractFeeFromIntermediateAddressAmount; + newRecipient.amount = exchangeAddressAmount + extraFee; + newRecipient.fSubtractFeeFromAmount = false; recipients.push_back(newRecipient); } @@ -553,7 +556,10 @@ void SendCoinsDialog::on_sendButton_clicked() questionString.append(" (" + QString::number(txSize / 1000) + " kB)"); if (fGoThroughTransparentAddress) { - questionString.append(tr(". Note: the transaction will go through a transparent address, additional fees may apply.")); + questionString.append(tr(". Note: the transaction will go through a transparent address, fee for the second transaction is ")); + questionString.append(""); + questionString.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), extraFee)); + questionString.append("."); } } @@ -653,6 +659,7 @@ void SendCoinsDialog::on_sendButton_clicked() CCoinControl ctrl; ctrl.fAllowOtherInputs = false; + ctrl.fNoChange = true; ctrl.Select(outpoint); WalletModelTransaction secondTransaction(exchangeRecipients); @@ -668,7 +675,7 @@ void SendCoinsDialog::on_sendButton_clicked() return; } - sendStatus = model->sendCoins(currentTransaction); + sendStatus = model->sendCoins(secondTransaction); // process sendStatus and on error generate message shown to user processSendCoinsReturn(sendStatus); } diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h index 064a2b9d99..feed073c26 100644 --- a/src/wallet/coincontrol.h +++ b/src/wallet/coincontrol.h @@ -24,6 +24,8 @@ class CCoinControl { public: CTxDestination destChange; + //! If true, don't use any change + bool fNoChange; //! If false, allows unselected inputs, but requires all selected inputs be used bool fAllowOtherInputs; //! Includes watch only addresses which match the ISMINE_WATCH_SOLVABLE criteria @@ -49,6 +51,7 @@ class CCoinControl void SetNull() { destChange = CNoDestination(); + fNoChange = false; fAllowOtherInputs = false; fRequireAllInputs = true; fAllowWatchOnly = false; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 0aa4b89c96..b1f5715403 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4450,7 +4450,7 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CWalletT const CAmount nChange = nValueIn - nValueToSelect; CTxOut newTxOut; - if (nChange > 0) + if (nChange > 0 && !(coinControl && coinControl->fNoChange)) { // Fill a vout to ourself // TODO: pass in scriptChange instead of reservekey so