Skip to content

Commit d2c9e4d

Browse files
committed
Merge #9615: Wallet incremental fee
4b189c1 Change bumpfee result value from 'oldfee' to 'origfee'. (Alex Morcos) 0c0c63f Introduce WALLET_INCREMENTAL_RELAY_FEE (Alex Morcos) e8021ec Use CWallet::GetMinimumFee in bumpfee (Alex Morcos) ae9719a Refactor GetMinimumFee to give option of providing targetFee (Alex Morcos) fe8e8ef [rpc] Add incremental relay fee to getnetworkinfo (Alex Morcos) 6b331e6 Fix to have miner test aware of new separate block min tx fee (Alex Morcos) de6400d Fix missing use of dustRelayFee (Alex Morcos) 5b15870 Use incrementalRelayFee for BIP 125 replacement (Alex Morcos)
2 parents 720b579 + 4b189c1 commit d2c9e4d

File tree

6 files changed

+70
-36
lines changed

6 files changed

+70
-36
lines changed

src/rpc/net.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "net.h"
1111
#include "net_processing.h"
1212
#include "netbase.h"
13+
#include "policy/policy.h"
1314
#include "protocol.h"
1415
#include "sync.h"
1516
#include "timedata.h"
@@ -417,6 +418,7 @@ UniValue getnetworkinfo(const JSONRPCRequest& request)
417418
" ,...\n"
418419
" ],\n"
419420
" \"relayfee\": x.xxxxxxxx, (numeric) minimum relay fee for non-free transactions in " + CURRENCY_UNIT + "/kB\n"
421+
" \"incrementalfee\": x.xxxxxxxx, (numeric) minimum fee increment for mempool limiting or BIP 125 replacement in " + CURRENCY_UNIT + "/kB\n"
420422
" \"localaddresses\": [ (array) list of local addresses\n"
421423
" {\n"
422424
" \"address\": \"xxxx\", (string) network address\n"
@@ -447,6 +449,7 @@ UniValue getnetworkinfo(const JSONRPCRequest& request)
447449
}
448450
obj.push_back(Pair("networks", GetNetworksInfo()));
449451
obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())));
452+
obj.push_back(Pair("incrementalfee", ValueFromAmount(::incrementalRelayFee.GetFeePerK())));
450453
UniValue localAddresses(UniValue::VARR);
451454
{
452455
LOCK(cs_mapLocalHost);

src/test/miner_tests.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "consensus/validation.h"
1010
#include "validation.h"
1111
#include "miner.h"
12+
#include "policy/policy.h"
1213
#include "pubkey.h"
1314
#include "script/standard.h"
1415
#include "txmempool.h"
@@ -24,6 +25,8 @@
2425

2526
BOOST_FIXTURE_TEST_SUITE(miner_tests, TestingSetup)
2627

28+
static CFeeRate blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE);
29+
2730
static
2831
struct {
2932
unsigned char extranonce;
@@ -112,16 +115,16 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey,
112115
BOOST_CHECK(pblocktemplate->block.vtx[2]->GetHash() == hashHighFeeTx);
113116
BOOST_CHECK(pblocktemplate->block.vtx[3]->GetHash() == hashMediumFeeTx);
114117

115-
// Test that a package below the min relay fee doesn't get included
118+
// Test that a package below the block min tx fee doesn't get included
116119
tx.vin[0].prevout.hash = hashHighFeeTx;
117120
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 0 fee
118121
uint256 hashFreeTx = tx.GetHash();
119122
mempool.addUnchecked(hashFreeTx, entry.Fee(0).FromTx(tx));
120123
size_t freeTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
121124

122125
// Calculate a fee on child transaction that will put the package just
123-
// below the min relay fee (assuming 1 child tx of the same size).
124-
CAmount feeToUse = minRelayTxFee.GetFee(2*freeTxSize) - 1;
126+
// below the block min tx fee (assuming 1 child tx of the same size).
127+
CAmount feeToUse = blockMinFeeRate.GetFee(2*freeTxSize) - 1;
125128

126129
tx.vin[0].prevout.hash = hashFreeTx;
127130
tx.vout[0].nValue = 5000000000LL - 1000 - 50000 - feeToUse;
@@ -158,7 +161,7 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey,
158161
// This tx can't be mined by itself
159162
tx.vin[0].prevout.hash = hashFreeTx2;
160163
tx.vout.resize(1);
161-
feeToUse = minRelayTxFee.GetFee(freeTxSize);
164+
feeToUse = blockMinFeeRate.GetFee(freeTxSize);
162165
tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse;
163166
uint256 hashLowFeeTx2 = tx.GetHash();
164167
mempool.addUnchecked(hashLowFeeTx2, entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));

