Skip to content

Commit 3b20e23

Browse files
committed
Merge pull request #6722
58254aa Fix stale comment in CTxMemPool::TrimToSize. (Matt Corallo) 2bc5018 Fix comment formatting tabs (Matt Corallo) 8abe0f5 Undo GetMinFee-requires-extra-call-to-hit-0 (Matt Corallo) 9e93640 Drop minRelayTxFee to 1000 (Matt Corallo) 074cb15 Add reasonable test case for mempool trimming (Matt Corallo) d355cf4 Only call TrimToSize once per reorg/blocks disconnect (Matt Corallo) 794a8ce Implement on-the-fly mempool size limitation. (Matt Corallo) e6c7b36 Print mempool size in KB when adding txn (Matt Corallo) 241d607 Add CFeeRate += operator (Matt Corallo) e8bcdce Track (and define) ::minRelayTxFee in CTxMemPool (Matt Corallo) 9c9b66f Fix calling mempool directly, instead of pool, in ATMP (Matt Corallo) 49b6fd5 Add Mempool Expire function to remove old transactions (Pieter Wuille) 78b82f4 Reverse the sort on the mempool's feerate index (Suhas Daftuar)
2 parents c6de5cc + 58254aa commit 3b20e23

File tree

10 files changed

+361
-57
lines changed

10 files changed

+361
-57
lines changed

src/amount.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class CFeeRate
5151
friend bool operator==(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK == b.nSatoshisPerK; }
5252
friend bool operator<=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK <= b.nSatoshisPerK; }
5353
friend bool operator>=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK >= b.nSatoshisPerK; }
54+
CFeeRate& operator+=(const CFeeRate& a) { nSatoshisPerK += a.nSatoshisPerK; return *this; }
5455
std::string ToString() const;
5556

5657
ADD_SERIALIZE_METHODS;

src/init.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,8 @@ std::string HelpMessage(HelpMessageMode mode)
320320
strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache));
321321
strUsage += HelpMessageOpt("-loadblock=<file>", _("Imports blocks from external blk000??.dat file") + " " + _("on startup"));
322322
strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS));
323+
strUsage += HelpMessageOpt("-maxmempool=<n>", strprintf(_("Keep the transaction memory pool below <n> megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE));
324+
strUsage += HelpMessageOpt("-mempoolexpiry=<n>", strprintf(_("Do not keep transactions in the mempool longer than <n> hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY));
323325
strUsage += HelpMessageOpt("-par=<n>", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"),
324326
-GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS));
325327
#ifndef WIN32
@@ -840,6 +842,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
840842
fCheckBlockIndex = GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
841843
fCheckpointsEnabled = GetBoolArg("-checkpoints", true);
842844

845+
// -mempoollimit limits
846+
int64_t nMempoolSizeLimit = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
847+
int64_t nMempoolDescendantSizeLimit = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000;
848+
if (nMempoolSizeLimit < 0 || nMempoolSizeLimit < nMempoolDescendantSizeLimit * 40)
849+
return InitError(strprintf(_("Error: -maxmempool must be at least %d MB"), GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) / 25));
850+
843851
// -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
844852
nScriptCheckThreads = GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
845853
if (nScriptCheckThreads <= 0)

src/main.cpp

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ uint64_t nPruneTarget = 0;
7575
bool fAlerts = DEFAULT_ALERTS;
7676

7777
/** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */
78-
CFeeRate minRelayTxFee = CFeeRate(5000);
78+
CFeeRate minRelayTxFee = CFeeRate(1000);
7979

8080
CTxMemPool mempool(::minRelayTxFee);
8181

@@ -740,17 +740,14 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
740740
return true;
741741
}
742742

743-
CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree)
743+
CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned int nBytes, bool fAllowFree)
744744
{
745-
{
746-
LOCK(mempool.cs);
747-
uint256 hash = tx.GetHash();
748-
double dPriorityDelta = 0;
749-
CAmount nFeeDelta = 0;
750-
mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
751-
if (dPriorityDelta > 0 || nFeeDelta > 0)
752-
return 0;
753-
}
745+
uint256 hash = tx.GetHash();
746+
double dPriorityDelta = 0;
747+
CAmount nFeeDelta = 0;
748+
pool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
749+
if (dPriorityDelta > 0 || nFeeDelta > 0)
750+
return 0;
754751

755752
CAmount nMinFee = ::minRelayTxFee.GetFee(nBytes);
756753

@@ -779,7 +776,7 @@ static std::string FormatStateMessage(const CValidationState &state)
779776
}
780777

781778
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
782-
bool* pfMissingInputs, bool fRejectAbsurdFee)
779+
bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee)
783780
{
784781
AssertLockHeld(cs_main);
785782
if (pfMissingInputs)
@@ -879,17 +876,20 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
879876
CAmount nFees = nValueIn-nValueOut;
880877
double dPriority = view.GetPriority(tx, chainActive.Height());
881878

882-
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), mempool.HasNoInputsOf(tx));
879+
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx));
883880
unsigned int nSize = entry.GetTxSize();
884881

