@@ -197,17 +197,19 @@ static std::string LabelFromValue(const UniValue& value)
197197/* *
198198 * Update coin control with fee estimation based on the given parameters
199199 *
200- * @param[in] pwallet Wallet pointer
201- * @param[in,out] cc Coin control which is to be updated
202- * @param[in] conf_target UniValue integer, confirmation target in blocks, values between 1 and 1008 are valid per policy/fees.h;
203- * if a fee_rate is present, 0 is allowed here as a no-op positional placeholder
204- * @param[in] estimate_mode UniValue string, fee estimation mode, valid values are "unset", "economical" or "conservative";
205- * if a fee_rate is present, "" is allowed here as a no-op positional placeholder
206- * @param[in] fee_rate UniValue real, fee rate in sat/vB;
207- * if a fee_rate is present, both conf_target and estimate_mode must either be null, or no-op values
200+ * @param[in] pwallet Wallet pointer
201+ * @param[in,out] cc Coin control to be updated
202+ * @param[in] conf_target UniValue integer; confirmation target in blocks, values between 1 and 1008 are valid per policy/fees.h;
203+ * if a fee_rate is present, 0 is allowed here as a no-op positional placeholder
204+ * @param[in] estimate_mode UniValue string; fee estimation mode, valid values are "unset", "economical" or "conservative";
205+ * if a fee_rate is present, "" is allowed here as a no-op positional placeholder
206+ * @param[in] fee_rate UniValue real; fee rate in sat/vB;
207+ * if a fee_rate is present, both conf_target and estimate_mode must either be null, or no-op
208+ * @param[in] override_min_fee bool; whether to set fOverrideFeeRate to true to disable minimum fee rate checks and instead
209+ * verify only that fee_rate is greater than 0
208210 * @throws a JSONRPCError if conf_target, estimate_mode, or fee_rate contain invalid values or are in conflict
209211 */
210- static void SetFeeEstimateMode (const CWallet* pwallet, CCoinControl& cc, const UniValue& conf_target, const UniValue& estimate_mode, const UniValue& fee_rate)
212+ static void SetFeeEstimateMode (const CWallet* pwallet, CCoinControl& cc, const UniValue& conf_target, const UniValue& estimate_mode, const UniValue& fee_rate, bool override_min_fee )
211213{
212214 if (!fee_rate.isNull ()) {
213215 if (!conf_target.isNull () && conf_target.get_int () > 0 ) {
@@ -216,7 +218,14 @@ static void SetFeeEstimateMode(const CWallet* pwallet, CCoinControl& cc, const U
216218 if (!estimate_mode.isNull () && !estimate_mode.get_str ().empty ()) {
217219 throw JSONRPCError (RPC_INVALID_PARAMETER, " Cannot specify both estimate_mode and fee_rate" );
218220 }
219- cc.m_feerate = CFeeRate (AmountFromValue (fee_rate), COIN);
221+ CFeeRate fee_rate_in_sat_vb{CFeeRate (AmountFromValue (fee_rate), COIN)};
222+ if (override_min_fee) {
223+ if (fee_rate_in_sat_vb <= CFeeRate (0 )) {
224+ throw JSONRPCError (RPC_INVALID_PARAMETER, strprintf (" Invalid fee_rate %s (must be greater than 0)" , fee_rate_in_sat_vb.ToString (FeeEstimateMode::SAT_VB)));
225+ }
226+ cc.fOverrideFeeRate = true ;
227+ }
228+ cc.m_feerate = fee_rate_in_sat_vb;
220229 // Default RBF to true for explicit fee_rate, if unset.
221230 if (cc.m_signal_bip125_rbf == nullopt ) cc.m_signal_bip125_rbf = true ;
222231 return ;
@@ -504,7 +513,7 @@ static RPCHelpMan sendtoaddress()
504513 // We also enable partial spend avoidance if reuse avoidance is set.
505514 coin_control.m_avoid_partial_spends |= coin_control.m_avoid_address_reuse ;
506515
507- SetFeeEstimateMode (pwallet, coin_control, /* conf_target */ request.params [6 ], /* estimate_mode */ request.params [7 ], /* fee_rate */ request.params [9 ]);
516+ SetFeeEstimateMode (pwallet, coin_control, /* conf_target */ request.params [6 ], /* estimate_mode */ request.params [7 ], /* fee_rate */ request.params [9 ], /* override_min_fee */ false );
508517
509518 EnsureWalletIsUnlocked (pwallet);
510519
@@ -932,7 +941,7 @@ static RPCHelpMan sendmany()
932941 coin_control.m_signal_bip125_rbf = request.params [5 ].get_bool ();
933942 }
934943
935- SetFeeEstimateMode (pwallet, coin_control, /* conf_target */ request.params [6 ], /* estimate_mode */ request.params [7 ], /* fee_rate */ request.params [8 ]);
944+ SetFeeEstimateMode (pwallet, coin_control, /* conf_target */ request.params [6 ], /* estimate_mode */ request.params [7 ], /* fee_rate */ request.params [8 ], /* override_min_fee */ false );
936945
937946 std::vector<CRecipient> recipients;
938947 ParseRecipients (sendTo, subtractFeeFromAmount, recipients);
@@ -3049,7 +3058,7 @@ static RPCHelpMan listunspent()
30493058 };
30503059}
30513060
3052- void FundTransaction (CWallet* const pwallet, CMutableTransaction& tx, CAmount& fee_out, int & change_position, const UniValue& options, CCoinControl& coinControl)
3061+ void FundTransaction (CWallet* const pwallet, CMutableTransaction& tx, CAmount& fee_out, int & change_position, const UniValue& options, CCoinControl& coinControl, bool override_min_fee )
30533062{
30543063 // Make sure the results are valid at least up to the most recent block
30553064 // the user could have gotten from another RPC command prior to now
@@ -3154,7 +3163,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
31543163 if (options.exists (" replaceable" )) {
31553164 coinControl.m_signal_bip125_rbf = options[" replaceable" ].get_bool ();
31563165 }
3157- SetFeeEstimateMode (pwallet, coinControl, options[" conf_target" ], options[" estimate_mode" ], options[" fee_rate" ]);
3166+ SetFeeEstimateMode (pwallet, coinControl, options[" conf_target" ], options[" estimate_mode" ], options[" fee_rate" ], override_min_fee );
31583167 }
31593168 } else {
31603169 // if options is null and not a bool
@@ -3275,7 +3284,7 @@ static RPCHelpMan fundrawtransaction()
32753284 CCoinControl coin_control;
32763285 // Automatically select (additional) coins. Can be overridden by options.add_inputs.
32773286 coin_control.m_add_inputs = true ;
3278- FundTransaction (pwallet, tx, fee, change_position, request.params [1 ], coin_control);
3287+ FundTransaction (pwallet, tx, fee, change_position, request.params [1 ], coin_control, /* override_min_fee */ true );
32793288
32803289 UniValue result (UniValue::VOBJ);
32813290 result.pushKV (" hex" , EncodeHexTx (CTransaction (tx)));
@@ -3482,7 +3491,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
34823491 if (options.exists (" replaceable" )) {
34833492 coin_control.m_signal_bip125_rbf = options[" replaceable" ].get_bool ();
34843493 }
3485- SetFeeEstimateMode (pwallet, coin_control, conf_target, options[" estimate_mode" ], options[" fee_rate" ]);
3494+ SetFeeEstimateMode (pwallet, coin_control, conf_target, options[" estimate_mode" ], options[" fee_rate" ], /* override_min_fee */ false );
34863495 }
34873496
34883497 // Make sure the results are valid at least up to the most recent block
@@ -4146,7 +4155,7 @@ static RPCHelpMan send()
41464155 // Automatically select coins, unless at least one is manually selected. Can
41474156 // be overridden by options.add_inputs.
41484157 coin_control.m_add_inputs = rawTx.vin .size () == 0 ;
4149- FundTransaction (pwallet, rawTx, fee, change_position, options, coin_control);
4158+ FundTransaction (pwallet, rawTx, fee, change_position, options, coin_control, /* override_min_fee */ false );
41504159
41514160 bool add_to_wallet = true ;
41524161 if (options.exists (" add_to_wallet" )) {
@@ -4433,7 +4442,7 @@ static RPCHelpMan walletcreatefundedpsbt()
44334442 // Automatically select coins, unless at least one is manually selected. Can
44344443 // be overridden by options.add_inputs.
44354444 coin_control.m_add_inputs = rawTx.vin .size () == 0 ;
4436- FundTransaction (pwallet, rawTx, fee, change_position, request.params [3 ], coin_control);
4445+ FundTransaction (pwallet, rawTx, fee, change_position, request.params [3 ], coin_control, /* override_min_fee */ true );
44374446
44384447 // Make a blank psbt
44394448 PartiallySignedTransaction psbtx (rawTx);
0 commit comments