src/validation.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -932,14 +932,14 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
932932
// Finally in addition to paying more fees than the conflicts the
933933
// new transaction must pay for its own bandwidth.
934934
CAmount nDeltaFees = nModifiedFees - nConflictingFees;
935-
if (nDeltaFees < ::minRelayTxFee.GetFee(nSize))
935+
if (nDeltaFees < ::incrementalRelayFee.GetFee(nSize))
936936
{
937937
return state.DoS(0, false,
938938
REJECT_INSUFFICIENTFEE, "insufficient fee", false,
939939
strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s",
940940
hash.ToString(),
941941
FormatMoney(nDeltaFees),
942-
FormatMoney(::minRelayTxFee.GetFee(nSize))));
942+
FormatMoney(::incrementalRelayFee.GetFee(nSize))));
943943
}
944944
}
945945

src/wallet/rpcwallet.cpp

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2682,8 +2682,8 @@ UniValue bumpfee(const JSONRPCRequest& request)
26822682
"By default, the new fee will be calculated automatically using estimatefee.\n"
26832683
"The user can specify a confirmation target for estimatefee.\n"
26842684
"Alternatively, the user can specify totalFee, or use RPC setpaytxfee to set a higher fee rate.\n"
2685-
"At a minimum, the new fee rate must be high enough to pay a new relay fee (relay fee amount returned\n"
2686-
"by getnetworkinfo RPC) and to enter the node's mempool.\n"
2685+
"At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee\n"
2686+
"returned by getnetworkinfo) to enter the node's mempool.\n"
26872687
"\nArguments:\n"
26882688
"1. txid (string, required) The txid to be bumped\n"
26892689
"2. options (object, optional)\n"
@@ -2704,8 +2704,8 @@ UniValue bumpfee(const JSONRPCRequest& request)
27042704
"\nResult:\n"
27052705
"{\n"
27062706
" \"txid\": \"value\", (string) The id of the new transaction\n"
2707-
" \"oldfee\": n, (numeric) Fee of the replaced transaction\n"
2708-
" \"fee\": n, (numeric) Fee of the new transaction\n"
2707+
" \"origfee\": n, (numeric) Fee of the replaced transaction\n"
2708+
" \"fee\": n, (numeric) Fee of the new transaction\n"
27092709
"}\n"
27102710
"\nExamples:\n"
27112711
"\nBump the fee, get the new transaction\'s txid\n" +
@@ -2769,6 +2769,10 @@ UniValue bumpfee(const JSONRPCRequest& request)
27692769
throw JSONRPCError(RPC_MISC_ERROR, "Transaction does not have a change output");
27702770
}
27712771

2772+
// signature sizes can vary by a byte, so add 1 for each input when calculating the new fee
2773+
int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
2774+
const int64_t maxNewTxSize = txSize + wtx.tx->vin.size();
2775+
27722776
// optional parameters
27732777
bool specifiedConfirmTarget = false;
27742778
int newConfirmTarget = nTxConfirmTarget;
@@ -2794,10 +2798,11 @@ UniValue bumpfee(const JSONRPCRequest& request)
27942798
}
27952799
} else if (options.exists("totalFee")) {
27962800
totalFee = options["totalFee"].get_int64();
2797-
if (totalFee <= 0) {
2798-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid totalFee (cannot be <= 0)");
2799-
} else if (totalFee > maxTxFee) {
2800-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid totalFee (cannot be higher than maxTxFee)");
2801+
CAmount requiredFee = CWallet::GetRequiredFee(maxNewTxSize);
2802+
if (totalFee < requiredFee ) {
2803+
throw JSONRPCError(RPC_INVALID_PARAMETER,
2804+
strprintf("Insufficient totalFee (cannot be less than required fee %s)",
2805+
FormatMoney(requiredFee)));
28012806
}
28022807
}
28032808

@@ -2806,42 +2811,53 @@ UniValue bumpfee(const JSONRPCRequest& request)
28062811
}
28072812
}
28082813

