diff --git a/doc/REST-interface.md b/doc/REST-interface.md --- a/doc/REST-interface.md +++ b/doc/REST-interface.md @@ -97,10 +97,7 @@ Returns various information about the TX mempool. Only supports JSON as output format. -* loaded : (boolean) if the mempool is fully loaded -* size : (numeric) the number of transactions in the TX mempool -* bytes : (numeric) size of the TX mempool in bytes -* usage : (numeric) total TX mempool memory usage +Refer to the `getmempoolinfo` RPC for documentation of the fields. `GET /rest/mempool/contents.json` diff --git a/doc/release-notes.md b/doc/release-notes.md --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -5,3 +5,5 @@ This release includes the following features and fixes: +- A new `total_fee` field showing the total fees for all transactions in the + mempool has been added to the `getmempoolinfo` RPC. diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -2018,6 +2018,7 @@ ret.pushKV("size", (int64_t)pool.size()); ret.pushKV("bytes", (int64_t)pool.GetTotalTxSize()); ret.pushKV("usage", (int64_t)pool.DynamicMemoryUsage()); + ret.pushKV("total_fee", pool.GetTotalFee()); size_t maxmempool = gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; ret.pushKV("maxmempool", (int64_t)maxmempool); @@ -2030,6 +2031,7 @@ } static RPCHelpMan getmempoolinfo() { + const auto &ticker = Currency::get().ticker; return RPCHelpMan{ "getmempoolinfo", "Returns details on the active state of the TX memory pool.\n", @@ -2047,8 +2049,11 @@ "Total memory usage for the mempool"}, {RPCResult::Type::NUM, "maxmempool", "Maximum memory usage for the mempool"}, + {RPCResult::Type::STR_AMOUNT, "total_fee", + "Total fees for the mempool in " + ticker + + ", ignoring modified fees through prioritizetransaction"}, {RPCResult::Type::STR_AMOUNT, "mempoolminfee", - "Minimum fee rate in " + Currency::get().ticker + + "Minimum fee rate in " + ticker + "/kB for tx to be accepted. Is the maximum of " "minrelaytxfee and minimum mempool fee"}, {RPCResult::Type::STR_AMOUNT, "minrelaytxfee", diff --git a/src/txmempool.h b/src/txmempool.h --- a/src/txmempool.h +++ b/src/txmempool.h @@ -502,10 +502,12 @@ std::atomic nTransactionsUpdated{0}; //! sum of all mempool tx's sizes. - uint64_t totalTxSize; + uint64_t totalTxSize GUARDED_BY(cs); + //! sum of all mempool tx's fees (NOT modified fee) + Amount m_total_fee GUARDED_BY(cs); //! sum of dynamic memory usage of all the map elements (NOT the maps //! themselves) - uint64_t cachedInnerUsage; + uint64_t cachedInnerUsage GUARDED_BY(cs); mutable int64_t lastRollingFeeUpdate GUARDED_BY(cs); mutable bool blockSinceLastRollingFeeBump GUARDED_BY(cs); @@ -792,6 +794,11 @@ return totalTxSize; } + Amount GetTotalFee() const EXCLUSIVE_LOCKS_REQUIRED(cs) { + AssertLockHeld(cs); + return m_total_fee; + } + bool exists(const TxId &txid) const { LOCK(cs); return mapTx.count(txid) != 0; diff --git a/src/txmempool.cpp b/src/txmempool.cpp --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -461,6 +461,7 @@ nTransactionsUpdated++; totalTxSize += entry.GetTxSize(); + m_total_fee += entry.GetFee(); vTxHashes.emplace_back(tx.GetHash(), newit); newit->vTxHashesIdx = vTxHashes.size() - 1; @@ -499,6 +500,7 @@ } totalTxSize -= it->GetTxSize(); + m_total_fee -= it->GetFee(); cachedInnerUsage -= it->DynamicMemoryUsage(); cachedInnerUsage -= memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst()); @@ -687,6 +689,7 @@ mapNextTx.clear(); vTxHashes.clear(); totalTxSize = 0; + m_total_fee = Amount::zero(); cachedInnerUsage = 0; lastRollingFeeUpdate = GetTime(); blockSinceLastRollingFeeBump = false; @@ -729,6 +732,7 @@ (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); uint64_t checkTotal = 0; + Amount check_total_fee{Amount::zero()}; uint64_t innerUsage = 0; CCoinsViewCache &active_coins_tip = active_chainstate.CoinsTip(); @@ -741,6 +745,7 @@ it != mapTx.end(); it++) { unsigned int i = 0; checkTotal += it->GetTxSize(); + check_total_fee += it->GetFee(); innerUsage += it->DynamicMemoryUsage(); const CTransaction &tx = it->GetTx(); innerUsage += memusage::DynamicUsage(it->GetMemPoolParentsConst()) + @@ -854,6 +859,7 @@ } assert(totalTxSize == checkTotal); + assert(m_total_fee == check_total_fee); assert(innerUsage == cachedInnerUsage); } diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py --- a/test/functional/mempool_persist.py +++ b/test/functional/mempool_persist.py @@ -71,6 +71,8 @@ assert_equal(len(self.nodes[0].getrawmempool()), 5) assert_equal(len(self.nodes[1].getrawmempool()), 5) + total_fee_old = self.nodes[0].getmempoolinfo()['total_fee'] + self.log.debug("Prioritize a transaction on node0") fees = self.nodes[0].getmempoolentry(txid=last_txid)['fees'] assert_equal(fees['base'], fees['modified']) @@ -78,6 +80,15 @@ fees = self.nodes[0].getmempoolentry(txid=last_txid)['fees'] assert_equal(fees['base'] + Decimal('10.0'), fees['modified']) + self.log.info( + 'Check the total base fee is unchanged after prioritisetransaction') + assert_equal( + total_fee_old, + self.nodes[0].getmempoolinfo()['total_fee']) + assert_equal(total_fee_old, + sum(v['fees']['base'] for k, v + in self.nodes[0].getrawmempool(verbose=True).items())) + tx_creation_time = self.nodes[0].getmempoolentry(txid=last_txid)[ 'time'] assert_greater_than_or_equal(tx_creation_time, tx_creation_time_lower)