@@ -74,6 +74,16 @@ std::set<int> InterpretSubtractFeeFromOutputInstructions(const UniValue& sffo_in
74
74
}
75
75
}
76
76
}
77
+ if (sffo.isNum ()) {
78
+ int pos = sffo.getInt <int >();
79
+ if (sffo_set.contains (pos))
80
+ throw JSONRPCError (RPC_INVALID_PARAMETER, strprintf (" Invalid parameter, duplicated position: %d" , pos));
81
+ if (pos < 0 )
82
+ throw JSONRPCError (RPC_INVALID_PARAMETER, strprintf (" Invalid parameter, negative position: %d" , pos));
83
+ if (pos >= int (destinations.size ()))
84
+ throw JSONRPCError (RPC_INVALID_PARAMETER, strprintf (" Invalid parameter, position too large: %d" , pos));
85
+ sffo_set.insert (pos);
86
+ }
77
87
}
78
88
return sffo_set;
79
89
}
@@ -480,17 +490,17 @@ static std::vector<RPCArg> FundTxDoc(bool solving_data = true)
480
490
return args;
481
491
}
482
492
483
- CreatedTransactionResult FundTransaction (CWallet& wallet, const CMutableTransaction& tx, const UniValue& options, CCoinControl& coinControl, bool override_min_fee)
493
+ CreatedTransactionResult FundTransaction (CWallet& wallet, const CMutableTransaction& tx, const std::vector<CRecipient>& recipients, const UniValue& options, CCoinControl& coinControl, bool override_min_fee)
484
494
{
495
+ // We want to make sure tx.vout is not used now that we are passing outputs as a vector of recipients.
496
+ // This sets us up to remove tx completely in a future PR in favor of passing the inputs directly.
497
+ CHECK_NONFATAL (tx.vout .empty ());
485
498
// Make sure the results are valid at least up to the most recent block
486
499
// the user could have gotten from another RPC command prior to now
487
500
wallet.BlockUntilSyncedToCurrentChain ();
488
501
489
502
std::optional<unsigned int > change_position;
490
503
bool lockUnspents = false ;
491
- UniValue subtractFeeFromOutputs;
492
- std::set<int > setSubtractFeeFromOutputs;
493
-
494
504
if (!options.isNull ()) {
495
505
if (options.type () == UniValue::VBOOL) {
496
506
// backward compatibility bool only fallback
@@ -545,7 +555,7 @@ CreatedTransactionResult FundTransaction(CWallet& wallet, const CMutableTransact
545
555
546
556
if (options.exists (" changePosition" ) || options.exists (" change_position" )) {
547
557
int pos = (options.exists (" change_position" ) ? options[" change_position" ] : options[" changePosition" ]).getInt <int >();
548
- if (pos < 0 || (unsigned int )pos > tx. vout .size ()) {
558
+ if (pos < 0 || (unsigned int )pos > recipients .size ()) {
549
559
throw JSONRPCError (RPC_INVALID_PARAMETER, " changePosition out of bounds" );
550
560
}
551
561
change_position = (unsigned int )pos;
@@ -587,9 +597,6 @@ CreatedTransactionResult FundTransaction(CWallet& wallet, const CMutableTransact
587
597
coinControl.fOverrideFeeRate = true ;
588
598
}
589
599
590
- if (options.exists (" subtractFeeFromOutputs" ) || options.exists (" subtract_fee_from_outputs" ) )
591
- subtractFeeFromOutputs = (options.exists (" subtract_fee_from_outputs" ) ? options[" subtract_fee_from_outputs" ] : options[" subtractFeeFromOutputs" ]).get_array ();
592
-
593
600
if (options.exists (" replaceable" )) {
594
601
coinControl.m_signal_bip125_rbf = options[" replaceable" ].get_bool ();
595
602
}
@@ -695,21 +702,10 @@ CreatedTransactionResult FundTransaction(CWallet& wallet, const CMutableTransact
695
702
}
696
703
}
697
704
698
- if (tx. vout . size () == 0 )
705
+ if (recipients. empty () )
699
706
throw JSONRPCError (RPC_INVALID_PARAMETER, " TX must have at least one output" );
700
707
701
- for (unsigned int idx = 0 ; idx < subtractFeeFromOutputs.size (); idx++) {
702
- int pos = subtractFeeFromOutputs[idx].getInt <int >();
703
- if (setSubtractFeeFromOutputs.count (pos))
704
- throw JSONRPCError (RPC_INVALID_PARAMETER, strprintf (" Invalid parameter, duplicated position: %d" , pos));
705
- if (pos < 0 )
706
- throw JSONRPCError (RPC_INVALID_PARAMETER, strprintf (" Invalid parameter, negative position: %d" , pos));
707
- if (pos >= int (tx.vout .size ()))
708
- throw JSONRPCError (RPC_INVALID_PARAMETER, strprintf (" Invalid parameter, position too large: %d" , pos));
709
- setSubtractFeeFromOutputs.insert (pos);
710
- }
711
-
712
- auto txr = FundTransaction (wallet, tx, change_position, lockUnspents, setSubtractFeeFromOutputs, coinControl);
708
+ auto txr = FundTransaction (wallet, tx, recipients, change_position, lockUnspents, coinControl);
713
709
if (!txr) {
714
710
throw JSONRPCError (RPC_WALLET_ERROR, ErrorString (txr).original );
715
711
}
@@ -835,11 +831,25 @@ RPCHelpMan fundrawtransaction()
835
831
if (!DecodeHexTx (tx, request.params [0 ].get_str (), try_no_witness, try_witness)) {
836
832
throw JSONRPCError (RPC_DESERIALIZATION_ERROR, " TX decode failed" );
837
833
}
838
-
834
+ UniValue options = request.params [1 ];
835
+ std::vector<std::pair<CTxDestination, CAmount>> destinations;
836
+ for (const auto & tx_out : tx.vout ) {
837
+ CTxDestination dest;
838
+ ExtractDestination (tx_out.scriptPubKey , dest);
839
+ destinations.emplace_back (dest, tx_out.nValue );
840
+ }
841
+ std::vector<std::string> dummy (destinations.size (), " dummy" );
842
+ std::vector<CRecipient> recipients = CreateRecipients (
843
+ destinations,
844
+ InterpretSubtractFeeFromOutputInstructions (options[" subtractFeeFromOutputs" ], dummy)
845
+ );
839
846
CCoinControl coin_control;
840
847
// Automatically select (additional) coins. Can be overridden by options.add_inputs.
841
848
coin_control.m_allow_other_inputs = true ;
842
- auto txr = FundTransaction (*pwallet, tx, request.params [1 ], coin_control, /* override_min_fee=*/ true );
849
+ // Clear tx.vout since it is not meant to be used now that we are passing outputs directly.
850
+ // This sets us up for a future PR to completely remove tx from the function signature in favor of passing inputs directly
851
+ tx.vout .clear ();
852
+ auto txr = FundTransaction (*pwallet, tx, recipients, options, coin_control, /* override_min_fee=*/ true );
843
853
844
854
UniValue result (UniValue::VOBJ);
845
855
result.pushKV (" hex" , EncodeHexTx (*txr.tx ));
@@ -1267,13 +1277,22 @@ RPCHelpMan send()
1267
1277
1268
1278
1269
1279
bool rbf{options.exists (" replaceable" ) ? options[" replaceable" ].get_bool () : pwallet->m_signal_rbf };
1280
+ UniValue outputs (UniValue::VOBJ);
1281
+ outputs = NormalizeOutputs (request.params [0 ]);
1282
+ std::vector<CRecipient> recipients = CreateRecipients (
1283
+ ParseOutputs (outputs),
1284
+ InterpretSubtractFeeFromOutputInstructions (options[" subtract_fee_from_outputs" ], outputs.getKeys ())
1285
+ );
1270
1286
CMutableTransaction rawTx = ConstructTransaction (options[" inputs" ], request.params [0 ], options[" locktime" ], rbf);
1271
1287
CCoinControl coin_control;
1272
1288
// Automatically select coins, unless at least one is manually selected. Can
1273
1289
// be overridden by options.add_inputs.
1274
1290
coin_control.m_allow_other_inputs = rawTx.vin .size () == 0 ;
1275
1291
SetOptionsInputWeights (options[" inputs" ], options);
1276
- auto txr = FundTransaction (*pwallet, rawTx, options, coin_control, /* override_min_fee=*/ false );
1292
+ // Clear tx.vout since it is not meant to be used now that we are passing outputs directly.
1293
+ // This sets us up for a future PR to completely remove tx from the function signature in favor of passing inputs directly
1294
+ rawTx.vout .clear ();
1295
+ auto txr = FundTransaction (*pwallet, rawTx, recipients, options, coin_control, /* override_min_fee=*/ false );
1277
1296
1278
1297
return FinishTransaction (pwallet, options, CMutableTransaction (*txr.tx ));
1279
1298
}
@@ -1703,12 +1722,21 @@ RPCHelpMan walletcreatefundedpsbt()
1703
1722
const UniValue &replaceable_arg = options[" replaceable" ];
1704
1723
const bool rbf{replaceable_arg.isNull () ? wallet.m_signal_rbf : replaceable_arg.get_bool ()};
1705
1724
CMutableTransaction rawTx = ConstructTransaction (request.params [0 ], request.params [1 ], request.params [2 ], rbf);
1725
+ UniValue outputs (UniValue::VOBJ);
1726
+ outputs = NormalizeOutputs (request.params [1 ]);
1727
+ std::vector<CRecipient> recipients = CreateRecipients (
1728
+ ParseOutputs (outputs),
1729
+ InterpretSubtractFeeFromOutputInstructions (options[" subtractFeeFromOutputs" ], outputs.getKeys ())
1730
+ );
1706
1731
CCoinControl coin_control;
1707
1732
// Automatically select coins, unless at least one is manually selected. Can
1708
1733
// be overridden by options.add_inputs.
1709
1734
coin_control.m_allow_other_inputs = rawTx.vin .size () == 0 ;
1710
1735
SetOptionsInputWeights (request.params [0 ], options);
1711
- auto txr = FundTransaction (wallet, rawTx, options, coin_control, /* override_min_fee=*/ true );
1736
+ // Clear tx.vout since it is not meant to be used now that we are passing outputs directly.
1737
+ // This sets us up for a future PR to completely remove tx from the function signature in favor of passing inputs directly
1738
+ rawTx.vout .clear ();
1739
+ auto txr = FundTransaction (wallet, rawTx, recipients, options, coin_control, /* override_min_fee=*/ true );
1712
1740
1713
1741
// Make a blank psbt
1714
1742
PartiallySignedTransaction psbtx (CMutableTransaction (*txr.tx ));
0 commit comments