2809-
// signature sizes can vary by a byte, so add 1 for each input when calculating the new fee
2810-
int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
2811-
const int64_t maxNewTxSize = txSize + wtx.tx->vin.size();
2812-
28132814
// calculate the old fee and fee-rate
28142815
CAmount nOldFee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut();
28152816
CFeeRate nOldFeeRate(nOldFee, txSize);
28162817
CAmount nNewFee;
28172818
CFeeRate nNewFeeRate;
2819+
// The wallet uses a conservative WALLET_INCREMENTAL_RELAY_FEE value to
2820+
// future proof against changes to network wide policy for incremental relay
2821+
// fee that our node may not be aware of.
2822+
CFeeRate walletIncrementalRelayFee = CFeeRate(WALLET_INCREMENTAL_RELAY_FEE);
2823+
if (::incrementalRelayFee > walletIncrementalRelayFee) {
2824+
walletIncrementalRelayFee = ::incrementalRelayFee;
2825+
}
28182826

28192827
if (totalFee > 0) {
2820-
CAmount minTotalFee = nOldFeeRate.GetFee(maxNewTxSize) + minRelayTxFee.GetFee(maxNewTxSize);
2828+
CAmount minTotalFee = nOldFeeRate.GetFee(maxNewTxSize) + ::incrementalRelayFee.GetFee(maxNewTxSize);
28212829
if (totalFee < minTotalFee) {
2822-
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid totalFee, must be at least %s (oldFee %s + relayFee %s)", FormatMoney(minTotalFee), nOldFeeRate.GetFee(maxNewTxSize), minRelayTxFee.GetFee(maxNewTxSize)));
2830+
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s)",
2831+
FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxNewTxSize)), FormatMoney(::incrementalRelayFee.GetFee(maxNewTxSize))));
28232832
}
28242833
nNewFee = totalFee;
28252834
nNewFeeRate = CFeeRate(totalFee, maxNewTxSize);
28262835
} else {
2827-
// use the user-defined payTxFee if possible, otherwise use smartfee / fallbackfee
2828-
if (!specifiedConfirmTarget && payTxFee.GetFeePerK() != 0) {
2829-
nNewFeeRate = payTxFee;
2830-
} else {
2831-
nNewFeeRate = mempool.estimateSmartFee(newConfirmTarget);
2836+
// if user specified a confirm target then don't consider any global payTxFee
2837+
if (specifiedConfirmTarget) {
2838+
nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, CAmount(0));
28322839
}
2833-
if (nNewFeeRate.GetFeePerK() == 0) {
2834-
nNewFeeRate = CWallet::fallbackFee;
2840+
// otherwise use the regular wallet logic to select payTxFee or default confirm target
2841+
else {
2842+
nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool);
28352843
}
28362844

2837-
// new fee rate must be at least old rate + minimum relay rate
2838-
if (nNewFeeRate.GetFeePerK() < nOldFeeRate.GetFeePerK() + ::minRelayTxFee.GetFeePerK()) {
2839-
nNewFeeRate = CFeeRate(nOldFeeRate.GetFeePerK() + ::minRelayTxFee.GetFeePerK());
2840-
}
2845+
nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize);
28412846

