Merge #10589: More economical fee estimates for RBF and RPC options to control
f135923
Add RPC options for RBF, confirmation target, and conservative fee estimation. (Alex Morcos)f0bf33d
Change default fee estimation mode. (Alex Morcos)e0738e3
remove default argument from estimateSmartFee (Alex Morcos)d507c30
Introduce a fee estimate mode. (Alex Morcos)cfaef69
remove default argument from GetMinimumFee (Alex Morcos) Tree-SHA512: 49c3a49a6893790a7e8b4e93a48f123dd5307af26c2017800683b76b4df8fc904ba73402917878676242c7440e3e04288d0c1ff3c2c907418724efc03cedab50
This commit is contained in:
commit
104f5f21dc
13 changed files with 157 additions and 28 deletions
|
@ -36,6 +36,20 @@ std::string StringForFeeReason(FeeReason reason) {
|
|||
return reason_string->second;
|
||||
}
|
||||
|
||||
bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode) {
|
||||
static const std::map<std::string, FeeEstimateMode> fee_modes = {
|
||||
{"UNSET", FeeEstimateMode::UNSET},
|
||||
{"ECONOMICAL", FeeEstimateMode::ECONOMICAL},
|
||||
{"CONSERVATIVE", FeeEstimateMode::CONSERVATIVE},
|
||||
};
|
||||
auto mode = fee_modes.find(mode_string);
|
||||
|
||||
if (mode == fee_modes.end()) return false;
|
||||
|
||||
fee_estimate_mode = mode->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* We will instantiate an instance of this class to track transactions that were
|
||||
* included in a block. We will lump transactions into a bucket according to their
|
||||
|
|
|
@ -90,6 +90,15 @@ enum class FeeReason {
|
|||
|
||||
std::string StringForFeeReason(FeeReason reason);
|
||||
|
||||
/* Used to determine type of fee estimation requested */
|
||||
enum class FeeEstimateMode {
|
||||
UNSET, //! Use default settings based on other criteria
|
||||
ECONOMICAL, //! Force estimateSmartFee to use non-conservative estimates
|
||||
CONSERVATIVE, //! Force estimateSmartFee to use conservative estimates
|
||||
};
|
||||
|
||||
bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode);
|
||||
|
||||
/* Used to return detailed information about a feerate bucket */
|
||||
struct EstimatorBucket
|
||||
{
|
||||
|
@ -197,7 +206,7 @@ public:
|
|||
* the closest target where one can be given. 'conservative' estimates are
|
||||
* valid over longer time horizons also.
|
||||
*/
|
||||
CFeeRate estimateSmartFee(int confTarget, FeeCalculation *feeCalc, const CTxMemPool& pool, bool conservative = true) const;
|
||||
CFeeRate estimateSmartFee(int confTarget, FeeCalculation *feeCalc, const CTxMemPool& pool, bool conservative) const;
|
||||
|
||||
/** Return a specific fee estimate calculation with a given success
|
||||
* threshold and time horizon, and optionally return detailed data about
|
||||
|
|
|
@ -490,6 +490,8 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
|
|||
else nBytesInputs += 148;
|
||||
}
|
||||
|
||||
bool conservative_estimate = CalculateEstimateType(FeeEstimateMode::UNSET, coinControl->signalRbf);
|
||||
|
||||
// calculation
|
||||
if (nQuantity > 0)
|
||||
{
|
||||
|
@ -510,7 +512,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
|
|||
nBytes -= 34;
|
||||
|
||||
// Fee
|
||||
nPayFee = CWallet::GetMinimumFee(nBytes, coinControl->nConfirmTarget, ::mempool, ::feeEstimator);
|
||||
nPayFee = CWallet::GetMinimumFee(nBytes, coinControl->nConfirmTarget, ::mempool, ::feeEstimator, nullptr /* FeeCalculation */, false /* ignoreGlobalPayTxFee */, conservative_estimate);
|
||||
|
||||
if (nPayAmount > 0)
|
||||
{
|
||||
|
@ -585,7 +587,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
|
|||
if (payTxFee.GetFeePerK() > 0)
|
||||
dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), payTxFee.GetFeePerK()) / 1000;
|
||||
else {
|
||||
dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), ::feeEstimator.estimateSmartFee(coinControl->nConfirmTarget, NULL, ::mempool).GetFeePerK()) / 1000;
|
||||
dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), ::feeEstimator.estimateSmartFee(coinControl->nConfirmTarget, NULL, ::mempool, conservative_estimate).GetFeePerK()) / 1000;
|
||||
}
|
||||
QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary);
|
||||
|
||||
|
|
|
@ -166,6 +166,8 @@ void SendCoinsDialog::setModel(WalletModel *_model)
|
|||
connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateFeeSectionControls()));
|
||||
connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateGlobalFeeVariables()));
|
||||
connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
|
||||
connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(updateSmartFeeLabel()));
|
||||
connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
|
||||
ui->customFee->setSingleStep(CWallet::GetRequiredFee(1000));
|
||||
updateFeeSectionControls();
|
||||
updateMinFeeLabel();
|
||||
|
@ -652,7 +654,8 @@ void SendCoinsDialog::updateSmartFeeLabel()
|
|||
|
||||
int nBlocksToConfirm = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
|
||||
FeeCalculation feeCalc;
|
||||
CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocksToConfirm, &feeCalc, ::mempool);
|
||||
bool conservative_estimate = CalculateEstimateType(FeeEstimateMode::UNSET, ui->optInRBF->isChecked());
|
||||
CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocksToConfirm, &feeCalc, ::mempool, conservative_estimate);
|
||||
if (feeRate <= CFeeRate(0)) // not enough data => minfee
|
||||
{
|
||||
ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(),
|
||||
|
@ -827,6 +830,7 @@ void SendCoinsDialog::coinControlUpdateLabels()
|
|||
} else {
|
||||
CoinControlDialog::coinControl->nConfirmTarget = model->getDefaultConfirmTarget();
|
||||
}
|
||||
CoinControlDialog::coinControl->signalRbf = ui->optInRBF->isChecked();
|
||||
|
||||
for(int i = 0; i < ui->entries->count(); ++i)
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "keystore.h"
|
||||
#include "validation.h"
|
||||
#include "net.h" // for g_connman
|
||||
#include "policy/fees.h"
|
||||
#include "policy/rbf.h"
|
||||
#include "sync.h"
|
||||
#include "ui_interface.h"
|
||||
|
@ -667,7 +668,7 @@ bool WalletModel::bumpFee(uint256 hash)
|
|||
std::unique_ptr<CFeeBumper> feeBump;
|
||||
{
|
||||
LOCK2(cs_main, wallet->cs_wallet);
|
||||
feeBump.reset(new CFeeBumper(wallet, hash, nTxConfirmTarget, false, 0, true));
|
||||
feeBump.reset(new CFeeBumper(wallet, hash, nTxConfirmTarget, false, 0, true, FeeEstimateMode::UNSET));
|
||||
}
|
||||
if (feeBump->getResult() != BumpFeeResult::OK)
|
||||
{
|
||||
|
|
|
@ -37,6 +37,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "getnetworkhashps", 1, "height" },
|
||||
{ "sendtoaddress", 1, "amount" },
|
||||
{ "sendtoaddress", 4, "subtractfeefromamount" },
|
||||
{ "sendtoaddress", 5 , "replaceable" },
|
||||
{ "sendtoaddress", 6 , "conf_target" },
|
||||
{ "settxfee", 0, "amount" },
|
||||
{ "getreceivedbyaddress", 1, "minconf" },
|
||||
{ "getreceivedbyaccount", 1, "minconf" },
|
||||
|
@ -69,6 +71,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "sendmany", 1, "amounts" },
|
||||
{ "sendmany", 2, "minconf" },
|
||||
{ "sendmany", 4, "subtractfeefrom" },
|
||||
{ "sendmany", 5 , "replaceable" },
|
||||
{ "sendmany", 6 , "conf_target" },
|
||||
{ "addmultisigaddress", 0, "nrequired" },
|
||||
{ "addmultisigaddress", 1, "keys" },
|
||||
{ "createmultisig", 0, "nrequired" },
|
||||
|
|
|
@ -184,8 +184,8 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
|
|||
mpool.TrimToSize(1);
|
||||
BOOST_CHECK(mpool.GetMinFee(1).GetFeePerK() > feeV[5]);
|
||||
for (int i = 1; i < 10; i++) {
|
||||
BOOST_CHECK(feeEst.estimateSmartFee(i, NULL, mpool).GetFeePerK() >= feeEst.estimateRawFee(i, 0.85, FeeEstimateHorizon::MED_HALFLIFE).GetFeePerK());
|
||||
BOOST_CHECK(feeEst.estimateSmartFee(i, NULL, mpool).GetFeePerK() >= mpool.GetMinFee(1).GetFeePerK());
|
||||
BOOST_CHECK(feeEst.estimateSmartFee(i, NULL, mpool, true).GetFeePerK() >= feeEst.estimateRawFee(i, 0.85, FeeEstimateHorizon::MED_HALFLIFE).GetFeePerK());
|
||||
BOOST_CHECK(feeEst.estimateSmartFee(i, NULL, mpool, true).GetFeePerK() >= mpool.GetMinFee(1).GetFeePerK());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#define BITCOIN_WALLET_COINCONTROL_H
|
||||
|
||||
#include "policy/feerate.h"
|
||||
#include "policy/fees.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "wallet/wallet.h"
|
||||
|
||||
|
@ -26,6 +27,8 @@ public:
|
|||
int nConfirmTarget;
|
||||
//! Signal BIP-125 replace by fee.
|
||||
bool signalRbf;
|
||||
//! Fee estimation mode to control arguments to estimateSmartFee
|
||||
FeeEstimateMode m_fee_mode;
|
||||
|
||||
CCoinControl()
|
||||
{
|
||||
|
@ -42,6 +45,7 @@ public:
|
|||
fOverrideFeeRate = false;
|
||||
nConfirmTarget = 0;
|
||||
signalRbf = fWalletRbf;
|
||||
m_fee_mode = FeeEstimateMode::UNSET;
|
||||
}
|
||||
|
||||
bool HasSelected() const
|
||||
|
|
|
@ -66,7 +66,7 @@ bool CFeeBumper::preconditionChecks(const CWallet *pWallet, const CWalletTx& wtx
|
|||
return true;
|
||||
}
|
||||
|
||||
CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConfirmTarget, bool ignoreGlobalPayTxFee, CAmount totalFee, bool newTxReplaceable)
|
||||
CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConfirmTarget, bool ignoreGlobalPayTxFee, CAmount totalFee, bool newTxReplaceable, FeeEstimateMode fee_mode)
|
||||
:
|
||||
txid(std::move(txidIn)),
|
||||
nOldFee(0),
|
||||
|
@ -165,7 +165,8 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConf
|
|||
nNewFee = totalFee;
|
||||
nNewFeeRate = CFeeRate(totalFee, maxNewTxSize);
|
||||
} else {
|
||||
nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, ::feeEstimator, nullptr, ignoreGlobalPayTxFee);
|
||||
bool conservative_estimate = CalculateEstimateType(fee_mode, newTxReplaceable);
|
||||
nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, ::feeEstimator, nullptr /* FeeCalculation */, ignoreGlobalPayTxFee, conservative_estimate);
|
||||
nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize);
|
||||
|
||||
// New fee rate must be at least old rate + minimum incremental relay rate
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
class CWallet;
|
||||
class CWalletTx;
|
||||
class uint256;
|
||||
enum class FeeEstimateMode;
|
||||
|
||||
enum class BumpFeeResult
|
||||
{
|
||||
|
@ -24,7 +25,7 @@ enum class BumpFeeResult
|
|||
class CFeeBumper
|
||||
{
|
||||
public:
|
||||
CFeeBumper(const CWallet *pWalletIn, const uint256 txidIn, int newConfirmTarget, bool ignoreGlobalPayTxFee, CAmount totalFee, bool newTxReplaceable);
|
||||
CFeeBumper(const CWallet *pWalletIn, const uint256 txidIn, int newConfirmTarget, bool ignoreGlobalPayTxFee, CAmount totalFee, bool newTxReplaceable, FeeEstimateMode fee_mode);
|
||||
BumpFeeResult getResult() const { return currentResult; }
|
||||
const std::vector<std::string>& getErrors() const { return vErrors; }
|
||||
CAmount getOldFee() const { return nOldFee; }
|
||||
|
|
|
@ -356,7 +356,7 @@ UniValue getaddressesbyaccount(const JSONRPCRequest& request)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew)
|
||||
static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew, CCoinControl *coin_control = nullptr)
|
||||
{
|
||||
CAmount curBalance = pwallet->GetBalance();
|
||||
|
||||
|
@ -382,7 +382,7 @@ static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CA
|
|||
int nChangePosRet = -1;
|
||||
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
|
||||
vecSend.push_back(recipient);
|
||||
if (!pwallet->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) {
|
||||
if (!pwallet->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError, coin_control)) {
|
||||
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance)
|
||||
strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired));
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, strError);
|
||||
|
@ -401,9 +401,9 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
|
|||
return NullUniValue;
|
||||
}
|
||||
|
||||
if (request.fHelp || request.params.size() < 2 || request.params.size() > 5)
|
||||
if (request.fHelp || request.params.size() < 2 || request.params.size() > 8)
|
||||
throw std::runtime_error(
|
||||
"sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" subtractfeefromamount )\n"
|
||||
"sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" subtractfeefromamount replaceable conf_target \"estimate_mode\")\n"
|
||||
"\nSend an amount to a given address.\n"
|
||||
+ HelpRequiringPassphrase(pwallet) +
|
||||
"\nArguments:\n"
|
||||
|
@ -416,6 +416,12 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
|
|||
" transaction, just kept in your wallet.\n"
|
||||
"5. subtractfeefromamount (boolean, optional, default=false) The fee will be deducted from the amount being sent.\n"
|
||||
" The recipient will receive less bitcoins than you enter in the amount field.\n"
|
||||
"6. replaceable (boolean, optional) Allow this transaction to be replaced by a transaction with higher fees via BIP 125\n"
|
||||
"7. conf_target (numeric, optional) Confirmation target (in blocks)\n"
|
||||
"8. \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n"
|
||||
" \"UNSET\"\n"
|
||||
" \"ECONOMICAL\"\n"
|
||||
" \"CONSERVATIVE\"\n"
|
||||
"\nResult:\n"
|
||||
"\"txid\" (string) The transaction id.\n"
|
||||
"\nExamples:\n"
|
||||
|
@ -444,12 +450,29 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
|
|||
wtx.mapValue["to"] = request.params[3].get_str();
|
||||
|
||||
bool fSubtractFeeFromAmount = false;
|
||||
if (request.params.size() > 4)
|
||||
if (request.params.size() > 4 && !request.params[4].isNull()) {
|
||||
fSubtractFeeFromAmount = request.params[4].get_bool();
|
||||
}
|
||||
|
||||
CCoinControl coin_control;
|
||||
if (request.params.size() > 5 && !request.params[5].isNull()) {
|
||||
coin_control.signalRbf = request.params[5].get_bool();
|
||||
}
|
||||
|
||||
if (request.params.size() > 6 && !request.params[6].isNull()) {
|
||||
coin_control.nConfirmTarget = request.params[6].get_int();
|
||||
}
|
||||
|
||||
if (request.params.size() > 7 && !request.params[7].isNull()) {
|
||||
if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EnsureWalletIsUnlocked(pwallet);
|
||||
|
||||
SendMoney(pwallet, address.Get(), nAmount, fSubtractFeeFromAmount, wtx);
|
||||
SendMoney(pwallet, address.Get(), nAmount, fSubtractFeeFromAmount, wtx, &coin_control);
|
||||
|
||||
return wtx.GetHash().GetHex();
|
||||
}
|
||||
|
@ -888,9 +911,9 @@ UniValue sendmany(const JSONRPCRequest& request)
|
|||
return NullUniValue;
|
||||
}
|
||||
|
||||
if (request.fHelp || request.params.size() < 2 || request.params.size() > 5)
|
||||
if (request.fHelp || request.params.size() < 2 || request.params.size() > 8)
|
||||
throw std::runtime_error(
|
||||
"sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] )\n"
|
||||
"sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] replaceable conf_target \"estimate_mode\")\n"
|
||||
"\nSend multiple times. Amounts are double-precision floating point numbers."
|
||||
+ HelpRequiringPassphrase(pwallet) + "\n"
|
||||
"\nArguments:\n"
|
||||
|
@ -910,7 +933,13 @@ UniValue sendmany(const JSONRPCRequest& request)
|
|||
" \"address\" (string) Subtract fee from this address\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
"\nResult:\n"
|
||||
"6. replaceable (boolean, optional) Allow this transaction to be replaced by a transaction with higher fees via BIP 125\n"
|
||||
"7. conf_target (numeric, optional) Confirmation target (in blocks)\n"
|
||||
"8. \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n"
|
||||
" \"UNSET\"\n"
|
||||
" \"ECONOMICAL\"\n"
|
||||
" \"CONSERVATIVE\"\n"
|
||||
"\nResult:\n"
|
||||
"\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n"
|
||||
" the number of addresses.\n"
|
||||
"\nExamples:\n"
|
||||
|
@ -942,9 +971,24 @@ UniValue sendmany(const JSONRPCRequest& request)
|
|||
wtx.mapValue["comment"] = request.params[3].get_str();
|
||||
|
||||
UniValue subtractFeeFromAmount(UniValue::VARR);
|
||||
if (request.params.size() > 4)
|
||||
if (request.params.size() > 4 && !request.params[4].isNull())
|
||||
subtractFeeFromAmount = request.params[4].get_array();
|
||||
|
||||
CCoinControl coin_control;
|
||||
if (request.params.size() > 5 && !request.params[5].isNull()) {
|
||||
coin_control.signalRbf = request.params[5].get_bool();
|
||||
}
|
||||
|
||||
if (request.params.size() > 6 && !request.params[6].isNull()) {
|
||||
coin_control.nConfirmTarget = request.params[6].get_int();
|
||||
}
|
||||
|
||||
if (request.params.size() > 7 && !request.params[7].isNull()) {
|
||||
if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
|
||||
}
|
||||
}
|
||||
|
||||
std::set<CBitcoinAddress> setAddress;
|
||||
std::vector<CRecipient> vecSend;
|
||||
|
||||
|
@ -989,7 +1033,7 @@ UniValue sendmany(const JSONRPCRequest& request)
|
|||
CAmount nFeeRequired = 0;
|
||||
int nChangePosRet = -1;
|
||||
std::string strFailReason;
|
||||
bool fCreated = pwallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason);
|
||||
bool fCreated = pwallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason, &coin_control);
|
||||
if (!fCreated)
|
||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
|
||||
CValidationState state;
|
||||
|
@ -2658,6 +2702,11 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
|
|||
" [vout_index,...]\n"
|
||||
" \"replaceable\" (boolean, optional) Marks this transaction as BIP125 replaceable.\n"
|
||||
" Allows this transaction to be replaced by a transaction with higher fees\n"
|
||||
" \"conf_target\" (numeric, optional) Confirmation target (in blocks)\n"
|
||||
" \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n"
|
||||
" \"UNSET\"\n"
|
||||
" \"ECONOMICAL\"\n"
|
||||
" \"CONSERVATIVE\"\n"
|
||||
" }\n"
|
||||
" for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n"
|
||||
"\nResult:\n"
|
||||
|
@ -2710,6 +2759,8 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
|
|||
{"feeRate", UniValueType()}, // will be checked below
|
||||
{"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
|
||||
{"replaceable", UniValueType(UniValue::VBOOL)},
|
||||
{"conf_target", UniValueType(UniValue::VNUM)},
|
||||
{"estimate_mode", UniValueType(UniValue::VSTR)},
|
||||
},
|
||||
true, true);
|
||||
|
||||
|
@ -2746,6 +2797,14 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
|
|||
if (options.exists("replaceable")) {
|
||||
coinControl.signalRbf = options["replaceable"].get_bool();
|
||||
}
|
||||
if (options.exists("conf_target")) {
|
||||
coinControl.nConfirmTarget = options["conf_target"].get_int();
|
||||
}
|
||||
if (options.exists("estimate_mode")) {
|
||||
if (!FeeModeFromString(options["estimate_mode"].get_str(), coinControl.m_fee_mode)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2823,6 +2882,10 @@ UniValue bumpfee(const JSONRPCRequest& request)
|
|||
" so the new transaction will not be explicitly bip-125 replaceable (though it may\n"
|
||||
" still be replaceable in practice, for example if it has unconfirmed ancestors which\n"
|
||||
" are replaceable).\n"
|
||||
" \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n"
|
||||
" \"UNSET\"\n"
|
||||
" \"ECONOMICAL\"\n"
|
||||
" \"CONSERVATIVE\"\n"
|
||||
" }\n"
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
|
@ -2845,6 +2908,7 @@ UniValue bumpfee(const JSONRPCRequest& request)
|
|||
int newConfirmTarget = nTxConfirmTarget;
|
||||
CAmount totalFee = 0;
|
||||
bool replaceable = true;
|
||||
FeeEstimateMode fee_mode = FeeEstimateMode::UNSET;
|
||||
if (request.params.size() > 1) {
|
||||
UniValue options = request.params[1];
|
||||
RPCTypeCheckObj(options,
|
||||
|
@ -2852,6 +2916,7 @@ UniValue bumpfee(const JSONRPCRequest& request)
|
|||
{"confTarget", UniValueType(UniValue::VNUM)},
|
||||
{"totalFee", UniValueType(UniValue::VNUM)},
|
||||
{"replaceable", UniValueType(UniValue::VBOOL)},
|
||||
{"estimate_mode", UniValueType(UniValue::VSTR)},
|
||||
},
|
||||
true, true);
|
||||
|
||||
|
@ -2876,12 +2941,17 @@ UniValue bumpfee(const JSONRPCRequest& request)
|
|||
if (options.exists("replaceable")) {
|
||||
replaceable = options["replaceable"].get_bool();
|
||||
}
|
||||
if (options.exists("estimate_mode")) {
|
||||
if (!FeeModeFromString(options["estimate_mode"].get_str(), fee_mode)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOCK2(cs_main, pwallet->cs_wallet);
|
||||
EnsureWalletIsUnlocked(pwallet);
|
||||
|
||||
CFeeBumper feeBump(pwallet, hash, newConfirmTarget, ignoreGlobalPayTxFee, totalFee, replaceable);
|
||||
CFeeBumper feeBump(pwallet, hash, newConfirmTarget, ignoreGlobalPayTxFee, totalFee, replaceable, fee_mode);
|
||||
BumpFeeResult res = feeBump.getResult();
|
||||
if (res != BumpFeeResult::OK)
|
||||
{
|
||||
|
@ -3023,8 +3093,8 @@ static const CRPCCommand commands[] =
|
|||
{ "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} },
|
||||
{ "wallet", "move", &movecmd, false, {"fromaccount","toaccount","amount","minconf","comment"} },
|
||||
{ "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} },
|
||||
{ "wallet", "sendmany", &sendmany, false, {"fromaccount","amounts","minconf","comment","subtractfeefrom"} },
|
||||
{ "wallet", "sendtoaddress", &sendtoaddress, false, {"address","amount","comment","comment_to","subtractfeefromamount"} },
|
||||
{ "wallet", "sendmany", &sendmany, false, {"fromaccount","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} },
|
||||
{ "wallet", "sendtoaddress", &sendtoaddress, false, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode"} },
|
||||
{ "wallet", "setaccount", &setaccount, true, {"address","account"} },
|
||||
{ "wallet", "settxfee", &settxfee, true, {"amount"} },
|
||||
{ "wallet", "signmessage", &signmessage, true, {"address","message"} },
|
||||
|
|
|
@ -2724,7 +2724,10 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
|
|||
if (coinControl && coinControl->nConfirmTarget > 0)
|
||||
currentConfirmationTarget = coinControl->nConfirmTarget;
|
||||
|
||||
CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, ::mempool, ::feeEstimator, &feeCalc);
|
||||
// Allow to override the default fee estimate mode over the CoinControl instance
|
||||
bool conservative_estimate = CalculateEstimateType(coinControl ? coinControl->m_fee_mode : FeeEstimateMode::UNSET, rbf);
|
||||
|
||||
CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, ::mempool, ::feeEstimator, &feeCalc, false /* ignoreGlobalPayTxFee */, conservative_estimate);
|
||||
if (coinControl && coinControl->fOverrideFeeRate)
|
||||
nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes);
|
||||
|
||||
|
@ -2905,13 +2908,13 @@ CAmount CWallet::GetRequiredFee(unsigned int nTxBytes)
|
|||
return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes));
|
||||
}
|
||||
|
||||
CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc, bool ignoreGlobalPayTxFee)
|
||||
CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc, bool ignoreGlobalPayTxFee, bool conservative_estimate)
|
||||
{
|
||||
// payTxFee is the user-set global for desired feerate
|
||||
CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes);
|
||||
// User didn't set: use -txconfirmtarget to estimate...
|
||||
if (nFeeNeeded == 0 || ignoreGlobalPayTxFee) {
|
||||
nFeeNeeded = estimator.estimateSmartFee(nConfirmTarget, feeCalc, pool, true).GetFee(nTxBytes);
|
||||
nFeeNeeded = estimator.estimateSmartFee(nConfirmTarget, feeCalc, pool, conservative_estimate).GetFee(nTxBytes);
|
||||
// ... unless we don't have enough mempool data for estimatefee, then use fallbackFee
|
||||
if (nFeeNeeded == 0) {
|
||||
nFeeNeeded = fallbackFee.GetFee(nTxBytes);
|
||||
|
@ -4154,3 +4157,15 @@ bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState&
|
|||
{
|
||||
return ::AcceptToMemoryPool(mempool, state, tx, true, NULL, NULL, false, nAbsurdFee);
|
||||
}
|
||||
|
||||
bool CalculateEstimateType(FeeEstimateMode mode, bool opt_in_rbf) {
|
||||
switch (mode) {
|
||||
case FeeEstimateMode::UNSET:
|
||||
return !opt_in_rbf; // Allow for lower fees if RBF is an option
|
||||
case FeeEstimateMode::CONSERVATIVE:
|
||||
return true;
|
||||
case FeeEstimateMode::ECONOMICAL:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ class CTxMemPool;
|
|||
class CBlockPolicyEstimator;
|
||||
class CWalletTx;
|
||||
struct FeeCalculation;
|
||||
enum class FeeEstimateMode;
|
||||
|
||||
/** (client) version numbers for particular wallet features */
|
||||
enum WalletFeature
|
||||
|
@ -963,7 +964,7 @@ public:
|
|||
* Estimate the minimum fee considering user set parameters
|
||||
* and the required fee
|
||||
*/
|
||||
static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc = nullptr, bool ignoreGlobalPayTxFee = false);
|
||||
static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc, bool ignoreGlobalPayTxFee, bool conservative_estimate);
|
||||
/**
|
||||
* Return the minimum required fee taking into account the
|
||||
* floating relay fee and user set minimum transaction fee
|
||||
|
@ -1211,4 +1212,7 @@ bool CWallet::DummySignTx(CMutableTransaction &txNew, const ContainerType &coins
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CalculateEstimateType(FeeEstimateMode mode, bool opt_in_rbf);
|
||||
|
||||
#endif // BITCOIN_WALLET_WALLET_H
|
||||
|
|
Loading…
Reference in a new issue