885882
// Don't accept it if it can't get into a block
886-
CAmount txMinFee = GetMinRelayFee(tx, nSize, true);
883+
CAmount txMinFee = GetMinRelayFee(tx, pool, nSize, true);
887884
if (fLimitFree && nFees < txMinFee)
888885
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee", false,
889886
strprintf("%d < %d", nFees, txMinFee));
890887

891-
// Require that free transactions have sufficient priority to be mined in the next block.
892-
if (GetBoolArg("-relaypriority", true) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) {
888+
CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
889+
if (mempoolRejectFee > 0 && nFees < mempoolRejectFee) {
890+
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee));
891+
} else if (GetBoolArg("-relaypriority", true) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) {
892+
// Require that free transactions have sufficient priority to be mined in the next block.
893893
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority");
894894
}
895895

@@ -954,6 +954,17 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
954954

955955
// Store transaction in memory
956956
pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload());
957+
958+
// trim mempool and check if tx was trimmed
959+
if (!fOverrideMempoolLimit) {
960+
int expired = pool.Expire(GetTime() - GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
961+
if (expired != 0)
962+
LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired);
963+
964+
pool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
965+
if (!pool.exists(tx.GetHash()))
966+
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full");
967+
}
957968
}
958969

959970
SyncWithWallets(tx, NULL);
@@ -2020,7 +2031,7 @@ void static UpdateTip(CBlockIndex *pindexNew) {
20202031
}
20212032
}
20222033

2023-
/** Disconnect chainActive's tip. */
2034+
/** Disconnect chainActive's tip. You want to manually re-limit mempool size after this */
20242035
bool static DisconnectTip(CValidationState &state) {
20252036
CBlockIndex *pindexDelete = chainActive.Tip();
20262037
assert(pindexDelete);
@@ -2047,7 +2058,7 @@ bool static DisconnectTip(CValidationState &state) {
20472058
// ignore validation errors in resurrected transactions
20482059
list<CTransaction> removed;
20492060
CValidationState stateDummy;
2050-
if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) {
2061+
if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, true)) {
20512062
mempool.remove(tx, removed, true);
20522063
} else if (mempool.exists(tx.GetHash())) {
20532064
vHashUpdate.push_back(tx.GetHash());
@@ -2220,9 +2231,11 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo
22202231
const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork);
22212232

22222233
// Disconnect active blocks which are no longer in the best chain.
2234+
bool fBlocksDisconnected = false;
22232235
while (chainActive.Tip() && chainActive.Tip() != pindexFork) {
22242236
if (!DisconnectTip(state))
22252237
return false;
2238+
fBlocksDisconnected = true;
22262239
}
22272240

22282241
// Build list of new blocks to connect.
@@ -2268,6 +2281,9 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo
22682281
}
22692282
}
22702283

2284+
if (fBlocksDisconnected)
2285+
mempool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
2286+
22712287
// Callbacks/notifications for a new best chain.
22722288
if (fInvalidFound)
22732289
CheckForkWarningConditionsOnNewFork(vpindexToConnect.back());
@@ -2354,6 +2370,8 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) {
23542370
}
23552371
}
23562372

2373+
mempool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
2374+
23572375
// The resulting new best tip may not be in setBlockIndexCandidates anymore, so
23582376
// add it again.
23592377
BlockMap::iterator it = mapBlockIndex.begin();
@@ -4290,10 +4308,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
42904308
RelayTransaction(tx);
42914309
vWorkQueue.push_back(inv.hash);
42924310

4293-
LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u)\n",
4311+
LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n",
42944312
pfrom->id,
42954313
tx.GetHash().ToString(),
4296-
mempool.size());
4314+
mempool.size(), mempool.DynamicMemoryUsage() / 1000);
42974315

42984316
// Recursively process any orphan transactions that depended on this one
42994317
set<NodeId> setMisbehaving;

src/main.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ static const unsigned int DEFAULT_ANCESTOR_SIZE_LIMIT = 900;
5151
static const unsigned int DEFAULT_DESCENDANT_LIMIT = 1000;
5252
/** Default for -limitdescendantsize, maximum kilobytes of in-mempool descendants */
5353
static const unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT = 2500;
54+
/** Default for -maxmempool, maximum megabytes of mempool memory usage */
55+
static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300;
56+
/** Default for -mempoolexpiry, expiration time for mempool transactions in hours */
57+
static const unsigned int DEFAULT_MEMPOOL_EXPIRY = 72;
5458
/** The maximum size of a blk?????.dat file (since 0.8) */
5559
static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
5660
/** The pre-allocation chunk size for blk?????.dat files (since 0.8) */
@@ -225,7 +229,7 @@ void PruneAndFlush();
225229

226230
/** (try to) add transaction to memory pool **/
227231
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
228-
bool* pfMissingInputs, bool fRejectAbsurdFee=false);
232+
bool* pfMissingInputs, bool fOverrideMempoolLimit=false, bool fRejectAbsurdFee=false);
229233

230234

231235
struct CNodeStateStats {

src/rpcrawtransaction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,7 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp)
809809
// push to local node and sync with wallets
810810
CValidationState state;
811811
bool fMissingInputs;
812-
if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, !fOverrideFees)) {
812+
if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, false, !fOverrideFees)) {
813813
if (state.IsInvalid()) {
814814
throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
815815
} else {

0 commit comments

Comments
 (0)