2842-
nNewFee = nNewFeeRate.GetFee(maxNewTxSize);
2847+
// New fee rate must be at least old rate + minimum incremental relay rate
2848+
if (nNewFeeRate.GetFeePerK() < nOldFeeRate.GetFeePerK() + walletIncrementalRelayFee.GetFeePerK()) {
2849+
nNewFeeRate = CFeeRate(nOldFeeRate.GetFeePerK() + walletIncrementalRelayFee.GetFeePerK());
2850+
nNewFee = nNewFeeRate.GetFee(maxNewTxSize);
2851+
}
28432852
}
28442853

2854+
// Check that in all cases the new fee doesn't violate maxTxFee
2855+
if (nNewFee > maxTxFee) {
2856+
throw JSONRPCError(RPC_MISC_ERROR,
2857+
strprintf("Specified or calculated fee %s is too high (cannot be higher than maxTxFee %s)",
2858+
FormatMoney(nNewFee), FormatMoney(maxTxFee)));
2859+
}
2860+
28452861
// check that fee rate is higher than mempool's minimum fee
28462862
// (no point in bumping fee if we know that the new tx won't be accepted to the mempool)
28472863
// This may occur if the user set TotalFee or paytxfee too low, if fallbackfee is too low, or, perhaps,
@@ -2864,7 +2880,7 @@ UniValue bumpfee(const JSONRPCRequest& request)
28642880

28652881
// If the output would become dust, discard it (converting the dust to fee)
28662882
poutput->nValue -= nDelta;
2867-
if (poutput->nValue <= poutput->GetDustThreshold(::minRelayTxFee)) {
2883+
if (poutput->nValue <= poutput->GetDustThreshold(::dustRelayFee)) {
28682884
LogPrint("rpc", "Bumping fee and discarding dust output\n");
28692885
nNewFee += poutput->nValue;
28702886
tx.vout.erase(tx.vout.begin() + nOutput);
@@ -2913,7 +2929,7 @@ UniValue bumpfee(const JSONRPCRequest& request)
29132929

29142930
UniValue result(UniValue::VOBJ);
29152931
result.push_back(Pair("txid", wtxBumped.GetHash().GetHex()));
2916-
result.push_back(Pair("oldfee", ValueFromAmount(nOldFee)));
2932+
result.push_back(Pair("origfee", ValueFromAmount(nOldFee)));
29172933
result.push_back(Pair("fee", ValueFromAmount(nNewFee)));
29182934

29192935
return result;

src/wallet/wallet.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2802,8 +2802,13 @@ CAmount CWallet::GetRequiredFee(unsigned int nTxBytes)
28022802

28032803
CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool)
28042804
{
2805-
// payTxFee is user-set "I want to pay this much"
2806-
CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes);
2805+
// payTxFee is the user-set global for desired feerate
2806+
return GetMinimumFee(nTxBytes, nConfirmTarget, pool, payTxFee.GetFee(nTxBytes));
2807+
}
2808+
2809+
CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, CAmount targetFee)
2810+
{
2811+
CAmount nFeeNeeded = targetFee;
28072812
// User didn't set: use -txconfirmtarget to estimate...
28082813
if (nFeeNeeded == 0) {
28092814
int estimateFoundTarget = nConfirmTarget;

src/wallet/wallet.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ static const CAmount DEFAULT_TRANSACTION_FEE = 0;
4848
static const CAmount DEFAULT_FALLBACK_FEE = 20000;
4949
//! -mintxfee default
5050
static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000;
51+
//! minimum recommended increment for BIP 125 replacement txs
52+
static const CAmount WALLET_INCREMENTAL_RELAY_FEE = 5000;
5153
//! target minimum change amount
5254
static const CAmount MIN_CHANGE = CENT;
5355
//! final minimum change amount after paying for fees
@@ -802,6 +804,11 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
802804
* and the required fee
803805
*/
804806
static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool);
807+
/**
808+
* Estimate the minimum fee considering required fee and targetFee or if 0
809+
* then fee estimation for nConfirmTarget
810+
*/
811+
static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, CAmount targetFee);
805812
/**
806813
* Return the minimum required fee taking into account the
807814
* floating relay fee and user set minimum transaction fee

0 commit comments

Comments
 (0)