@@ -539,6 +539,56 @@ static bool IsCurrentForFeeEstimation()
539
539
return true ;
540
540
}
541
541
542
+ /* Make mempool consistent after a reorg, by re-adding or recursively erasing
543
+ * disconnected block transactions from the mempool, and also removing any
544
+ * other transactions from the mempool that are no longer valid given the new
545
+ * tip/height.
546
+ *
547
+ * Note: we assume that disconnectpool only contains transactions that are NOT
548
+ * confirmed in the current chain nor already in the mempool (otherwise,
549
+ * in-mempool descendants of such transactions would be removed).
550
+ *
551
+ * Passing fAddToMempool=false will skip trying to add the transactions back,
552
+ * and instead just erase from the mempool as needed.
553
+ */
554
+
555
+ void UpdateMempoolForReorg (DisconnectedBlockTransactions &disconnectpool, bool fAddToMempool )
556
+ {
557
+ AssertLockHeld (cs_main);
558
+ std::vector<uint256> vHashUpdate;
559
+ // disconnectpool's insertion_order index sorts the entries from
560
+ // oldest to newest, but the oldest entry will be the last tx from the
561
+ // latest mined block that was disconnected.
562
+ // Iterate disconnectpool in reverse, so that we add transactions
563
+ // back to the mempool starting with the earliest transaction that had
564
+ // been previously seen in a block.
565
+ auto it = disconnectpool.queuedTx .get <insertion_order>().rbegin ();
566
+ while (it != disconnectpool.queuedTx .get <insertion_order>().rend ()) {
567
+ // ignore validation errors in resurrected transactions
568
+ CValidationState stateDummy;
569
+ if (!fAddToMempool || (*it)->IsCoinBase () || !AcceptToMemoryPool (mempool, stateDummy, *it, false , NULL , NULL , true )) {
570
+ // If the transaction doesn't make it in to the mempool, remove any
571
+ // transactions that depend on it (which would now be orphans).
572
+ mempool.removeRecursive (**it, MemPoolRemovalReason::REORG);
573
+ } else if (mempool.exists ((*it)->GetHash ())) {
574
+ vHashUpdate.push_back ((*it)->GetHash ());
575
+ }
576
+ ++it;
577
+ }
578
+ disconnectpool.queuedTx .clear ();
579
+ // AcceptToMemoryPool/addUnchecked all assume that new mempool entries have
580
+ // no in-mempool children, which is generally not true when adding
581
+ // previously-confirmed transactions back to the mempool.
582
+ // UpdateTransactionsFromBlock finds descendants of any transactions in
583
+ // the disconnectpool that were added back and cleans up the mempool state.
584
+ mempool.UpdateTransactionsFromBlock (vHashUpdate);
585
+
586
+ // We also need to remove any now-immature transactions
587
+ mempool.removeForReorg (pcoinsTip, chainActive.Tip ()->nHeight + 1 , STANDARD_LOCKTIME_VERIFY_FLAGS);
588
+ // Re-limit mempool size, in case we added any transactions
589
+ LimitMempoolSize (mempool, GetArg (" -maxmempool" , DEFAULT_MAX_MEMPOOL_SIZE) * 1000000 , GetArg (" -mempoolexpiry" , DEFAULT_MEMPOOL_EXPIRY) * 60 * 60 );
590
+ }
591
+
542
592
bool AcceptToMemoryPoolWorker (CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool fLimitFree ,
543
593
bool * pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced,
544
594
bool fOverrideMempoolLimit , const CAmount& nAbsurdFee, std::vector<uint256>& vHashTxnToUncache)
@@ -2119,8 +2169,17 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) {
2119
2169
2120
2170
}
2121
2171
2122
- /* * Disconnect chainActive's tip. You probably want to call mempool.removeForReorg and manually re-limit mempool size after this, with cs_main held. */
2123
- bool static DisconnectTip (CValidationState& state, const CChainParams& chainparams, bool fBare = false )
2172
+ /* * Disconnect chainActive's tip.
2173
+ * After calling, the mempool will be in an inconsistent state, with
2174
+ * transactions from disconnected blocks being added to disconnectpool. You
2175
+ * should make the mempool consistent again by calling UpdateMempoolForReorg.
2176
+ * with cs_main held.
2177
+ *
2178
+ * If disconnectpool is NULL, then no disconnected transactions are added to
2179
+ * disconnectpool (note that the caller is responsible for mempool consistency
2180
+ * in any case).
2181
+ */
2182
+ bool static DisconnectTip (CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions *disconnectpool)
2124
2183
{
2125
2184
CBlockIndex *pindexDelete = chainActive.Tip ();
2126
2185
assert (pindexDelete);
@@ -2143,25 +2202,17 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
2143
2202
if (!FlushStateToDisk (state, FLUSH_STATE_IF_NEEDED))
2144
2203
return false ;
2145
2204
2146
- if (!fBare ) {
2147
- // Resurrect mempool transactions from the disconnected block.
2148
- std::vector<uint256> vHashUpdate;
2149
- for (const auto & it : block.vtx ) {
2150
- const CTransaction& tx = *it;
2151
- // ignore validation errors in resurrected transactions
2152
- CValidationState stateDummy;
2153
- if (tx.IsCoinBase () || !AcceptToMemoryPool (mempool, stateDummy, it, false , NULL , NULL , true )) {
2154
- mempool.removeRecursive (tx, MemPoolRemovalReason::REORG);
2155
- } else if (mempool.exists (tx.GetHash ())) {
2156
- vHashUpdate.push_back (tx.GetHash ());
2157
- }
2205
+ if (disconnectpool) {
2206
+ // Save transactions to re-add to mempool at end of reorg
2207
+ for (auto it = block.vtx .rbegin (); it != block.vtx .rend (); ++it) {
2208
+ disconnectpool->addTransaction (*it);
2209
+ }
2210
+ while (disconnectpool->DynamicMemoryUsage () > MAX_DISCONNECTED_TX_POOL_SIZE * 1000 ) {
2211
+ // Drop the earliest entry, and remove its children from the mempool.
2212
+ auto it = disconnectpool->queuedTx .get <insertion_order>().begin ();
2213
+ mempool.removeRecursive (**it, MemPoolRemovalReason::REORG);
2214
+ disconnectpool->removeEntry (it);
2158
2215
}
2159
- // AcceptToMemoryPool/addUnchecked all assume that new mempool entries have
2160
- // no in-mempool children, which is generally not true when adding
2161
- // previously-confirmed transactions back to the mempool.
2162
- // UpdateTransactionsFromBlock finds descendants of any transactions in this
2163
- // block that were added back and cleans up the mempool state.
2164
- mempool.UpdateTransactionsFromBlock (vHashUpdate);
2165
2216
}
2166
2217
2167
2218
// Update chainActive and related variables.
@@ -2249,7 +2300,7 @@ class ConnectTrace {
2249
2300
*
2250
2301
* The block is added to connectTrace if connection succeeds.
2251
2302
*/
2252
- bool static ConnectTip (CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace)
2303
+ bool static ConnectTip (CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool )
2253
2304
{
2254
2305
assert (pindexNew->pprev == chainActive.Tip ());
2255
2306
// Read block from disk.
@@ -2291,6 +2342,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
2291
2342
LogPrint (BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs]\n " , (nTime5 - nTime4) * 0.001 , nTimeChainState * 0.000001 );
2292
2343
// Remove conflicting transactions from the mempool.;
2293
2344
mempool.removeForBlock (blockConnecting.vtx , pindexNew->nHeight );
2345
+ disconnectpool.removeForBlock (blockConnecting.vtx );
2294
2346
// Update chainActive & related variables.
2295
2347
UpdateTip (pindexNew, chainparams);
2296
2348
@@ -2384,9 +2436,14 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
2384
2436
2385
2437
// Disconnect active blocks which are no longer in the best chain.
2386
2438
bool fBlocksDisconnected = false ;
2439
+ DisconnectedBlockTransactions disconnectpool;
2387
2440
while (chainActive.Tip () && chainActive.Tip () != pindexFork) {
2388
- if (!DisconnectTip (state, chainparams))
2441
+ if (!DisconnectTip (state, chainparams, &disconnectpool)) {
2442
+ // This is likely a fatal error, but keep the mempool consistent,
2443
+ // just in case. Only remove from the mempool in this case.
2444
+ UpdateMempoolForReorg (disconnectpool, false );
2389
2445
return false ;
2446
+ }
2390
2447
fBlocksDisconnected = true ;
2391
2448
}
2392
2449
@@ -2409,7 +2466,7 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
2409
2466
2410
2467
// Connect new blocks.
2411
2468
BOOST_REVERSE_FOREACH (CBlockIndex *pindexConnect, vpindexToConnect) {
2412
- if (!ConnectTip (state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace)) {
2469
+ if (!ConnectTip (state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool )) {
2413
2470
if (state.IsInvalid ()) {
2414
2471
// The block violates a consensus rule.
2415
2472
if (!state.CorruptionPossible ())
@@ -2420,6 +2477,9 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
2420
2477
break ;
2421
2478
} else {
2422
2479
// A system error occurred (disk space, database error, ...).
2480
+ // Make the mempool consistent with the current tip, just in case
2481
+ // any observers try to use it before shutdown.
2482
+ UpdateMempoolForReorg (disconnectpool, false );
2423
2483
return false ;
2424
2484
}
2425
2485
} else {
@@ -2434,8 +2494,9 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
2434
2494
}
2435
2495
2436
2496
if (fBlocksDisconnected ) {
2437
- mempool.removeForReorg (pcoinsTip, chainActive.Tip ()->nHeight + 1 , STANDARD_LOCKTIME_VERIFY_FLAGS);
2438
- LimitMempoolSize (mempool, GetArg (" -maxmempool" , DEFAULT_MAX_MEMPOOL_SIZE) * 1000000 , GetArg (" -mempoolexpiry" , DEFAULT_MEMPOOL_EXPIRY) * 60 * 60 );
2497
+ // If any blocks were disconnected, disconnectpool may be non empty. Add
2498
+ // any disconnected transactions back to the mempool.
2499
+ UpdateMempoolForReorg (disconnectpool, true );
2439
2500
}
2440
2501
mempool.check (pcoinsTip);
2441
2502
@@ -2584,20 +2645,25 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
2584
2645
setDirtyBlockIndex.insert (pindex);
2585
2646
setBlockIndexCandidates.erase (pindex);
2586
2647
2648
+ DisconnectedBlockTransactions disconnectpool;
2587
2649
while (chainActive.Contains (pindex)) {
2588
2650
CBlockIndex *pindexWalk = chainActive.Tip ();
2589
2651
pindexWalk->nStatus |= BLOCK_FAILED_CHILD;
2590
2652
setDirtyBlockIndex.insert (pindexWalk);
2591
2653
setBlockIndexCandidates.erase (pindexWalk);
2592
2654
// ActivateBestChain considers blocks already in chainActive
2593
2655
// unconditionally valid already, so force disconnect away from it.
2594
- if (!DisconnectTip (state, chainparams)) {
2595
- mempool.removeForReorg (pcoinsTip, chainActive.Tip ()->nHeight + 1 , STANDARD_LOCKTIME_VERIFY_FLAGS);
2656
+ if (!DisconnectTip (state, chainparams, &disconnectpool)) {
2657
+ // It's probably hopeless to try to make the mempool consistent
2658
+ // here if DisconnectTip failed, but we can try.
2659
+ UpdateMempoolForReorg (disconnectpool, false );
2596
2660
return false ;
2597
2661
}
2598
2662
}
2599
2663
2600
- LimitMempoolSize (mempool, GetArg (" -maxmempool" , DEFAULT_MAX_MEMPOOL_SIZE) * 1000000 , GetArg (" -mempoolexpiry" , DEFAULT_MEMPOOL_EXPIRY) * 60 * 60 );
2664
+ // DisconnectTip will add transactions to disconnectpool; try to add these
2665
+ // back to the mempool.
2666
+ UpdateMempoolForReorg (disconnectpool, true );
2601
2667
2602
2668
// The resulting new best tip may not be in setBlockIndexCandidates anymore, so
2603
2669
// add it again.
@@ -2610,7 +2676,6 @@ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, C
2610
2676
}
2611
2677
2612
2678
InvalidChainFound (pindex);
2613
- mempool.removeForReorg (pcoinsTip, chainActive.Tip ()->nHeight + 1 , STANDARD_LOCKTIME_VERIFY_FLAGS);
2614
2679
uiInterface.NotifyBlockTip (IsInitialBlockDownload (), pindex->pprev );
2615
2680
return true ;
2616
2681
}
@@ -3724,7 +3789,7 @@ bool RewindBlockIndex(const CChainParams& params)
3724
3789
// of the blockchain).
3725
3790
break ;
3726
3791
}
3727
- if (!DisconnectTip (state, params, true )) {
3792
+ if (!DisconnectTip (state, params, NULL )) {
3728
3793
return error (" RewindBlockIndex: unable to disconnect block at height %i" , pindex->nHeight );
3729
3794
}
3730
3795
// Occasionally flush state to disk.
0 commit comments