@@ -342,6 +342,56 @@ static bool IsCurrentForFeeEstimation()
342
342
return true ;
343
343
}
344
344
345
+ /* Make mempool consistent after a reorg, by re-adding or recursively erasing
346
+ * disconnected block transactions from the mempool, and also removing any
347
+ * other transactions from the mempool that are no longer valid given the new
348
+ * tip/height.
349
+ *
350
+ * Note: we assume that disconnectpool only contains transactions that are NOT
351
+ * confirmed in the current chain nor already in the mempool (otherwise,
352
+ * in-mempool descendants of such transactions would be removed).
353
+ *
354
+ * Passing fAddToMempool=false will skip trying to add the transactions back,
355
+ * and instead just erase from the mempool as needed.
356
+ */
357
+
358
+ void UpdateMempoolForReorg (DisconnectedBlockTransactions &disconnectpool, bool fAddToMempool )
359
+ {
360
+ AssertLockHeld (cs_main);
361
+ std::vector<uint256> vHashUpdate;
362
+ // disconnectpool's insertion_order index sorts the entries from
363
+ // oldest to newest, but the oldest entry will be the last tx from the
364
+ // latest mined block that was disconnected.
365
+ // Iterate disconnectpool in reverse, so that we add transactions
366
+ // back to the mempool starting with the earliest transaction that had
367
+ // been previously seen in a block.
368
+ auto it = disconnectpool.queuedTx .get <insertion_order>().rbegin ();
369
+ while (it != disconnectpool.queuedTx .get <insertion_order>().rend ()) {
370
+ // ignore validation errors in resurrected transactions
371
+ CValidationState stateDummy;
372
+ if (!fAddToMempool || (*it)->IsCoinBase () || !AcceptToMemoryPool (mempool, stateDummy, *it, false , NULL , NULL , true )) {
373
+ // If the transaction doesn't make it in to the mempool, remove any
374
+ // transactions that depend on it (which would now be orphans).
375
+ mempool.removeRecursive (**it, MemPoolRemovalReason::REORG);
376
+ } else if (mempool.exists ((*it)->GetHash ())) {
377
+ vHashUpdate.push_back ((*it)->GetHash ());
378
+ }
379
+ ++it;
380
+ }
381
+ disconnectpool.queuedTx .clear ();
382
+ // AcceptToMemoryPool/addUnchecked all assume that new mempool entries have
383
+ // no in-mempool children, which is generally not true when adding
384
+ // previously-confirmed transactions back to the mempool.
385
+ // UpdateTransactionsFromBlock finds descendants of any transactions in
386
+ // the disconnectpool that were added back and cleans up the mempool state.
387
+ mempool.UpdateTransactionsFromBlock (vHashUpdate);
388
+
389
+ // We also need to remove any now-immature transactions
390
+ mempool.removeForReorg (pcoinsTip, chainActive.Tip ()->nHeight + 1 , STANDARD_LOCKTIME_VERIFY_FLAGS);
391
+ // Re-limit mempool size, in case we added any transactions
392
+ LimitMempoolSize (mempool, GetArg (" -maxmempool" , DEFAULT_MAX_MEMPOOL_SIZE) * 1000000 , GetArg (" -mempoolexpiry" , DEFAULT_MEMPOOL_EXPIRY) * 60 * 60 );
393
+ }
394
+
345
395
bool AcceptToMemoryPoolWorker (CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool fLimitFree ,
346
396
bool * pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
347
397
bool fOverrideMempoolLimit , const CAmount& nAbsurdFee, std::vector<uint256>& vHashTxnToUncache)
@@ -1877,8 +1927,17 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) {
1877
1927
1878
1928
}
1879
1929
1880
- /* * Disconnect chainActive's tip. You probably want to call mempool.removeForReorg and manually re-limit mempool size after this, with cs_main held. */
1881
- bool static DisconnectTip (CValidationState& state, const CChainParams& chainparams, bool fBare = false )
1930
+ /* * Disconnect chainActive's tip.
1931
+ * After calling, the mempool will be in an inconsistent state, with
1932
+ * transactions from disconnected blocks being added to disconnectpool. You
1933
+ * should make the mempool consistent again by calling UpdateMempoolForReorg.
1934
+ * with cs_main held.
1935
+ *
1936
+ * If disconnectpool is NULL, then no disconnected transactions are added to
1937
+ * disconnectpool (note that the caller is responsible for mempool consistency
1938
+ * in any case).
1939
+ */
1940
+ bool static DisconnectTip (CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions *disconnectpool)
1882
1941
{
1883
1942
CBlockIndex *pindexDelete = chainActive.Tip ();
1884
1943
assert (pindexDelete);
@@ -1901,25 +1960,17 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
1901
1960
if (!FlushStateToDisk (state, FLUSH_STATE_IF_NEEDED))
1902
1961
return false ;
1903
1962
1904
- if (!fBare ) {
1905
- // Resurrect mempool transactions from the disconnected block.
1906
- std::vector<uint256> vHashUpdate;
1907
- for (const auto & it : block.vtx ) {
1908
- const CTransaction& tx = *it;
1909
- // ignore validation errors in resurrected transactions
1910
- CValidationState stateDummy;
1911
- if (tx.IsCoinBase () || !AcceptToMemoryPool (mempool, stateDummy, it, false , NULL , NULL , true )) {
1912
- mempool.removeRecursive (tx, MemPoolRemovalReason::REORG);
1913
- } else if (mempool.exists (tx.GetHash ())) {
1914
- vHashUpdate.push_back (tx.GetHash ());
1915
- }
1963
+ if (disconnectpool) {
1964
+ // Save transactions to re-add to mempool at end of reorg
1965
+ for (auto it = block.vtx .rbegin (); it != block.vtx .rend (); ++it) {
1966
+ disconnectpool->addTransaction (*it);
1967
+ }
1968
+ while (disconnectpool->DynamicMemoryUsage () > MAX_DISCONNECTED_TX_POOL_SIZE * 1000 ) {
1969
+ // Drop the earliest entry, and remove its children from the mempool.
1970
+ auto it = disconnectpool->queuedTx .get <insertion_order>().begin ();
1971
+ mempool.removeRecursive (**it, MemPoolRemovalReason::REORG);
1972
+ disconnectpool->removeEntry (it);
1916
1973
}
1917
- // AcceptToMemoryPool/addUnchecked all assume that new mempool entries have
1918
- // no in-mempool children, which is generally not true when adding
1919
- // previously-confirmed transactions back to the mempool.
1920
- // UpdateTransactionsFromBlock finds descendants of any transactions in this
1921
- // block that were added back and cleans up the mempool state.
1922
- mempool.UpdateTransactionsFromBlock (vHashUpdate);
1923
1974
}
1924
1975
1925
1976
// Update chainActive and related variables.
@@ -2007,7 +2058,7 @@ class ConnectTrace {
2007
2058
*
2008
2059
* The block is added to connectTrace if connection succeeds.
2009
2060
*/
2010
- bool static ConnectTip (CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace)
2061
+ bool static ConnectTip (CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool )
2011
2062
{
2012
2063
assert (pindexNew->pprev == chainActive.Tip ());
2013
2064
// Read block from disk.
@@ -2049,6 +2100,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
2049
2100
LogPrint (BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs]\n " , (nTime5 - nTime4) * 0.001 , nTimeChainState * 0.000001 );
2050
2101
// Remove conflicting transactions from the mempool.;
2051
2102
mempool.removeForBlock (blockConnecting.vtx , pindexNew->nHeight );
2103
+ disconnectpool.removeForBlock (blockConnecting.vtx );
2052
2104
// Update chainActive & related variables.
2053
2105
UpdateTip (pindexNew, chainparams);
2054
2106
@@ -2142,9 +2194,14 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
2142
2194
2143
2195
// Disconnect active blocks which are no longer in the best chain.
2144
2196
bool fBlocksDisconnected = false ;
2197
+ DisconnectedBlockTransactions disconnectpool;
2145
2198
while (chainActive.Tip () && chainActive.Tip () != pindexFork) {
2146
- if (!DisconnectTip (state, chainparams))
2199
+ if (!DisconnectTip (state, chainparams, &disconnectpool)) {
2200
+ // This is likely a fatal error, but keep the mempool consistent,
2201
+ // just in case. Only remove from the mempool in this case.
2202
+ UpdateMempoolForReorg (disconnectpool, false );
2147
2203
return false ;
2204
+ }
2148
2205
fBlocksDisconnected = true ;
2149
2206
}
2150
2207
@@ -2167,7 +2224,7 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
2167
2224
2168
2225
// Connect new blocks.
2169
2226
BOOST_REVERSE_FOREACH (CBlockIndex *pindexConnect, vpindexToConnect) {
2170
- if (!ConnectTip (state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace)) {
2227
+ if (!ConnectTip (state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool )) {
2171
2228
if (state.IsInvalid ()) {
2172
2229
// The block violates a consensus rule.
2173
2230
if (!state.CorruptionPossible ())
@@ -2178,6 +2235,9 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
2178
2235
break ;
2179
2236
} else {
2180
2237
// A system error occurred (disk space, database error, ...).
2238
+ // Make the mempool consistent with the current tip, just in case
2239
+ // any observers try to use it before shutdown.
2240
+ UpdateMempoolForReorg (disconnectpool, false );
2181
2241
return false ;
2182
2242
}
2183
2243
} else {
@@ -2192,8 +2252,9 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
2192
2252
}
2193
2253
2194
2254
if (fBlocksDisconnected ) {
2195
- mempool.removeForReorg (pcoinsTip, chainActive.Tip ()->nHeight + 1 , STANDARD_LOCKTIME_VERIFY_FLAGS);
2196
- LimitMempoolSize (mempool, GetArg (" -maxmempool" , DEFAULT_MAX_MEMPOOL_SIZE) * 1000000 , GetArg (" -mempoolexpiry" , DEFAULT_MEMPOOL_EXPIRY) * 60 * 60 );
2255
+ // If any blocks were disconnected, disconnectpool may be non empty. Add
2256
+ // any disconnected transactions back to the mempool.
2257
+ UpdateMempoolForReorg (disconnectpool, true );
2197
2258
}
2198
2259
mempool.check (pcoinsTip);
2199
2260
@@ -2342,20 +2403,25 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
2342
2403
setDirtyBlockIndex.insert (pindex);
2343
2404
setBlockIndexCandidates.erase (pindex);
2344
2405
2406
+ DisconnectedBlockTransactions disconnectpool;
2345
2407
while (chainActive.Contains (pindex)) {
2346
2408
CBlockIndex *pindexWalk = chainActive.Tip ();
2347
2409
pindexWalk->nStatus |= BLOCK_FAILED_CHILD;
2348
2410
setDirtyBlockIndex.insert (pindexWalk);
2349
2411
setBlockIndexCandidates.erase (pindexWalk);
2350
2412
// ActivateBestChain considers blocks already in chainActive
2351
2413
// unconditionally valid already, so force disconnect away from it.
2352
- if (!DisconnectTip (state, chainparams)) {
2353
- mempool.removeForReorg (pcoinsTip, chainActive.Tip ()->nHeight + 1 , STANDARD_LOCKTIME_VERIFY_FLAGS);
2414
+ if (!DisconnectTip (state, chainparams, &disconnectpool)) {
2415
+ // It's probably hopeless to try to make the mempool consistent
2416
+ // here if DisconnectTip failed, but we can try.
2417
+ UpdateMempoolForReorg (disconnectpool, false );
2354
2418
return false ;
2355
2419
}
2356
2420
}
2357
2421
2358
- LimitMempoolSize (mempool, GetArg (" -maxmempool" , DEFAULT_MAX_MEMPOOL_SIZE) * 1000000 , GetArg (" -mempoolexpiry" , DEFAULT_MEMPOOL_EXPIRY) * 60 * 60 );
2422
+ // DisconnectTip will add transactions to disconnectpool; try to add these
2423
+ // back to the mempool.
2424
+ UpdateMempoolForReorg (disconnectpool, true );
2359
2425
2360
2426
// The resulting new best tip may not be in setBlockIndexCandidates anymore, so
2361
2427
// add it again.
@@ -2368,7 +2434,6 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
2368
2434
}
2369
2435
2370
2436
InvalidChainFound (pindex);
2371
- mempool.removeForReorg (pcoinsTip, chainActive.Tip ()->nHeight + 1 , STANDARD_LOCKTIME_VERIFY_FLAGS);
2372
2437
uiInterface.NotifyBlockTip (IsInitialBlockDownload (), pindex->pprev );
2373
2438
return true ;
2374
2439
}
@@ -3482,7 +3547,7 @@ bool RewindBlockIndex(const CChainParams& params)
3482
3547
// of the blockchain).
3483
3548
break ;
3484
3549
}
3485
- if (!DisconnectTip (state, params, true )) {
3550
+ if (!DisconnectTip (state, params, NULL )) {
3486
3551
return error (" RewindBlockIndex: unable to disconnect block at height %i" , pindex->nHeight );
3487
3552
}
3488
3553
// Occasionally flush state to disk.
0 commit comments