Skip to content

Commit d30ed09

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 415814d commit d30ed09

File tree

3 files changed

+29
-56
lines changed

3 files changed

+29
-56
lines changed

src/wallet/rpc/spend.cpp

+21-26
Original file line numberDiff line numberDiff line change
@@ -489,13 +489,13 @@ static std::vector<RPCArg> FundTxDoc(bool solving_data = true)
489489
return args;
490490
}
491491

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

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

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

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

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

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

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

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

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

859858
return result;
860859
},
@@ -1272,18 +1271,16 @@ RPCHelpMan send()
12721271
PreventOutdatedOptions(options);
12731272

12741273

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

1286-
return FinishTransaction(pwallet, options, rawTx);
1283+
return FinishTransaction(pwallet, options, CMutableTransaction(*txr.tx));
12871284
}
12881285
};
12891286
}
@@ -1699,8 +1696,6 @@ RPCHelpMan walletcreatefundedpsbt()
16991696

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

1702-
CAmount fee;
1703-
int change_position;
17041699
const UniValue &replaceable_arg = options["replaceable"];
17051700
const bool rbf{replaceable_arg.isNull() ? wallet.m_signal_rbf : replaceable_arg.get_bool()};
17061701
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
@@ -1709,10 +1704,10 @@ RPCHelpMan walletcreatefundedpsbt()
17091704
// be overridden by options.add_inputs.
17101705
coin_control.m_allow_other_inputs = rawTx.vin.size() == 0;
17111706
SetOptionsInputWeights(request.params[0], options);
1712-
FundTransaction(wallet, rawTx, fee, change_position, options, coin_control, /*override_min_fee=*/true);
1707+
auto txr = FundTransaction(wallet, rawTx, options, coin_control, /*override_min_fee=*/true);
17131708

17141709
// Make a blank psbt
1715-
PartiallySignedTransaction psbtx(rawTx);
1710+
PartiallySignedTransaction psbtx(CMutableTransaction(*txr.tx));
17161711

17171712
// Fill transaction with out data but don't sign
17181713
bool bip32derivs = request.params[4].isNull() ? true : request.params[4].get_bool();
@@ -1728,8 +1723,8 @@ RPCHelpMan walletcreatefundedpsbt()
17281723

17291724
UniValue result(UniValue::VOBJ);
17301725
result.pushKV("psbt", EncodeBase64(ssTx.str()));
1731-
result.pushKV("fee", ValueFromAmount(fee));
1732-
result.pushKV("changepos", change_position);
1726+
result.pushKV("fee", ValueFromAmount(txr.fee));
1727+
result.pushKV("changepos", txr.change_pos ? (int)*txr.change_pos : -1);
17331728
return result;
17341729
},
17351730
};

src/wallet/spend.cpp

+7-29
Original file line numberDiff line numberDiff line change
@@ -1314,7 +1314,7 @@ util::Result<CreatedTransactionResult> CreateTransaction(
13141314
return res;
13151315
}
13161316

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

@@ -1348,8 +1348,7 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet,
13481348
PreselectedInput& preset_txin = coinControl.Select(outPoint);
13491349
if (!wallet.IsMine(outPoint)) {
13501350
if (coins[outPoint].out.IsNull()) {
1351-
error = _("Unable to find UTXO for external input");
1352-
return false;
1351+
return util::Error{_("Unable to find UTXO for external input")};
13531352
}
13541353

13551354
// The input was not in the wallet, but is in the UTXO set, so select as external
@@ -1360,38 +1359,17 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet,
13601359
preset_txin.SetScriptWitness(txin.scriptWitness);
13611360
}
13621361

1363-
auto res = CreateTransaction(wallet, vecSend, nChangePosInOut == -1 ? std::nullopt : std::optional<unsigned int>((unsigned int)nChangePosInOut), coinControl, false);
1362+
auto res = CreateTransaction(wallet, vecSend, change_pos, coinControl, false);
13641363
if (!res) {
1365-
error = util::ErrorString(res);
1366-
return false;
1367-
}
1368-
const auto& txr = *res;
1369-
CTransactionRef tx_new = txr.tx;
1370-
nFeeRet = txr.fee;
1371-
nChangePosInOut = txr.change_pos ? *txr.change_pos : -1;
1372-
1373-
if (nChangePosInOut != -1) {
1374-
tx.vout.insert(tx.vout.begin() + nChangePosInOut, tx_new->vout[nChangePosInOut]);
1375-
}
1376-
1377-
// Copy output sizes from new transaction; they may have had the fee
1378-
// subtracted from them.
1379-
for (unsigned int idx = 0; idx < tx.vout.size(); idx++) {
1380-
tx.vout[idx].nValue = tx_new->vout[idx].nValue;
1364+
return res;
13811365
}
13821366

1383-
// Add new txins while keeping original txin scriptSig/order.
1384-
for (const CTxIn& txin : tx_new->vin) {
1385-
if (!coinControl.IsSelected(txin.prevout)) {
1386-
tx.vin.push_back(txin);
1387-
1388-
}
1389-
if (lockUnspents) {
1367+
if (lockUnspents) {
1368+
for (const CTxIn& txin : res->tx->vin) {
13901369
wallet.LockCoin(txin.prevout);
13911370
}
1392-
13931371
}
13941372

1395-
return true;
1373+
return res;
13961374
}
13971375
} // namespace wallet

src/wallet/spend.h

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

228228
#endif // BITCOIN_WALLET_SPEND_H

0 commit comments

Comments
 (0)