Skip to content

Commit 0295b44

Browse files
committed
wallet: return CreatedTransactionResult from FundTransaction
Instead of using the output parameters, return CreatedTransactionResult from FundTransaction in the same way that CreateTransaction does. Additionally, instead of modifying the original CMutableTransaction, the result from CreateTransactionInternal is used.
1 parent 758501b commit 0295b44

File tree

4 files changed

+30
-58
lines changed

4 files changed

+30
-58
lines changed

src/wallet/rpc/spend.cpp

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -488,13 +488,13 @@ static std::vector<RPCArg> FundTxDoc(bool solving_data = true)
488488
return args;
489489
}
490490

491-
void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out, int& change_position, const UniValue& options, CCoinControl& coinControl, bool override_min_fee)
491+
CreatedTransactionResult FundTransaction(CWallet& wallet, const CMutableTransaction& tx, const UniValue& options, CCoinControl& coinControl, bool override_min_fee)
492492
{
493493
// Make sure the results are valid at least up to the most recent block
494494
// the user could have gotten from another RPC command prior to now
495495
wallet.BlockUntilSyncedToCurrentChain();
496496

497-
change_position = -1;
497+
std::optional<unsigned int> change_position;
498498
bool lockUnspents = false;
499499
UniValue subtractFeeFromOutputs;
500500
std::set<int> setSubtractFeeFromOutputs;
@@ -552,7 +552,11 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
552552
}
553553

554554
if (options.exists("changePosition") || options.exists("change_position")) {
555-
change_position = (options.exists("change_position") ? options["change_position"] : options["changePosition"]).getInt<int>();
555+
int pos = (options.exists("change_position") ? options["change_position"] : options["changePosition"]).getInt<int>();
556+
if (pos < 0 || (unsigned int)pos > tx.vout.size()) {
557+
throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds");
558+
}
559+
change_position = (unsigned int)pos;
556560
}
557561

558562
if (options.exists("change_type")) {
@@ -702,9 +706,6 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
702706
if (tx.vout.size() == 0)
703707
throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output");
704708

705-
if (change_position != -1 && (change_position < 0 || (unsigned int)change_position > tx.vout.size()))
706-
throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds");
707-
708709
for (unsigned int idx = 0; idx < subtractFeeFromOutputs.size(); idx++) {
709710
int pos = subtractFeeFromOutputs[idx].getInt<int>();
710711
if (setSubtractFeeFromOutputs.count(pos))
@@ -716,11 +717,11 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
716717
setSubtractFeeFromOutputs.insert(pos);
717718
}
718719

719-
bilingual_str error;
720-
721-
if (!FundTransaction(wallet, tx, fee_out, change_position, error, lockUnspents, setSubtractFeeFromOutputs, coinControl)) {
722-
throw JSONRPCError(RPC_WALLET_ERROR, error.original);
720+
auto txr = FundTransaction(wallet, tx, change_position, lockUnspents, setSubtractFeeFromOutputs, coinControl);
721+
if (!txr) {
722+
throw JSONRPCError(RPC_WALLET_ERROR, ErrorString(txr).original);
723723
}
724+
return *txr;
724725
}
725726

726727
static void SetOptionsInputWeights(const UniValue& inputs, UniValue& options)
@@ -843,17 +844,15 @@ RPCHelpMan fundrawtransaction()
843844
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
844845
}
845846

846-
CAmount fee;
847-
int change_position;
848847
CCoinControl coin_control;
849848
// Automatically select (additional) coins. Can be overridden by options.add_inputs.
850849
coin_control.m_allow_other_inputs = true;
851-
FundTransaction(*pwallet, tx, fee, change_position, request.params[1], coin_control, /*override_min_fee=*/true);
850+
auto txr = FundTransaction(*pwallet, tx, request.params[1], coin_control, /*override_min_fee=*/true);
852851

