@@ -656,19 +656,22 @@ static void DiscourageFeeSniping(CMutableTransaction& tx, FastRandomContext& rng
656656 }
657657}
658658
659- static bool CreateTransactionInternal (
659+ static std::optional<CreatedTransactionResult> CreateTransactionInternal (
660660 CWallet& wallet,
661661 const std::vector<CRecipient>& vecSend,
662- CTransactionRef& tx,
663- CAmount& nFeeRet,
664- int & nChangePosInOut,
662+ int change_pos,
665663 bilingual_str& error,
666664 const CCoinControl& coin_control,
667665 FeeCalculation& fee_calc_out,
668666 bool sign) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
669667{
670668 AssertLockHeld (wallet.cs_wallet );
671669
670+ // out variables, to be packed into returned result structure
671+ CTransactionRef tx;
672+ CAmount nFeeRet;
673+ int nChangePosInOut = change_pos;
674+
672675 FastRandomContext rng_fast;
673676 CMutableTransaction txNew; // The resulting transaction that we make
674677
@@ -742,12 +745,12 @@ static bool CreateTransactionInternal(
742745 // provided one
743746 if (coin_control.m_feerate && coin_selection_params.m_effective_feerate > *coin_control.m_feerate ) {
744747 error = strprintf (_ (" Fee rate (%s) is lower than the minimum fee rate setting (%s)" ), coin_control.m_feerate ->ToString (FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate .ToString (FeeEstimateMode::SAT_VB));
745- return false ;
748+ return std:: nullopt ;
746749 }
747750 if (feeCalc.reason == FeeReason::FALLBACK && !wallet.m_allow_fallback_fee ) {
748751 // eventually allow a fallback fee
749752 error = _ (" Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee." );
750- return false ;
753+ return std:: nullopt ;
751754 }
752755
753756 // Calculate the cost of change
@@ -774,7 +777,7 @@ static bool CreateTransactionInternal(
774777 if (IsDust (txout, wallet.chain ().relayDustFee ()))
775778 {
776779 error = _ (" Transaction amount too small" );
777- return false ;
780+ return std:: nullopt ;
778781 }
779782 txNew.vout .push_back (txout);
780783 }
@@ -791,7 +794,7 @@ static bool CreateTransactionInternal(
791794 std::optional<SelectionResult> result = SelectCoins (wallet, vAvailableCoins, /* nTargetValue=*/ selection_target, coin_control, coin_selection_params);
792795 if (!result) {
793796 error = _ (" Insufficient funds" );
794- return false ;
797+ return std:: nullopt ;
795798 }
796799 TRACE5 (coin_selection, selected_coins, wallet.GetName ().c_str (), GetAlgorithmName (result->m_algo ).c_str (), result->m_target , result->GetWaste (), result->GetSelectedValue ());
797800
@@ -808,7 +811,7 @@ static bool CreateTransactionInternal(
808811 else if ((unsigned int )nChangePosInOut > txNew.vout .size ())
809812 {
810813 error = _ (" Transaction change output index out of range" );
811- return false ;
814+ return std:: nullopt ;
812815 }
813816
814817 assert (nChangePosInOut != -1 );
@@ -836,7 +839,7 @@ static bool CreateTransactionInternal(
836839 int nBytes = tx_sizes.vsize ;
837840 if (nBytes == -1 ) {
838841 error = _ (" Missing solving data for estimating transaction size" );
839- return false ;
842+ return std:: nullopt ;
840843 }
841844 nFeeRet = coin_selection_params.m_effective_feerate .GetFee (nBytes);
842845
@@ -900,7 +903,7 @@ static bool CreateTransactionInternal(
900903 } else {
901904 error = _ (" The transaction amount is too small to send after the fee has been deducted" );
902905 }
903- return false ;
906+ return std:: nullopt ;
904907 }
905908 }
906909 ++i;
@@ -910,12 +913,12 @@ static bool CreateTransactionInternal(
910913
911914 // Give up if change keypool ran out and change is required
912915 if (scriptChange.empty () && nChangePosInOut != -1 ) {
913- return false ;
916+ return std:: nullopt ;
914917 }
915918
916919 if (sign && !wallet.SignTransaction (txNew)) {
917920 error = _ (" Signing transaction failed" );
918- return false ;
921+ return std:: nullopt ;
919922 }
920923
921924 // Return the constructed transaction data.
@@ -926,19 +929,19 @@ static bool CreateTransactionInternal(
926929 (!sign && tx_sizes.weight > MAX_STANDARD_TX_WEIGHT))
927930 {
928931 error = _ (" Transaction too large" );
929- return false ;
932+ return std:: nullopt ;
930933 }
931934
932935 if (nFeeRet > wallet.m_default_max_tx_fee ) {
933936 error = TransactionErrorString (TransactionError::MAX_FEE_EXCEEDED);
934- return false ;
937+ return std:: nullopt ;
935938 }
936939
937940 if (gArgs .GetBoolArg (" -walletrejectlongchains" , DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
938941 // Lastly, ensure this tx will pass the mempool's chain limits
939942 if (!wallet.chain ().checkChainLimits (tx)) {
940943 error = _ (" Transaction has too long of a mempool chain" );
941- return false ;
944+ return std:: nullopt ;
942945 }
943946 }
944947
@@ -955,58 +958,51 @@ static bool CreateTransactionInternal(
955958 feeCalc.est .fail .start , feeCalc.est .fail .end ,
956959 (feeCalc.est .fail .totalConfirmed + feeCalc.est .fail .inMempool + feeCalc.est .fail .leftMempool ) > 0.0 ? 100 * feeCalc.est .fail .withinTarget / (feeCalc.est .fail .totalConfirmed + feeCalc.est .fail .inMempool + feeCalc.est .fail .leftMempool ) : 0.0 ,
957960 feeCalc.est .fail .withinTarget , feeCalc.est .fail .totalConfirmed , feeCalc.est .fail .inMempool , feeCalc.est .fail .leftMempool );
958- return true ;
961+ return CreatedTransactionResult (tx, nFeeRet, nChangePosInOut) ;
959962}
960963
961- bool CreateTransaction (
964+ std::optional<CreatedTransactionResult> CreateTransaction (
962965 CWallet& wallet,
963966 const std::vector<CRecipient>& vecSend,
964- CTransactionRef& tx,
965- CAmount& nFeeRet,
966- int & nChangePosInOut,
967+ int change_pos,
967968 bilingual_str& error,
968969 const CCoinControl& coin_control,
969970 FeeCalculation& fee_calc_out,
970971 bool sign)
971972{
972973 if (vecSend.empty ()) {
973974 error = _ (" Transaction must have at least one recipient" );
974- return false ;
975+ return std:: nullopt ;
975976 }
976977
977978 if (std::any_of (vecSend.cbegin (), vecSend.cend (), [](const auto & recipient){ return recipient.nAmount < 0 ; })) {
978979 error = _ (" Transaction amounts must not be negative" );
979- return false ;
980+ return std:: nullopt ;
980981 }
981982
982983 LOCK (wallet.cs_wallet );
983984
984- int nChangePosIn = nChangePosInOut ;
985- Assert (!tx); // tx is an out-param. TODO change the return type from bool to tx (or nullptr)
986- bool res = CreateTransactionInternal (wallet, vecSend, tx, nFeeRet, nChangePosInOut, error, coin_control, fee_calc_out, sign );
987- TRACE4 (coin_selection, normal_create_tx_internal, wallet. GetName (). c_str (), res, nFeeRet, nChangePosInOut) ;
985+ std::optional<CreatedTransactionResult> txr_ungrouped = CreateTransactionInternal (wallet, vecSend, change_pos, error, coin_control, fee_calc_out, sign) ;
986+ TRACE4 (coin_selection, normal_create_tx_internal, wallet. GetName (). c_str (), txr_ungrouped. has_value (),
987+ txr_ungrouped. has_value () ? txr_ungrouped-> fee : 0 , txr_ungrouped. has_value () ? txr_ungrouped-> change_pos : 0 );
988+ if (!txr_ungrouped) return std:: nullopt ;
988989 // try with avoidpartialspends unless it's enabled already
989- if (res && nFeeRet > 0 /* 0 means non-functional fee rate estimation */ && wallet.m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends ) {
990+ if (txr_ungrouped-> fee > 0 /* 0 means non-functional fee rate estimation */ && wallet.m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends ) {
990991 TRACE1 (coin_selection, attempting_aps_create_tx, wallet.GetName ().c_str ());
991992 CCoinControl tmp_cc = coin_control;
992993 tmp_cc.m_avoid_partial_spends = true ;
993- CAmount nFeeRet2;
994- CTransactionRef tx2;
995- int nChangePosInOut2 = nChangePosIn;
996994 bilingual_str error2; // fired and forgotten; if an error occurs, we discard the results
997- if (CreateTransactionInternal (wallet, vecSend, tx2, nFeeRet2, nChangePosInOut2, error2, tmp_cc, fee_calc_out, sign)) {
995+ std::optional<CreatedTransactionResult> txr_grouped = CreateTransactionInternal (wallet, vecSend, change_pos, error2, tmp_cc, fee_calc_out, sign);
996+ if (txr_grouped) {
998997 // if fee of this alternative one is within the range of the max fee, we use this one
999- const bool use_aps = nFeeRet2 <= nFeeRet + wallet.m_max_aps_fee ;
1000- wallet.WalletLogPrintf (" Fee non-grouped = %lld, grouped = %lld, using %s\n " , nFeeRet, nFeeRet2, use_aps ? " grouped" : " non-grouped" );
1001- TRACE5 (coin_selection, aps_create_tx_internal, wallet.GetName ().c_str (), use_aps, res, nFeeRet2, nChangePosInOut2);
1002- if (use_aps) {
1003- tx = tx2;
1004- nFeeRet = nFeeRet2;
1005- nChangePosInOut = nChangePosInOut2;
1006- }
998+ const bool use_aps = txr_grouped->fee <= txr_ungrouped->fee + wallet.m_max_aps_fee ;
999+ wallet.WalletLogPrintf (" Fee non-grouped = %lld, grouped = %lld, using %s\n " ,
1000+ txr_ungrouped->fee , txr_grouped->fee , use_aps ? " grouped" : " non-grouped" );
1001+ TRACE5 (coin_selection, aps_create_tx_internal, wallet.GetName ().c_str (), use_aps, true , txr_grouped->fee , txr_grouped->change_pos );
1002+ if (use_aps) return txr_grouped;
10071003 }
10081004 }
1009- return res ;
1005+ return txr_ungrouped ;
10101006}
10111007
10121008bool FundTransaction (CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet, int & nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int >& setSubtractFeeFromOutputs, CCoinControl coinControl)
@@ -1030,11 +1026,12 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet,
10301026 // CreateTransaction call and LockCoin calls (when lockUnspents is true).
10311027 LOCK (wallet.cs_wallet );
10321028
1033- CTransactionRef tx_new;
10341029 FeeCalculation fee_calc_out;
1035- if (!CreateTransaction (wallet, vecSend, tx_new, nFeeRet, nChangePosInOut, error, coinControl, fee_calc_out, false )) {
1036- return false ;
1037- }
1030+ std::optional<CreatedTransactionResult> txr = CreateTransaction (wallet, vecSend, nChangePosInOut, error, coinControl, fee_calc_out, false );
1031+ if (!txr) return false ;
1032+ CTransactionRef tx_new = txr->tx ;
1033+ nFeeRet = txr->fee ;
1034+ nChangePosInOut = txr->change_pos ;
10381035
10391036 if (nChangePosInOut != -1 ) {
10401037 tx.vout .insert (tx.vout .begin () + nChangePosInOut, tx_new->vout [nChangePosInOut]);
0 commit comments