From cb1407196fba648aa75504e3ab3d46aa0181563a Mon Sep 17 00:00:00 2001 From: glozow Date: Thu, 30 Sep 2021 09:25:11 +0100 Subject: [PATCH 1/8] [refactor/bench] make mempool_stress bench reusable and parameterizable --- src/bench/mempool_stress.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/bench/mempool_stress.cpp b/src/bench/mempool_stress.cpp index f28768efc85..21d7407fcaf 100644 --- a/src/bench/mempool_stress.cpp +++ b/src/bench/mempool_stress.cpp @@ -26,14 +26,8 @@ struct Available { Available(CTransactionRef& ref, size_t tx_count) : ref(ref), tx_count(tx_count){} }; -static void ComplexMemPool(benchmark::Bench& bench) +static std::vector CreateOrderedCoins(FastRandomContext& det_rand, int childTxs, int min_ancestors) { - int childTxs = 800; - if (bench.complexityN() > 1) { - childTxs = static_cast(bench.complexityN()); - } - - FastRandomContext det_rand{true}; std::vector available_coins; std::vector ordered_coins; // Create some base transactions @@ -58,8 +52,10 @@ static void ComplexMemPool(benchmark::Bench& bench) size_t idx = det_rand.randrange(available_coins.size()); Available coin = available_coins[idx]; uint256 hash = coin.ref->GetHash(); - // biased towards taking just one ancestor, but maybe more - size_t n_to_take = det_rand.randrange(2) == 0 ? 1 : 1+det_rand.randrange(coin.ref->vout.size() - coin.vin_left); + // biased towards taking min_ancestors parents, but maybe more + size_t n_to_take = det_rand.randrange(2) == 0 ? + min_ancestors : + min_ancestors + det_rand.randrange(coin.ref->vout.size() - coin.vin_left); for (size_t i = 0; i < n_to_take; ++i) { tx.vin.emplace_back(); tx.vin.back().prevout = COutPoint(hash, coin.vin_left++); @@ -79,6 +75,17 @@ static void ComplexMemPool(benchmark::Bench& bench) ordered_coins.emplace_back(MakeTransactionRef(tx)); available_coins.emplace_back(ordered_coins.back(), tx_counter++); } + return ordered_coins; +} + +static void ComplexMemPool(benchmark::Bench& bench) +{ + FastRandomContext det_rand{true}; + int childTxs = 800; + if (bench.complexityN() > 1) { + childTxs = static_cast(bench.complexityN()); + } + std::vector ordered_coins = CreateOrderedCoins(det_rand, childTxs, /* min_ancestors */ 1); const auto testing_setup = MakeNoLogFileContext(CBaseChainParams::MAIN); CTxMemPool pool; LOCK2(cs_main, pool.cs); From 30e240f65e69c6dffcd033afc63895345bd51f53 Mon Sep 17 00:00:00 2001 From: glozow Date: Thu, 30 Sep 2021 09:31:18 +0100 Subject: [PATCH 2/8] [bench] Benchmark CTxMemPool::check() --- src/bench/mempool_stress.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/bench/mempool_stress.cpp b/src/bench/mempool_stress.cpp index 21d7407fcaf..16c57881d41 100644 --- a/src/bench/mempool_stress.cpp +++ b/src/bench/mempool_stress.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -98,4 +99,20 @@ static void ComplexMemPool(benchmark::Bench& bench) }); } +static void MempoolCheck(benchmark::Bench& bench) +{ + FastRandomContext det_rand{true}; + const int childTxs = bench.complexityN() > 1 ? static_cast(bench.complexityN()) : 2000; + const std::vector ordered_coins = CreateOrderedCoins(det_rand, childTxs, /* min_ancestors */ 5); + const auto testing_setup = MakeNoLogFileContext(CBaseChainParams::MAIN, {"-checkmempool=1"}); + CTxMemPool pool; + LOCK2(cs_main, pool.cs); + for (auto& tx : ordered_coins) AddTx(tx, pool); + + bench.run([&]() NO_THREAD_SAFETY_ANALYSIS { + pool.check(testing_setup.get()->m_node.chainman->ActiveChainstate()); + }); +} + BENCHMARK(ComplexMemPool); +BENCHMARK(MempoolCheck); From 54c6f3c1da01090aee9691a2c2bee0984a054ce8 Mon Sep 17 00:00:00 2001 From: glozow Date: Thu, 30 Sep 2021 09:08:40 +0100 Subject: [PATCH 3/8] [mempool] speed up check() by using coins cache and iterating in topo order No behavior changes. Before, we're always adding transactions to the "check later" queue if they have any parents in the mempool. But there's no reason to do this if all of its inputs are already available from mempoolDuplicate. Instead, check for inputs, and only mark fDependsWait=true if the parents haven't been processed yet. Reduce the amount of "check later" transactions by looking at ancestors before descendants. Do this by iterating through them in ascending order by ancestor count. This works because a child will always have more in-mempool ancestors than its parent. We should never have any entries in the "check later" queue after this commit. --- src/txmempool.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 5a93f30c8a3..10f7f66263f 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -693,13 +693,14 @@ void CTxMemPool::check(CChainState& active_chainstate) const uint64_t checkTotal = 0; CAmount check_total_fee{0}; uint64_t innerUsage = 0; + uint64_t prev_ancestor_count{0}; CCoinsViewCache& active_coins_tip = active_chainstate.CoinsTip(); CCoinsViewCache mempoolDuplicate(const_cast(&active_coins_tip)); const int64_t spendheight = active_chainstate.m_chain.Height() + 1; std::list waitingOnDependants; - for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { + for (const auto& it : GetSortedDepthAndScore()) { unsigned int i = 0; checkTotal += it->GetTxSize(); check_total_fee += it->GetFee(); @@ -714,7 +715,7 @@ void CTxMemPool::check(CChainState& active_chainstate) const if (it2 != mapTx.end()) { const CTransaction& tx2 = it2->GetTx(); assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull()); - fDependsWait = true; + if (!mempoolDuplicate.HaveCoin(txin.prevout)) fDependsWait = true; setParentCheck.insert(*it2); } else { assert(active_coins_tip.HaveCoin(txin.prevout)); @@ -751,6 +752,9 @@ void CTxMemPool::check(CChainState& active_chainstate) const assert(it->GetSizeWithAncestors() == nSizeCheck); assert(it->GetSigOpCostWithAncestors() == nSigOpCheck); assert(it->GetModFeesWithAncestors() == nFeesCheck); + // Sanity check: we are walking in ascending ancestor count order. + assert(prev_ancestor_count <= it->GetCountWithAncestors()); + prev_ancestor_count = it->GetCountWithAncestors(); // Check children against mapNextTx CTxMemPoolEntry::Children setChildrenCheck; From e8639ec26aaf4de3fae280963434bf1cf2017b6f Mon Sep 17 00:00:00 2001 From: glozow Date: Wed, 29 Sep 2021 18:38:24 +0100 Subject: [PATCH 4/8] [mempool] remove now-unnecessary code Remove variables used for keeping track of mempool transactions for which we haven't processed the parents yet. Since we're iterating in topological order now, they're always unused. --- src/txmempool.cpp | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 10f7f66263f..2decc153f28 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -699,15 +699,12 @@ void CTxMemPool::check(CChainState& active_chainstate) const CCoinsViewCache mempoolDuplicate(const_cast(&active_coins_tip)); const int64_t spendheight = active_chainstate.m_chain.Height() + 1; - std::list waitingOnDependants; for (const auto& it : GetSortedDepthAndScore()) { - 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()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst()); - bool fDependsWait = false; CTxMemPoolEntry::Parents setParentCheck; for (const CTxIn &txin : tx.vin) { // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's. @@ -715,7 +712,10 @@ void CTxMemPool::check(CChainState& active_chainstate) const if (it2 != mapTx.end()) { const CTransaction& tx2 = it2->GetTx(); assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull()); - if (!mempoolDuplicate.HaveCoin(txin.prevout)) fDependsWait = true; + // We are iterating through the mempool entries sorted in order by ancestor count. + // All parents must have been checked before their children and their coins added to + // the mempoolDuplicate coins cache. + assert(mempoolDuplicate.HaveCoin(txin.prevout)); setParentCheck.insert(*it2); } else { assert(active_coins_tip.HaveCoin(txin.prevout)); @@ -725,7 +725,6 @@ void CTxMemPool::check(CChainState& active_chainstate) const assert(it3 != mapNextTx.end()); assert(it3->first == &txin.prevout); assert(it3->second == &tx); - i++; } auto comp = [](const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) -> bool { return a.GetTx().GetHash() == b.GetTx().GetHash(); @@ -773,24 +772,7 @@ void CTxMemPool::check(CChainState& active_chainstate) const // just a sanity check, not definitive that this calc is correct... assert(it->GetSizeWithDescendants() >= child_sizes + it->GetTxSize()); - if (fDependsWait) - waitingOnDependants.push_back(&(*it)); - else { - CheckInputsAndUpdateCoins(tx, mempoolDuplicate, spendheight); - } - } - unsigned int stepsSinceLastRemove = 0; - while (!waitingOnDependants.empty()) { - const CTxMemPoolEntry* entry = waitingOnDependants.front(); - waitingOnDependants.pop_front(); - if (!mempoolDuplicate.HaveInputs(entry->GetTx())) { - waitingOnDependants.push_back(entry); - stepsSinceLastRemove++; - assert(stepsSinceLastRemove < waitingOnDependants.size()); - } else { - CheckInputsAndUpdateCoins(entry->GetTx(), mempoolDuplicate, spendheight); - stepsSinceLastRemove = 0; - } + CheckInputsAndUpdateCoins(tx, mempoolDuplicate, spendheight); } for (auto it = mapNextTx.cbegin(); it != mapNextTx.cend(); it++) { uint256 hash = it->second->GetHash(); From 09d18916afb0ecae90700d4befd9d5dc52767970 Mon Sep 17 00:00:00 2001 From: glozow Date: Wed, 29 Sep 2021 19:10:44 +0100 Subject: [PATCH 5/8] MOVEONLY: remove single-use helper func CheckInputsAndUpdateCoins --- src/txmempool.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 2decc153f28..40e142dc477 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -671,15 +671,6 @@ void CTxMemPool::clear() _clear(); } -static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& mempoolDuplicate, const int64_t spendheight) -{ - TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass - CAmount txfee = 0; - bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee); - assert(fCheckResult); - UpdateCoins(tx, mempoolDuplicate, std::numeric_limits::max()); -} - void CTxMemPool::check(CChainState& active_chainstate) const { if (m_check_ratio == 0) return; @@ -772,7 +763,11 @@ void CTxMemPool::check(CChainState& active_chainstate) const // just a sanity check, not definitive that this calc is correct... assert(it->GetSizeWithDescendants() >= child_sizes + it->GetTxSize()); - CheckInputsAndUpdateCoins(tx, mempoolDuplicate, spendheight); + TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass + CAmount txfee = 0; + bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee); + assert(fCheckResult); + UpdateCoins(tx, mempoolDuplicate, std::numeric_limits::max()); } for (auto it = mapNextTx.cbegin(); it != mapNextTx.cend(); it++) { uint256 hash = it->second->GetHash(); From 9e8d7ad5d9cc4b013826daead9cee09aad539401 Mon Sep 17 00:00:00 2001 From: glozow Date: Mon, 4 Oct 2021 13:01:38 +0100 Subject: [PATCH 6/8] [validation/mempool] use Spend/AddCoin instead of UpdateCoins UpdateCoins is an unnecessary dependency on validation. All we need to do is add and remove coins to check inputs. We don't need the extra logic for checking coinbases and handling TxUndos. Also remove the wrapper function in validation.h which constructs a throwaway TxUndo object before calling UpdateCoins because it is now unused. --- src/txmempool.cpp | 4 +++- src/validation.cpp | 6 ------ src/validation.h | 3 --- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 40e142dc477..c1abe24af7b 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -5,6 +5,7 @@ #include +#include #include #include #include @@ -767,7 +768,8 @@ void CTxMemPool::check(CChainState& active_chainstate) const CAmount txfee = 0; bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee); assert(fCheckResult); - UpdateCoins(tx, mempoolDuplicate, std::numeric_limits::max()); + for (const auto& input: tx.vin) mempoolDuplicate.SpendCoin(input.prevout); + AddCoins(mempoolDuplicate, tx, std::numeric_limits::max()); } for (auto it = mapNextTx.cbegin(); it != mapNextTx.cend(); it++) { uint256 hash = it->second->GetHash(); diff --git a/src/validation.cpp b/src/validation.cpp index 4504d2ca0a2..863502e0d76 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1240,12 +1240,6 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund AddCoins(inputs, tx, nHeight); } -void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight) -{ - CTxUndo txundo; - UpdateCoins(tx, inputs, txundo, nHeight); -} - bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness; diff --git a/src/validation.h b/src/validation.h index b2282828ced..caa0832dd3a 100644 --- a/src/validation.h +++ b/src/validation.h @@ -229,9 +229,6 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx const Package& txns, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main); -/** Apply the effects of this transaction on the UTXO set represented by view */ -void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight); - /** Transaction validation functions */ /** From ed6115f1eae0eb4669601106a9aaff078a2f3a74 Mon Sep 17 00:00:00 2001 From: glozow Date: Mon, 4 Oct 2021 12:58:50 +0100 Subject: [PATCH 7/8] [mempool] simplify some check() logic No transaction in the mempool should ever be a coinbase. Since mempoolDuplicate's backend is the chainstate coins view, it should always contain the coins available. --- src/txmempool.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index c1abe24af7b..f65fb24f190 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -704,14 +704,12 @@ void CTxMemPool::check(CChainState& active_chainstate) const if (it2 != mapTx.end()) { const CTransaction& tx2 = it2->GetTx(); assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull()); - // We are iterating through the mempool entries sorted in order by ancestor count. - // All parents must have been checked before their children and their coins added to - // the mempoolDuplicate coins cache. - assert(mempoolDuplicate.HaveCoin(txin.prevout)); setParentCheck.insert(*it2); - } else { - assert(active_coins_tip.HaveCoin(txin.prevout)); } + // We are iterating through the mempool entries sorted in order by ancestor count. + // All parents must have been checked before their children and their coins added to + // the mempoolDuplicate coins cache. + assert(mempoolDuplicate.HaveCoin(txin.prevout)); // Check whether its inputs are marked in mapNextTx. auto it3 = mapNextTx.find(txin.prevout); assert(it3 != mapNextTx.end()); @@ -766,8 +764,8 @@ void CTxMemPool::check(CChainState& active_chainstate) const TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass CAmount txfee = 0; - bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee); - assert(fCheckResult); + assert(!tx.IsCoinBase()); + assert(Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee)); for (const auto& input: tx.vin) mempoolDuplicate.SpendCoin(input.prevout); AddCoins(mempoolDuplicate, tx, std::numeric_limits::max()); } From 082c5bf099c64e3d27abe9b68a71ce500b693e7e Mon Sep 17 00:00:00 2001 From: glozow Date: Wed, 29 Sep 2021 19:36:01 +0100 Subject: [PATCH 8/8] [refactor] pass coinsview and height to check() Removes check's dependency on validation.h --- src/bench/mempool_stress.cpp | 3 ++- src/net_processing.cpp | 6 ++++-- src/test/fuzz/tx_pool.cpp | 4 ++-- src/txmempool.cpp | 4 +--- src/txmempool.h | 2 +- src/validation.cpp | 2 +- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/bench/mempool_stress.cpp b/src/bench/mempool_stress.cpp index 16c57881d41..a0a82ea359c 100644 --- a/src/bench/mempool_stress.cpp +++ b/src/bench/mempool_stress.cpp @@ -107,10 +107,11 @@ static void MempoolCheck(benchmark::Bench& bench) const auto testing_setup = MakeNoLogFileContext(CBaseChainParams::MAIN, {"-checkmempool=1"}); CTxMemPool pool; LOCK2(cs_main, pool.cs); + const CCoinsViewCache& coins_tip = testing_setup.get()->m_node.chainman->ActiveChainstate().CoinsTip(); for (auto& tx : ordered_coins) AddTx(tx, pool); bench.run([&]() NO_THREAD_SAFETY_ANALYSIS { - pool.check(testing_setup.get()->m_node.chainman->ActiveChainstate()); + pool.check(coins_tip, /* spendheight */ 2); }); } diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 008b4d679c6..12c4eece56c 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2298,7 +2298,8 @@ void PeerManagerImpl::ProcessOrphanTx(std::set& orphan_work_set) break; } } - m_mempool.check(m_chainman.ActiveChainstate()); + CChainState& active_chainstate = m_chainman.ActiveChainstate(); + m_mempool.check(active_chainstate.CoinsTip(), active_chainstate.m_chain.Height() + 1); } bool PeerManagerImpl::PrepareBlockFilterRequest(CNode& peer, @@ -3260,7 +3261,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, const TxValidationState& state = result.m_state; if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) { - m_mempool.check(m_chainman.ActiveChainstate()); + CChainState& active_chainstate = m_chainman.ActiveChainstate(); + m_mempool.check(active_chainstate.CoinsTip(), active_chainstate.m_chain.Height() + 1); // As this version of the transaction was acceptable, we can forget about any // requests for it. m_txrequest.ForgetTxHash(tx.GetHash()); diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp index 6201cc813c7..17b5ef88b92 100644 --- a/src/test/fuzz/tx_pool.cpp +++ b/src/test/fuzz/tx_pool.cpp @@ -81,7 +81,7 @@ void SetMempoolConstraints(ArgsManager& args, FuzzedDataProvider& fuzzed_data_pr void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, CChainState& chainstate) { - WITH_LOCK(::cs_main, tx_pool.check(chainstate)); + WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1)); { BlockAssembler::Options options; options.nBlockMaxWeight = fuzzed_data_provider.ConsumeIntegralInRange(0U, MAX_BLOCK_WEIGHT); @@ -97,7 +97,7 @@ void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, CCh std::vector all_txids; tx_pool.queryHashes(all_txids); assert(all_txids.size() < info_all.size()); - WITH_LOCK(::cs_main, tx_pool.check(chainstate)); + WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1)); } SyncWithValidationInterfaceQueue(); } diff --git a/src/txmempool.cpp b/src/txmempool.cpp index f65fb24f190..a0d9e2a6bf1 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -672,7 +672,7 @@ void CTxMemPool::clear() _clear(); } -void CTxMemPool::check(CChainState& active_chainstate) const +void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendheight) const { if (m_check_ratio == 0) return; @@ -687,9 +687,7 @@ void CTxMemPool::check(CChainState& active_chainstate) const uint64_t innerUsage = 0; uint64_t prev_ancestor_count{0}; - CCoinsViewCache& active_coins_tip = active_chainstate.CoinsTip(); CCoinsViewCache mempoolDuplicate(const_cast(&active_coins_tip)); - const int64_t spendheight = active_chainstate.m_chain.Height() + 1; for (const auto& it : GetSortedDepthAndScore()) { checkTotal += it->GetTxSize(); diff --git a/src/txmempool.h b/src/txmempool.h index 27ee0628a7d..a3a11eb72b6 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -622,7 +622,7 @@ public: * all inputs are in the mapNextTx array). If sanity-checking is turned off, * check does nothing. */ - void check(CChainState& active_chainstate) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + void check(const CCoinsViewCache& active_coins_tip, int64_t spendheight) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main); // addUnchecked must updated state for all ancestors of a given transaction, // to track size/count of descendant transactions. First version of diff --git a/src/validation.cpp b/src/validation.cpp index 863502e0d76..8f0ddd90640 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2486,7 +2486,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, CBlockIndex // any disconnected transactions back to the mempool. MaybeUpdateMempoolForReorg(disconnectpool, true); } - if (m_mempool) m_mempool->check(*this); + if (m_mempool) m_mempool->check(this->CoinsTip(), this->m_chain.Height() + 1); CheckForkWarningConditions();