853852
UniValue result(UniValue::VOBJ);
854-
result.pushKV("hex", EncodeHexTx(CTransaction(tx)));
855-
result.pushKV("fee", ValueFromAmount(fee));
856-
result.pushKV("changepos", change_position);
853+
result.pushKV("hex", EncodeHexTx(*txr.tx));
854+
result.pushKV("fee", ValueFromAmount(txr.fee));
855+
result.pushKV("changepos", txr.change_pos ? (int)*txr.change_pos : -1);
857856

858857
return result;
859858
},
@@ -1275,18 +1274,16 @@ RPCHelpMan send()
12751274
PreventOutdatedOptions(options);
12761275

12771276

1278-
CAmount fee;
1279-
int change_position;
12801277
bool rbf{options.exists("replaceable") ? options["replaceable"].get_bool() : pwallet->m_signal_rbf};
12811278
CMutableTransaction rawTx = ConstructTransaction(options["inputs"], request.params[0], options["locktime"], rbf);
12821279
CCoinControl coin_control;
12831280
// Automatically select coins, unless at least one is manually selected. Can
12841281
// be overridden by options.add_inputs.
12851282
coin_control.m_allow_other_inputs = rawTx.vin.size() == 0;
12861283
SetOptionsInputWeights(options["inputs"], options);
1287-
FundTransaction(*pwallet, rawTx, fee, change_position, options, coin_control, /*override_min_fee=*/false);
1284+
auto txr = FundTransaction(*pwallet, rawTx, options, coin_control, /*override_min_fee=*/false);
12881285

1289-
return FinishTransaction(pwallet, options, rawTx);
1286+
return FinishTransaction(pwallet, options, CMutableTransaction(*txr.tx));
12901287
}
12911288
};
12921289
}
@@ -1711,8 +1708,6 @@ RPCHelpMan walletcreatefundedpsbt()
17111708

17121709
UniValue options{request.params[3].isNull() ? UniValue::VOBJ : request.params[3]};
17131710

1714-
CAmount fee;
1715-
int change_position;
17161711
const UniValue &replaceable_arg = options["replaceable"];
17171712
const bool rbf{replaceable_arg.isNull() ? wallet.m_signal_rbf : replaceable_arg.get_bool()};
17181713
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
@@ -1721,10 +1716,10 @@ RPCHelpMan walletcreatefundedpsbt()
17211716
// be overridden by options.add_inputs.
17221717
coin_control.m_allow_other_inputs = rawTx.vin.size() == 0;
17231718
SetOptionsInputWeights(request.params[0], options);
1724-
FundTransaction(wallet, rawTx, fee, change_position, options, coin_control, /*override_min_fee=*/true);
1719+
auto txr = FundTransaction(wallet, rawTx, options, coin_control, /*override_min_fee=*/true);
17251720

17261721
// Make a blank psbt
1727-
PartiallySignedTransaction psbtx(rawTx);
1722+
PartiallySignedTransaction psbtx(CMutableTransaction(*txr.tx));
17281723

17291724
// Fill transaction with out data but don't sign
17301725
bool bip32derivs = request.params[4].isNull() ? true : request.params[4].get_bool();
@@ -1740,8 +1735,8 @@ RPCHelpMan walletcreatefundedpsbt()
17401735

17411736
UniValue result(UniValue::VOBJ);
17421737
result.pushKV("psbt", EncodeBase64(ssTx.str()));
1743-
result.pushKV("fee", ValueFromAmount(fee));
1744-
result.pushKV("changepos", change_position);
1738+
result.pushKV("fee", ValueFromAmount(txr.fee));
1739+
result.pushKV("changepos", txr.change_pos ? (int)*txr.change_pos : -1);
17451740
return result;
17461741
},
17471742
};

src/wallet/spend.cpp

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,7 +1360,7 @@ util::Result<CreatedTransactionResult> CreateTransaction(
13601360
return res;
13611361
}
13621362

