Skip to content

Commit fabeb1f

Browse files
author
MarcoFalke
committed
validation: Add missing mempool locks
1 parent fa0c9db commit fabeb1f

File tree

5 files changed

+26
-34
lines changed

5 files changed

+26
-34
lines changed

src/rpc/mining.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
469469
nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
470470
}
471471

472-
// Release the wallet and main lock while waiting
472+
// Release lock while waiting
473473
LEAVE_CRITICAL_SECTION(cs_main);
474474
{
475475
checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1);

src/txmempool.cpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendan
104104
// for each such descendant, also update the ancestor state to include the parent.
105105
void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashesToUpdate)
106106
{
107-
LOCK(cs);
107+
AssertLockHeld(cs);
108108
// For each entry in vHashesToUpdate, store the set of in-mempool, but not
109109
// in-vHashesToUpdate transactions, so that we don't have to recalculate
110110
// descendants when we come across a previously seen entry.
@@ -457,8 +457,7 @@ void CTxMemPool::CalculateDescendants(txiter entryit, setEntries& setDescendants
457457
void CTxMemPool::removeRecursive(const CTransaction &origTx, MemPoolRemovalReason reason)
458458
{
459459
// Remove transaction from memory pool
460-
{
461-
LOCK(cs);
460+
AssertLockHeld(cs);
462461
setEntries txToRemove;
463462
txiter origit = mapTx.find(origTx.GetHash());
464463
if (origit != mapTx.end()) {
@@ -483,13 +482,12 @@ void CTxMemPool::removeRecursive(const CTransaction &origTx, MemPoolRemovalReaso
483482
}
484483

485484
RemoveStaged(setAllRemoves, false, reason);
486-
}
487485
}
488486

489487
void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags)
490488
{
491489
// Remove transactions spending a coinbase which are now immature and no-longer-final transactions
492-
LOCK(cs);
490+
AssertLockHeld(cs);
493491
setEntries txToRemove;
494492
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
495493
const CTransaction& tx = it->GetTx();
@@ -545,7 +543,7 @@ void CTxMemPool::removeConflicts(const CTransaction &tx)
545543
*/
546544
void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight)
547545
{
548-
LOCK(cs);
546+
AssertLockHeld(cs);
549547
std::vector<const CTxMemPoolEntry*> entries;
550548
for (const auto& tx : vtx)
551549
{
@@ -920,7 +918,7 @@ void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants, MemPool
920918
}
921919

922920
int CTxMemPool::Expire(int64_t time) {
923-
LOCK(cs);
921+
AssertLockHeld(cs);
924922
indexed_transaction_set::index<entry_time>::type::iterator it = mapTx.get<entry_time>().begin();
925923
setEntries toremove;
926924
while (it != mapTx.get<entry_time>().end() && it->GetTime() < time) {
@@ -1013,7 +1011,7 @@ void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) {
10131011
}
10141012

10151013
void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpendsRemaining) {
1016-
LOCK(cs);
1014+
AssertLockHeld(cs);
10171015

10181016
unsigned nTxnRemoved = 0;
10191017
CFeeRate maxFeeRateRemoved(0);

src/txmempool.h

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -514,21 +514,12 @@ class CTxMemPool
514514
* `mempool.cs` whenever adding transactions to the mempool and whenever
515515
* changing the chain tip. It's necessary to keep both mutexes locked until
516516
* the mempool is consistent with the new chain tip and fully populated.
517-
*
518-
* @par Consistency bug
519-
*
520-
* The second guarantee above is not currently enforced, but
521-
* https://github.com/bitcoin/bitcoin/pull/14193 will fix it. No known code
522-
* in bitcoin currently depends on second guarantee, but it is important to
523-
* fix for third party code that needs be able to frequently poll the
524-
* mempool without locking `cs_main` and without encountering missing
525-
* transactions during reorgs.
526517
*/
527518
mutable RecursiveMutex cs;
528519
indexed_transaction_set mapTx GUARDED_BY(cs);
529520

530521
using txiter = indexed_transaction_set::nth_index<0>::type::const_iterator;
531-
std::vector<std::pair<uint256, txiter> > vTxHashes; //!< All tx witness hashes/entries in mapTx, in random order
522+
std::vector<std::pair<uint256, txiter>> vTxHashes GUARDED_BY(cs); //!< All tx witness hashes/entries in mapTx, in random order
532523

533524
struct CompareIteratorByHash {
534525
bool operator()(const txiter &a, const txiter &b) const {
@@ -583,10 +574,10 @@ class CTxMemPool
583574
void addUnchecked(const CTxMemPoolEntry& entry, bool validFeeEstimate = true) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
584575
void addUnchecked(const CTxMemPoolEntry& entry, setEntries& setAncestors, bool validFeeEstimate = true) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
585576

586-
void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN);
587-
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
588-
void removeConflicts(const CTransaction &tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
589-
void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight);
577+
void removeRecursive(const CTransaction& tx, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN) EXCLUSIVE_LOCKS_REQUIRED(cs);
578+
void removeForReorg(const CCoinsViewCache* pcoins, unsigned int nMemPoolHeight, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
579+
void removeConflicts(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
580+
void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight) EXCLUSIVE_LOCKS_REQUIRED(cs);
590581

591582
void clear();
592583
void _clear() EXCLUSIVE_LOCKS_REQUIRED(cs); //lock free
@@ -599,7 +590,7 @@ class CTxMemPool
599590
* Check that none of this transactions inputs are in the mempool, and thus
600591
* the tx is not dependent on other mempool transactions to be included in a block.
601592
*/
602-
bool HasNoInputsOf(const CTransaction& tx) const;
593+
bool HasNoInputsOf(const CTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs);
603594

604595
/** Affect CreateNewBlock prioritisation of transactions */
605596
void PrioritiseTransaction(const uint256& hash, const CAmount& nFeeDelta);
@@ -633,7 +624,7 @@ class CTxMemPool
633624
* for). Note: vHashesToUpdate should be the set of transactions from the
634625
* disconnected block that have been accepted back into the mempool.
635626
*/
636-
void UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
627+
void UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
637628

638629
/** Try to calculate all in-mempool ancestors of entry.
639630
* (these are all calculated including the tx itself)
@@ -664,10 +655,10 @@ class CTxMemPool
664655
* pvNoSpendsRemaining, if set, will be populated with the list of outpoints
665656
* which are not in mempool which no longer have any spends in this mempool.
666657
*/
667-
void TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpendsRemaining=nullptr);
658+
void TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpendsRemaining = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
668659

669660
/** Expire all transaction (and their dependencies) in the mempool older than time. Return the number of removed transactions. */
670-
int Expire(int64_t time);
661+
int Expire(int64_t time) EXCLUSIVE_LOCKS_REQUIRED(cs);
671662

672663
/**
673664
* Calculate the ancestor and descendant count for the given transaction.

src/validation.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,8 @@ bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flag
305305
// Returns the script flags which should be checked for a given block
306306
static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& chainparams);
307307

308-
static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) {
308+
static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
309+
{
309310
int expired = pool.Expire(GetTime() - age);
310311
if (expired != 0) {
311312
LogPrint(BCLog::MEMPOOL, "Expired %i transactions from the memory pool\n", expired);
@@ -342,7 +343,7 @@ static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
342343
* and instead just erase from the mempool as needed.
343344
*/
344345

345-
static void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
346+
static void UpdateMempoolForReorg(DisconnectedBlockTransactions& disconnectpool, bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs)
346347
{
347348
AssertLockHeld(cs_main);
348349
std::vector<uint256> vHashUpdate;
@@ -2549,7 +2550,7 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
25492550
LimitValidationInterfaceQueue();
25502551

25512552
{
2552-
LOCK(cs_main);
2553+
LOCK2(cs_main, ::mempool.cs); // Lock transaction pool for at least as long as it takes for connectTrace to be consumed
25532554
CBlockIndex* starting_tip = m_chain.Tip();
25542555
bool blocks_connected = false;
25552556
do {
@@ -2669,6 +2670,7 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
26692670
LimitValidationInterfaceQueue();
26702671

26712672
LOCK(cs_main);
2673+
LOCK(::mempool.cs); // Lock for as long as disconnectpool is in scope to make sure UpdateMempoolForReorg is called after DisconnectTip without unlocking in between
26722674
if (!m_chain.Contains(pindex)) break;
26732675
pindex_was_in_chain = true;
26742676
CBlockIndex *invalid_walk_tip = m_chain.Tip();
@@ -4102,7 +4104,7 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
41024104
// Loop until the tip is below nHeight, or we reach a pruned block.
41034105
while (!ShutdownRequested()) {
41044106
{
4105-
LOCK(cs_main);
4107+
LOCK2(cs_main, ::mempool.cs);
41064108
// Make sure nothing changed from under us (this won't happen because RewindBlockIndex runs before importing/network are active)
41074109
assert(tip == m_chain.Tip());
41084110
if (tip == nullptr || tip->nHeight < nHeight) break;

src/validation.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <protocol.h> // For CMessageHeader::MessageStartChars
1919
#include <script/script_error.h>
2020
#include <sync.h>
21+
#include <txmempool.h> // For CTxMemPool::cs
2122
#include <versionbits.h>
2223

2324
#include <algorithm>
@@ -556,7 +557,7 @@ class CChainState {
556557
CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
557558

558559
// Block disconnection on our pcoinsTip:
559-
bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
560+
bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
560561

561562
// Manual block validity manipulation:
562563
bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main);
@@ -575,8 +576,8 @@ class CChainState {
575576
bool IsInitialBlockDownload() const;
576577

577578
private:
578-
bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
579-
bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
579+
bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
580+
bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs);
580581

581582
CBlockIndex* AddToBlockIndex(const CBlockHeader& block) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
582583
/** Create a new block index entry for a given block hash */

0 commit comments

Comments
 (0)