1363-
bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl)
1363+
util::Result<CreatedTransactionResult> FundTransaction(CWallet& wallet, const CMutableTransaction& tx, std::optional<unsigned int> change_pos, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl)
13641364
{
13651365
std::vector<CRecipient> vecSend;
13661366

@@ -1396,8 +1396,7 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet,
13961396
PreselectedInput& preset_txin = coinControl.Select(outPoint);
13971397
if (!wallet.IsMine(outPoint)) {
13981398
if (coins[outPoint].out.IsNull()) {
1399-
error = _("Unable to find UTXO for external input");
1400-
return false;
1399+
return util::Error{_("Unable to find UTXO for external input")};
14011400
}
14021401

14031402
// The input was not in the wallet, but is in the UTXO set, so select as external
@@ -1408,38 +1407,17 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet,
14081407
preset_txin.SetScriptWitness(txin.scriptWitness);
14091408
}
14101409

1411-
auto res = CreateTransaction(wallet, vecSend, nChangePosInOut == -1 ? std::nullopt : std::optional<unsigned int>((unsigned int)nChangePosInOut), coinControl, false);
1410+
auto res = CreateTransaction(wallet, vecSend, change_pos, coinControl, false);
14121411
if (!res) {
1413-
error = util::ErrorString(res);
1414-
return false;
1415-
}
1416-
const auto& txr = *res;
1417-
CTransactionRef tx_new = txr.tx;
1418-
nFeeRet = txr.fee;
1419-
nChangePosInOut = txr.change_pos ? *txr.change_pos : -1;
1420-
1421-
if (nChangePosInOut != -1) {
1422-
tx.vout.insert(tx.vout.begin() + nChangePosInOut, tx_new->vout[nChangePosInOut]);
1423-
}
1424-
1425-
// Copy output sizes from new transaction; they may have had the fee
1426-
// subtracted from them.
1427-
for (unsigned int idx = 0; idx < tx.vout.size(); idx++) {
1428-
tx.vout[idx].nValue = tx_new->vout[idx].nValue;
1412+
return res;
14291413
}
14301414

1431-
// Add new txins while keeping original txin scriptSig/order.
1432-
for (const CTxIn& txin : tx_new->vin) {
1433-
if (!coinControl.IsSelected(txin.prevout)) {
1434-
tx.vin.push_back(txin);
1435-
1436-
}
1437-
if (lockUnspents) {
1415+
if (lockUnspents) {
1416+
for (const CTxIn& txin : res->tx->vin) {
14381417
wallet.LockCoin(txin.prevout);
14391418
}
1440-
14411419
}
14421420

1443-
return true;
1421+
return res;
14441422
}
14451423
} // namespace wallet

src/wallet/spend.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ util::Result<CreatedTransactionResult> CreateTransaction(CWallet& wallet, const
224224
* Insert additional inputs into the transaction by
225225
* calling CreateTransaction();
226226
*/
227-
bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl);
227+
util::Result<CreatedTransactionResult> FundTransaction(CWallet& wallet, const CMutableTransaction& tx, std::optional<unsigned int> change_pos, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl);
228228
} // namespace wallet
229229

230230
#endif // BITCOIN_WALLET_SPEND_H

src/wallet/test/fuzz/notifications.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,10 +156,9 @@ struct FuzzedWallet {
156156
coin_control.fOverrideFeeRate = fuzzed_data_provider.ConsumeBool();
157157
// Add solving data (m_external_provider and SelectExternal)?
158158

159-
CAmount fee_out;
160159
int change_position{fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, tx.vout.size() - 1)};
161160
bilingual_str error;
162-
(void)FundTransaction(*wallet, tx, fee_out, change_position, error, /*lockUnspents=*/false, subtract_fee_from_outputs, coin_control);
161+
(void)FundTransaction(*wallet, tx, change_position, /*lockUnspents=*/false, subtract_fee_from_outputs, coin_control);
163162
}
164163
};
165164

0 commit comments

Comments
 (0)