Merge #6319: backport: bitcoin#19668, #21598, #19979, #28774 - lock annotations

1db9e6ac76 Merge #19979: Replace LockAssertion with AssertLockHeld, remove LockAssertion (MarcoFalke)
d0a4198166 Merge #21598: refactor: Remove negative lock annotations from globals (MarcoFalke)
55114a682e Merge #19668: Do not hide compile-time thread safety warnings (MarcoFalke)
898282d620 Merge bitcoin/bitcoin#28774: wallet: avoid returning a reference to vMasterKey after releasing the mutex that guards it (Ava Chow)
6d452845dc fix: add missing lock annotation for ContextualCheckBlock and related TODO (Konstantin Akimov)
846ebab6e0 fix: fixes for orphange's locks and cs_main (Konstantin Akimov)
002580c42d fix: add ignoring safety-annotation for llmq/signing_shares due to template lambdas (Konstantin Akimov)
502d6ae8ef fix: add multiple missing annotation about locks for dash specific code (Konstantin Akimov)
a219a8be15 partial Merge #29040: refactor: Remove pre-C++20 code, fs::path cleanup (Konstantin Akimov)
042e8a4101 fix: drop unneded lock assert in net.cpp for m_nodes_mutex (Konstantin Akimov)

Pull request description:

  ## What was done?
  These 4 backports improve noticeable implementation of thread-safety analysis by moving many possible warnings to compilation level.
  There's a lot of fixes for annotations after that!

  ## How Has This Been Tested?
  Build with clang:
  ```
  CC=clang CXX=clang++ ./configure --prefix=$(pwd)/depends/x86_64-pc-linux-gnu --enable-suppress-external-warnings   --enable-debug  --enable-stacktraces --enable-werror --enable-crash-hooks --enable-maintainer-mode
  ```

  ## Breaking Changes
  n/a

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [x] I have made corresponding changes to the documentation
  - [x] I have assigned this pull request to a milestone

ACKs for top commit:
  UdjinM6:
    utACK 1db9e6ac76
  PastaPastaPasta:
    utACK 1db9e6ac76

Tree-SHA512: 42a73e423f311689babc366b58a92f024556cf63af93357cfa0715c471efc1491d02ed9e7ba19b23efcdf423ab5bd051a24da2ad64c32e4b6db132a05f6669c1
This commit is contained in:
pasta 2024-10-21 08:53:46 -05:00
commit 7701b8dcea
No known key found for this signature in database
GPG Key ID: E2F3D7916E722D38
29 changed files with 232 additions and 129 deletions

View File

@ -899,6 +899,53 @@ the upper cycle, etc.
Threads and synchronization
----------------------------
- Prefer `Mutex` type to `RecursiveMutex` one
- Consistently use [Clang Thread Safety Analysis](https://clang.llvm.org/docs/ThreadSafetyAnalysis.html) annotations to
get compile-time warnings about potential race conditions in code. Combine annotations in function declarations with
run-time asserts in function definitions:
```C++
// txmempool.h
class CTxMemPool
{
public:
...
mutable RecursiveMutex cs;
...
void UpdateTransactionsFromBlock(...) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, cs);
...
}
// txmempool.cpp
void CTxMemPool::UpdateTransactionsFromBlock(...)
{
AssertLockHeld(::cs_main);
AssertLockHeld(cs);
...
}
```
```C++
// validation.h
class ChainstateManager
{
public:
...
bool ProcessNewBlock(...) LOCKS_EXCLUDED(::cs_main);
...
}
// validation.cpp
bool ChainstateManager::ProcessNewBlock(...)
{
AssertLockNotHeld(::cs_main);
...
LOCK(::cs_main);
...
}
```
- Build and run tests with `-DDEBUG_LOCKORDER` to verify that no potential
deadlocks are introduced.

View File

@ -1448,11 +1448,16 @@ bool CCoinJoinClientSession::MakeCollateralAmounts()
});
// First try to use only non-denominated funds
if (std::any_of(vecTally.begin(), vecTally.end(), [&](const auto& item) { return MakeCollateralAmounts(item, false); })) {
if (ranges::any_of(vecTally, [&](const auto& item) EXCLUSIVE_LOCKS_REQUIRED(m_wallet.cs_wallet) {
return MakeCollateralAmounts(item, false);
})) {
return true;
}
// There should be at least some denominated funds we should be able to break in pieces to continue mixing
if (std::any_of(vecTally.begin(), vecTally.end(), [&](const auto& item) { return MakeCollateralAmounts(item, true); })) {
if (ranges::any_of(vecTally, [&](const auto& item) EXCLUSIVE_LOCKS_REQUIRED(m_wallet.cs_wallet) {
return MakeCollateralAmounts(item, true);
})) {
return true;
}

View File

@ -161,13 +161,16 @@ private:
/// Create denominations
bool CreateDenominated(CAmount nBalanceToDenominate);
bool CreateDenominated(CAmount nBalanceToDenominate, const CompactTallyItem& tallyItem, bool fCreateMixingCollaterals);
bool CreateDenominated(CAmount nBalanceToDenominate, const CompactTallyItem& tallyItem, bool fCreateMixingCollaterals)
EXCLUSIVE_LOCKS_REQUIRED(m_wallet.cs_wallet);
/// Split up large inputs or make fee sized inputs
bool MakeCollateralAmounts();
bool MakeCollateralAmounts(const CompactTallyItem& tallyItem, bool fTryDenominated);
bool MakeCollateralAmounts(const CompactTallyItem& tallyItem, bool fTryDenominated)
EXCLUSIVE_LOCKS_REQUIRED(m_wallet.cs_wallet);
bool CreateCollateralTransaction(CMutableTransaction& txCollateral, std::string& strReason);
bool CreateCollateralTransaction(CMutableTransaction& txCollateral, std::string& strReason)
EXCLUSIVE_LOCKS_REQUIRED(m_wallet.cs_wallet);
bool JoinExistingQueue(CAmount nBalanceNeedsAnonymized, CConnman& connman);
bool StartNewQueue(CAmount nBalanceNeedsAnonymized, CConnman& connman);
@ -175,7 +178,9 @@ private:
/// step 0: select denominated inputs and txouts
bool SelectDenominate(std::string& strErrorRet, std::vector<CTxDSIn>& vecTxDSInRet);
/// step 1: prepare denominated inputs and outputs
bool PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, const std::vector<CTxDSIn>& vecTxDSIn, std::vector<std::pair<CTxDSIn, CTxOut> >& vecPSInOutPairsRet, bool fDryRun = false);
bool PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, const std::vector<CTxDSIn>& vecTxDSIn,
std::vector<std::pair<CTxDSIn, CTxOut>>& vecPSInOutPairsRet, bool fDryRun = false)
EXCLUSIVE_LOCKS_REQUIRED(m_wallet.cs_wallet);
/// step 2: send denominated inputs and outputs prepared in step 1
bool SendDenominate(const std::vector<std::pair<CTxDSIn, CTxOut> >& vecPSInOutPairsIn, CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(!cs_coinjoin);

View File

@ -398,8 +398,8 @@ public:
private:
void CheckDSTXes(const CBlockIndex* pindex, const llmq::CChainLocksHandler& clhandler)
EXCLUSIVE_LOCKS_REQUIRED(!cs_mapdstx);
void UpdateDSTXConfirmedHeight(const CTransactionRef& tx, std::optional<int> nHeight);
void UpdateDSTXConfirmedHeight(const CTransactionRef& tx, std::optional<int> nHeight)
EXCLUSIVE_LOCKS_REQUIRED(cs_mapdstx);
};
bool ATMPIfSaneFee(ChainstateManager& chainman, const CTransactionRef& tx, bool test_accept = false)

View File

@ -1067,11 +1067,13 @@ CDeterministicMNList CDeterministicMNManager::GetListForBlockInternal(gsl::not_n
mnListsCache.emplace(snapshot.GetBlockHash(), snapshot);
} else {
// keep snapshots for yet alive quorums
if (ranges::any_of(Params().GetConsensus().llmqs, [&snapshot, this](const auto& params){
AssertLockHeld(cs);
return (snapshot.GetHeight() % params.dkgInterval == 0) &&
(snapshot.GetHeight() + params.dkgInterval * (params.keepOldConnections + 1) >= tipIndex->nHeight);
})) {
if (ranges::any_of(Params().GetConsensus().llmqs,
[&snapshot, this](const auto& params) EXCLUSIVE_LOCKS_REQUIRED(cs) {
AssertLockHeld(cs);
return (snapshot.GetHeight() % params.dkgInterval == 0) &&
(snapshot.GetHeight() + params.dkgInterval * (params.keepOldConnections + 1) >=
tipIndex->nHeight);
})) {
mnListsCache.emplace(snapshot.GetBlockHash(), snapshot);
}
}

View File

@ -171,8 +171,9 @@ public:
[[nodiscard]] UniValue ToJson(bool extended = false) const;
};
bool BuildSimplifiedMNListDiff(CDeterministicMNManager& dmnman, const ChainstateManager& chainman, const llmq::CQuorumBlockProcessor& qblockman,
const llmq::CQuorumManager& qman, const uint256& baseBlockHash, const uint256& blockHash,
CSimplifiedMNListDiff& mnListDiffRet, std::string& errorRet, bool extended = false);
bool BuildSimplifiedMNListDiff(CDeterministicMNManager& dmnman, const ChainstateManager& chainman,
const llmq::CQuorumBlockProcessor& qblockman, const llmq::CQuorumManager& qman,
const uint256& baseBlockHash, const uint256& blockHash, CSimplifiedMNListDiff& mnListDiffRet,
std::string& errorRet, bool extended = false) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
#endif // BITCOIN_EVO_SIMPLIFIEDMNS_H

View File

@ -19,9 +19,10 @@
#include <primitives/block.h>
#include <validation.h>
static bool CheckSpecialTxInner(CDeterministicMNManager& dmnman, const ChainstateManager& chainman, const llmq::CQuorumManager& qman, const CTransaction& tx,
const CBlockIndex* pindexPrev, const CCoinsViewCache& view, const std::optional<CRangesSet>& indexes, bool check_sigs,
TxValidationState& state)
static bool CheckSpecialTxInner(CDeterministicMNManager& dmnman, const ChainstateManager& chainman,
const llmq::CQuorumManager& qman, const CTransaction& tx, const CBlockIndex* pindexPrev,
const CCoinsViewCache& view, const std::optional<CRangesSet>& indexes, bool check_sigs,
TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
AssertLockHeld(cs_main);

View File

@ -347,9 +347,9 @@ public:
* - Track governance objects which are triggers
* - After triggers are activated and executed, they can be removed
*/
std::vector<std::shared_ptr<CSuperblock>> GetActiveTriggers() const;
bool AddNewTrigger(uint256 nHash);
void CleanAndRemoveTriggers();
std::vector<std::shared_ptr<CSuperblock>> GetActiveTriggers() const EXCLUSIVE_LOCKS_REQUIRED(cs);
bool AddNewTrigger(uint256 nHash) EXCLUSIVE_LOCKS_REQUIRED(cs);
void CleanAndRemoveTriggers() EXCLUSIVE_LOCKS_REQUIRED(cs);
// Superblocks related:
@ -373,7 +373,8 @@ public:
private:
void ExecuteBestSuperblock(const CDeterministicMNList& tip_mn_list, int nBlockHeight);
bool GetBestSuperblock(const CDeterministicMNList& tip_mn_list, CSuperblock_sptr& pSuperblockRet, int nBlockHeight);
bool GetBestSuperblock(const CDeterministicMNList& tip_mn_list, CSuperblock_sptr& pSuperblockRet, int nBlockHeight)
EXCLUSIVE_LOCKS_REQUIRED(cs);
std::optional<const CSuperblock> CreateSuperblockCandidate(int nHeight) const;
std::optional<const CGovernanceObject> CreateGovernanceTrigger(const std::optional<const CSuperblock>& sb_opt, PeerManager& peerman,

View File

@ -235,7 +235,8 @@ public:
/// Check the collateral transaction for the budget proposal/finalized budget
bool IsCollateralValid(const ChainstateManager& chainman, std::string& strError, bool& fMissingConfirmations) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void UpdateLocalValidity(const CDeterministicMNList& tip_mn_list, const ChainstateManager& chainman);
void UpdateLocalValidity(const CDeterministicMNList& tip_mn_list, const ChainstateManager& chainman)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void UpdateSentinelVariables(const CDeterministicMNList& tip_mn_list);

View File

@ -118,7 +118,7 @@ public:
/// sync once and only needs to process blocks in the ValidationInterface
/// queue. If the index is catching up from far behind, this method does
/// not block and immediately returns false.
bool BlockUntilSyncedToCurrentChain() const;
bool BlockUntilSyncedToCurrentChain() const LOCKS_EXCLUDED(::cs_main);
void Interrupt();

View File

@ -565,24 +565,29 @@ void CSigSharesManager::CollectPendingSigSharesToVerify(
// other nodes would be able to poison us with a large batch with N-1 valid shares and the last one being
// invalid, making batch verification fail and revert to per-share verification, which in turn would slow down
// the whole verification process
std::unordered_set<std::pair<NodeId, uint256>, StaticSaltedHasher> uniqueSignHashes;
IterateNodesRandom(nodeStates, [&]() {
return uniqueSignHashes.size() < maxUniqueSessions;
}, [&](NodeId nodeId, CSigSharesNodeState& ns) {
if (ns.pendingIncomingSigShares.Empty()) {
return false;
}
const auto& sigShare = *ns.pendingIncomingSigShares.GetFirst();
IterateNodesRandom(
nodeStates,
[&]() {
return uniqueSignHashes.size() < maxUniqueSessions;
// TODO: remove NO_THREAD_SAFETY_ANALYSIS
// using here template IterateNodesRandom makes impossible to use lock annotation
},
[&](NodeId nodeId, CSigSharesNodeState& ns) NO_THREAD_SAFETY_ANALYSIS {
if (ns.pendingIncomingSigShares.Empty()) {
return false;
}
const auto& sigShare = *ns.pendingIncomingSigShares.GetFirst();
AssertLockHeld(cs);
if (const bool alreadyHave = this->sigShares.Has(sigShare.GetKey()); !alreadyHave) {
uniqueSignHashes.emplace(nodeId, sigShare.GetSignHash());
retSigShares[nodeId].emplace_back(sigShare);
}
ns.pendingIncomingSigShares.Erase(sigShare.GetKey());
return !ns.pendingIncomingSigShares.Empty();
}, rnd);
AssertLockHeld(cs);
if (const bool alreadyHave = this->sigShares.Has(sigShare.GetKey()); !alreadyHave) {
uniqueSignHashes.emplace(nodeId, sigShare.GetSignHash());
retSigShares[nodeId].emplace_back(sigShare);
}
ns.pendingIncomingSigShares.Erase(sigShare.GetKey());
return !ns.pendingIncomingSigShares.Empty();
},
rnd);
if (retSigShares.empty()) {
return;
@ -1020,7 +1025,10 @@ void CSigSharesManager::CollectSigSharesToAnnounce(std::unordered_map<NodeId, st
std::unordered_map<std::pair<Consensus::LLMQType, uint256>, std::unordered_set<NodeId>, StaticSaltedHasher> quorumNodesMap;
sigSharesQueuedToAnnounce.ForEach([this, &quorumNodesMap, &sigSharesToAnnounce](const SigShareKey& sigShareKey, bool) {
// TODO: remove NO_THREAD_SAFETY_ANALYSIS
// using here template ForEach makes impossible to use lock annotation
sigSharesQueuedToAnnounce.ForEach([this, &quorumNodesMap, &sigSharesToAnnounce](const SigShareKey& sigShareKey,
bool) NO_THREAD_SAFETY_ANALYSIS {
AssertLockHeld(cs);
const auto& signHash = sigShareKey.first;
auto quorumMember = sigShareKey.second;
@ -1076,7 +1084,7 @@ bool CSigSharesManager::SendMessages()
std::unordered_map<NodeId, std::unordered_map<uint256, CSigSharesInv, StaticSaltedHasher>> sigSharesToAnnounce;
std::unordered_map<NodeId, std::vector<CSigSesAnn>> sigSessionAnnouncements;
auto addSigSesAnnIfNeeded = [&](NodeId nodeId, const uint256& signHash) {
auto addSigSesAnnIfNeeded = [&](NodeId nodeId, const uint256& signHash) EXCLUSIVE_LOCKS_REQUIRED(cs) {
AssertLockHeld(cs);
auto& nodeState = nodeStates[nodeId];
auto* session = nodeState.GetSessionBySignHash(signHash);
@ -1357,7 +1365,9 @@ void CSigSharesManager::Cleanup()
continue;
}
// remove global requested state to force a re-request from another node
it->second.requestedSigShares.ForEach([this](const SigShareKey& k, bool) {
// TODO: remove NO_THREAD_SAFETY_ANALYSIS
// using here template ForEach makes impossible to use lock annotation
it->second.requestedSigShares.ForEach([this](const SigShareKey& k, bool) NO_THREAD_SAFETY_ANALYSIS {
AssertLockHeld(cs);
sigSharesRequested.Erase(k);
});
@ -1390,7 +1400,9 @@ void CSigSharesManager::RemoveBannedNodeStates()
for (auto it = nodeStates.begin(); it != nodeStates.end();) {
if (Assert(m_peerman)->IsBanned(it->first)) {
// re-request sigshares from other nodes
it->second.requestedSigShares.ForEach([this](const SigShareKey& k, int64_t) {
// TODO: remove NO_THREAD_SAFETY_ANALYSIS
// using here template ForEach makes impossible to use lock annotation
it->second.requestedSigShares.ForEach([this](const SigShareKey& k, int64_t) NO_THREAD_SAFETY_ANALYSIS {
AssertLockHeld(cs);
sigSharesRequested.Erase(k);
});
@ -1419,7 +1431,9 @@ void CSigSharesManager::BanNode(NodeId nodeId)
auto& nodeState = it->second;
// Whatever we requested from him, let's request it from someone else now
nodeState.requestedSigShares.ForEach([this](const SigShareKey& k, int64_t) {
// TODO: remove NO_THREAD_SAFETY_ANALYSIS
// using here template ForEach makes impossible to use lock annotation
nodeState.requestedSigShares.ForEach([this](const SigShareKey& k, int64_t) NO_THREAD_SAFETY_ANALYSIS {
AssertLockHeld(cs);
sigSharesRequested.Erase(k);
});

View File

@ -209,7 +209,7 @@ public:
bool BuildQuorumRotationInfo(CDeterministicMNManager& dmnman, const ChainstateManager& chainman, const CQuorumManager& qman,
const CQuorumBlockProcessor& qblockman, const CGetQuorumRotationInfo& request,
CQuorumRotationInfo& response, std::string& errorRet);
CQuorumRotationInfo& response, std::string& errorRet) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
uint256 GetLastBaseBlockHash(Span<const CBlockIndex*> baseBlockIndexes, const CBlockIndex* blockIndex);
class CQuorumSnapshotManager

View File

@ -73,7 +73,11 @@ public:
UniValue ToJson() const;
public:
const uint256& GetProTxHash() const { LOCK(cs); return proTxHash; }
const uint256 GetProTxHash() const
{
LOCK(cs);
return proTxHash;
}
int64_t GetLastDsq() const { return nLastDsq; }
int GetMixingTxCount() const { return nMixingTxCount; }

View File

@ -647,8 +647,6 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
void CNode::CloseSocketDisconnect(CConnman* connman)
{
AssertLockHeld(connman->m_nodes_mutex);
fDisconnect = true;
LOCK2(connman->cs_mapSocketToNode, m_sock_mutex);
if (!m_sock) {
@ -3587,8 +3585,8 @@ void CConnman::ThreadOpenMasternodeConnections(CDeterministicMNManager& dmnman,
MasternodeProbeConn isProbe = MasternodeProbeConn::IsNotConnection;
const auto getPendingQuorumNodes = [&]() {
LockAssertion lock(cs_vPendingMasternodes);
const auto getPendingQuorumNodes = [&]() EXCLUSIVE_LOCKS_REQUIRED(cs_vPendingMasternodes) {
AssertLockHeld(cs_vPendingMasternodes);
std::vector<CDeterministicMNCPtr> ret;
for (const auto& group : masternodeQuorumNodes) {
for (const auto& proRegTxHash : group.second) {
@ -3626,8 +3624,8 @@ void CConnman::ThreadOpenMasternodeConnections(CDeterministicMNManager& dmnman,
return ret;
};
const auto getPendingProbes = [&]() {
LockAssertion lock(cs_vPendingMasternodes);
const auto getPendingProbes = [&]() EXCLUSIVE_LOCKS_REQUIRED(cs_vPendingMasternodes) {
AssertLockHeld(cs_vPendingMasternodes);
std::vector<CDeterministicMNCPtr> ret;
for (auto it = masternodePendingProbes.begin(); it != masternodePendingProbes.end(); ) {
auto dmn = mnList.GetMN(*it);

View File

@ -1316,6 +1316,8 @@ public:
return ForNode(id, FullyConnectedOnly, func);
}
using NodeFn = std::function<void(CNode*)>;
bool IsConnected(const CService& addr, std::function<bool(const CNode* pnode)> cond)
{
return ForNode(addr, cond, [](CNode* pnode){
@ -1372,10 +1374,9 @@ public:
}
};
template<typename Callable>
void ForEachNode(Callable&& func)
void ForEachNode(const NodeFn& fn)
{
ForEachNode(FullyConnectedOnly, func);
ForEachNode(FullyConnectedOnly, fn);
}
template<typename Condition, typename Callable>
@ -1388,10 +1389,9 @@ public:
}
};
template<typename Callable>
void ForEachNode(Callable&& func) const
void ForEachNode(const NodeFn& fn) const
{
ForEachNode(FullyConnectedOnly, func);
ForEachNode(FullyConnectedOnly, fn);
}
template<typename Condition, typename Callable, typename CallableAfter>

View File

@ -944,7 +944,7 @@ private:
/** Determine whether or not a peer can request a transaction, and return it (or nullptr if not found or not allowed). */
CTransactionRef FindTxForGetData(const CNode* peer, const uint256& txid, const std::chrono::seconds mempool_req, const std::chrono::seconds now) LOCKS_EXCLUDED(cs_main);
void ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic<bool>& interruptMsgProc) LOCKS_EXCLUDED(cs_main) EXCLUSIVE_LOCKS_REQUIRED(peer.m_getdata_requests_mutex);
void ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic<bool>& interruptMsgProc) EXCLUSIVE_LOCKS_REQUIRED(peer.m_getdata_requests_mutex) LOCKS_EXCLUDED(::cs_main);
/** Process a new block. Perform any post-processing housekeeping */
void ProcessBlock(CNode& from, const std::shared_ptr<const CBlock>& pblock, bool force_processing);
@ -1930,12 +1930,14 @@ void PeerManagerImpl::StartScheduledTasks(CScheduler& scheduler)
*/
void PeerManagerImpl::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex)
{
LOCK2(::cs_main, g_cs_orphans);
{
LOCK2(::cs_main, g_cs_orphans);
auto orphanWorkSet = m_orphanage.GetCandidatesForBlock(*pblock);
while (!orphanWorkSet.empty()) {
LogPrint(BCLog::MEMPOOL, "Trying to process %d orphans\n", orphanWorkSet.size());
ProcessOrphanTx(orphanWorkSet);
auto orphanWorkSet = m_orphanage.GetCandidatesForBlock(*pblock);
while (!orphanWorkSet.empty()) {
LogPrint(BCLog::MEMPOOL, "Trying to process %d orphans\n", orphanWorkSet.size());
ProcessOrphanTx(orphanWorkSet);
}
}
m_orphanage.EraseForBlock(*pblock);
@ -1998,8 +2000,8 @@ void PeerManagerImpl::NewPoWValidBlock(const CBlockIndex *pindex, const std::sha
m_most_recent_compact_block = pcmpctblock;
}
m_connman.ForEachNode([this, pindex, &lazy_ser, &hashBlock](CNode* pnode) {
LockAssertion lock(::cs_main);
m_connman.ForEachNode([this, pindex, &lazy_ser, &hashBlock](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
AssertLockHeld(::cs_main);
// TODO: Avoid the repeated-serialization here
if (pnode->fDisconnect)
return;
@ -5276,8 +5278,8 @@ void PeerManagerImpl::EvictExtraOutboundPeers(std::chrono::seconds now)
// We want to prevent recently connected to Onion peers from being disconnected here, protect them as long as
// there are more non_onion nodes than onion nodes so far
size_t onion_count = 0;
m_connman.ForEachNode([&](CNode* pnode) {
LockAssertion lock(::cs_main);
m_connman.ForEachNode([&](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
AssertLockHeld(::cs_main);
if (pnode->addr.IsTor() && ++onion_count <= m_connman.GetMaxOutboundOnionNodeCount()) return;
// Don't disconnect masternodes just because they were slow in block announcement
if (pnode->m_masternode_connection) return;
@ -5294,8 +5296,8 @@ void PeerManagerImpl::EvictExtraOutboundPeers(std::chrono::seconds now)
}
});
if (worst_peer != -1) {
bool disconnected = m_connman.ForNode(worst_peer, [&](CNode *pnode) {
LockAssertion lock(::cs_main);
bool disconnected = m_connman.ForNode(worst_peer, [&](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
AssertLockHeld(::cs_main);
// Only disconnect a peer that has been connected to us for
// some reasonable fraction of our check-frequency, to give
@ -5774,7 +5776,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
peer->m_blocks_for_inv_relay.clear();
}
auto queueAndMaybePushInv = [this, pto, peer, &vInv, &msgMaker](const CInv& invIn) {
auto queueAndMaybePushInv = [this, pto, peer, &vInv, &msgMaker](const CInv& invIn) EXCLUSIVE_LOCKS_REQUIRED(peer->m_tx_relay->m_tx_inventory_mutex) {
AssertLockHeld(peer->m_tx_relay->m_tx_inventory_mutex);
peer->m_tx_relay->m_tx_inventory_known_filter.insert(invIn.hash);
LogPrint(BCLog::NET, "SendMessages -- queued inv: %s index=%d peer=%d\n", invIn.ToString(), vInv.size(), pto->GetId());

View File

@ -213,8 +213,9 @@ static bool ValidatePlatformPort(const int32_t port)
#ifdef ENABLE_WALLET
template<typename SpecialTxPayload>
static void FundSpecialTx(CWallet& wallet, CMutableTransaction& tx, const SpecialTxPayload& payload, const CTxDestination& fundDest)
template <typename SpecialTxPayload>
static void FundSpecialTx(CWallet& wallet, CMutableTransaction& tx, const SpecialTxPayload& payload,
const CTxDestination& fundDest) EXCLUSIVE_LOCKS_REQUIRED(!wallet.cs_wallet)
{
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now

View File

@ -285,12 +285,16 @@ template void AssertLockHeldInternal(const char*, const char*, int, Mutex*);
template void AssertLockHeldInternal(const char*, const char*, int, RecursiveMutex*);
template void AssertLockHeldInternal(const char*, const char*, int, SharedMutex*);
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs)
template <typename MutexType>
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs)
{
if (!LockHeld(cs)) return;
tfm::format(std::cerr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld());
abort();
}
template void AssertLockNotHeldInternal(const char*, const char*, int, Mutex*);
template void AssertLockNotHeldInternal(const char*, const char*, int, RecursiveMutex*);
template void AssertLockNotHeldInternal(const char*, const char*, int, SharedMutex*);
void DeleteLock(void* cs)
{

View File

@ -57,8 +57,9 @@ void LeaveCritical();
void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line);
std::string LocksHeld();
template <typename MutexType>
void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) ASSERT_EXCLUSIVE_LOCK(cs);
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs);
template <typename MutexType>
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) LOCKS_EXCLUDED(cs);
void DeleteLock(void* cs);
bool LockStackEmpty();
@ -74,8 +75,9 @@ inline void EnterCritical(const char* pszName, const char* pszFile, int nLine, M
inline void LeaveCritical() {}
inline void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {}
template <typename MutexType>
inline void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) ASSERT_EXCLUSIVE_LOCK(cs) {}
inline void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
inline void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs) {}
template <typename MutexType>
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) LOCKS_EXCLUDED(cs) {}
inline void DeleteLock(void* cs) {}
inline bool LockStackEmpty() { return true; }
#endif
@ -481,18 +483,4 @@ public:
}
};
// Utility class for indicating to compiler thread analysis that a mutex is
// locked (when it couldn't be determined otherwise).
struct SCOPED_LOCKABLE LockAssertion
{
template <typename Mutex>
explicit LockAssertion(Mutex& mutex) EXCLUSIVE_LOCK_FUNCTION(mutex)
{
#ifdef DEBUG_LOCKORDER
AssertLockHeld(mutex);
#endif
}
~LockAssertion() UNLOCK_FUNCTION() {}
};
#endif // BITCOIN_SYNC_H

View File

@ -955,7 +955,7 @@ void CTxMemPool::removeProTxSpentCollateralConflicts(const CTransaction &tx)
assert(m_dmnman);
// Remove TXs that refer to a MN for which the collateral was spent
auto removeSpentCollateralConflict = [&](const uint256& proTxHash) {
auto removeSpentCollateralConflict = [&](const uint256& proTxHash) EXCLUSIVE_LOCKS_REQUIRED(cs) {
// Can't use equal_range here as every call to removeRecursive might invalidate iterators
AssertLockHeld(cs);
while (true) {
@ -1353,7 +1353,7 @@ bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const {
LOCK(cs);
auto hasKeyChangeInMempool = [&](const uint256& proTxHash) {
auto hasKeyChangeInMempool = [&](const uint256& proTxHash) EXCLUSIVE_LOCKS_REQUIRED(cs) {
AssertLockHeld(cs);
for (auto its = mapProTxRefs.equal_range(proTxHash); its.first != its.second; ++its.first) {
auto txit = mapTx.find(its.first->second);

View File

@ -197,7 +197,7 @@ std::set<uint256> TxOrphanage::GetCandidatesForBlock(const CBlock& block)
void TxOrphanage::EraseForBlock(const CBlock& block)
{
AssertLockHeld(g_cs_orphans);
LOCK(g_cs_orphans);
std::vector<uint256> vOrphanErase;

View File

@ -41,7 +41,7 @@ public:
void EraseForPeer(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
/** Erase all orphans included in or invalidated by a new block */
void EraseForBlock(const CBlock& block) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
void EraseForBlock(const CBlock& block) LOCKS_EXCLUDED(::g_cs_orphans);
/** Limit the orphanage to the given maximum */
unsigned int LimitOrphans(unsigned int max_orphans_size) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);

View File

@ -420,7 +420,7 @@ std::optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) co
return std::nullopt;
}
const fs::path& ArgsManager::GetBlocksDirPath() const
const fs::path ArgsManager::GetBlocksDirPath() const
{
LOCK(cs_args);
fs::path& path = m_cached_blocks_path;
@ -445,7 +445,7 @@ const fs::path& ArgsManager::GetBlocksDirPath() const
return path;
}
const fs::path& ArgsManager::GetDataDir(bool net_specific) const
const fs::path ArgsManager::GetDataDir(bool net_specific) const
{
LOCK(cs_args);
fs::path& path = net_specific ? m_cached_network_datadir_path : m_cached_datadir_path;

View File

@ -288,7 +288,7 @@ protected:
*
* @return Blocks path which is network specific
*/
const fs::path& GetBlocksDirPath() const;
const fs::path GetBlocksDirPath() const;
/**
* Get data directory path
@ -296,7 +296,7 @@ protected:
* @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
* @post Returned directory path is created unless it is empty
*/
const fs::path& GetDataDirBase() const { return GetDataDir(false); }
const fs::path GetDataDirBase() const { return GetDataDir(false); }
/**
* Get data directory path with appended network identifier
@ -304,7 +304,7 @@ protected:
* @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
* @post Returned directory path is created unless it is empty
*/
const fs::path& GetDataDirNet() const { return GetDataDir(true); }
const fs::path GetDataDirNet() const { return GetDataDir(true); }
fs::path GetBackupsDirPath();
@ -483,7 +483,7 @@ private:
* @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
* @post Returned directory path is created unless it is empty
*/
const fs::path& GetDataDir(bool net_specific) const;
const fs::path GetDataDir(bool net_specific) const;
// Helper function for LogArgs().
void logArgsPrefix(

View File

@ -3638,9 +3638,10 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
* in ConnectBlock().
* Note that -reindex-chainstate skips the validation that happens here!
*/
static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
AssertLockHeld(cs_main);
// TODO: validate - why do we need this cs_main ?
AssertLockHeld(::cs_main);
const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
// Enforce BIP113 (Median Time Past).

View File

@ -342,8 +342,11 @@ void LegacyScriptPubKeyMan::UpgradeKeyMetadata()
CHDChain hdChainCurrent;
if (!GetHDChain(hdChainCurrent))
throw std::runtime_error(std::string(__func__) + ": GetHDChain failed");
if (!DecryptHDChain(m_storage.GetEncryptionKey(), hdChainCurrent))
if (!m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {
return DecryptHDChain(encryption_key, hdChainCurrent);
})) {
throw std::runtime_error(std::string(__func__) + ": DecryptHDChain failed");
}
CExtKey masterKey;
SecureVector vchSeed = hdChainCurrent.GetSeed();
@ -474,8 +477,11 @@ bool LegacyScriptPubKeyMan::GetDecryptedHDChain(CHDChain& hdChainRet)
return false;
}
if (!DecryptHDChain(m_storage.GetEncryptionKey(), hdChainTmp))
if (!m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {
return DecryptHDChain(encryption_key, hdChainTmp);
})) {
return false;
}
// make sure seed matches this chain
if (hdChainTmp.GetID() != hdChainTmp.GetSeedHash())
@ -911,7 +917,9 @@ bool LegacyScriptPubKeyMan::AddKeyPubKeyInner(const CKey& key, const CPubKey &pu
std::vector<unsigned char> vchCryptedSecret;
CKeyingMaterial vchSecret(key.begin(), key.end());
if (!EncryptSecret(m_storage.GetEncryptionKey(), vchSecret, pubkey.GetHash(), vchCryptedSecret)) {
if (!m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {
return EncryptSecret(encryption_key, vchSecret, pubkey.GetHash(), vchCryptedSecret);
})) {
return false;
}
@ -933,7 +941,9 @@ bool LegacyScriptPubKeyMan::GetKeyInner(const CKeyID &address, CKey& keyOut) con
{
const CPubKey &vchPubKey = (*mi).second.first;
const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
return DecryptKey(m_storage.GetEncryptionKey(), vchCryptedSecret, vchPubKey, keyOut);
return m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {
return DecryptKey(encryption_key, vchCryptedSecret, vchPubKey, keyOut);
});
}
return false;
}
@ -1164,8 +1174,11 @@ bool LegacyScriptPubKeyMan::GetKey(const CKeyID &address, CKey& keyOut) const
CHDChain hdChainCurrent;
if (!GetHDChain(hdChainCurrent))
throw std::runtime_error(std::string(__func__) + ": GetHDChain failed");
if (!DecryptHDChain(m_storage.GetEncryptionKey(), hdChainCurrent))
if (!m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {
return DecryptHDChain(encryption_key, hdChainCurrent);
})) {
throw std::runtime_error(std::string(__func__) + ": DecryptHDChain failed");
}
// make sure seed matches this chain
if (hdChainCurrent.GetID() != hdChainCurrent.GetSeedHash())
throw std::runtime_error(std::string(__func__) + ": Wrong HD chain!");
@ -1276,8 +1289,11 @@ void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata&
throw std::runtime_error(std::string(__func__) + ": GetHDChain failed");
}
if (!DecryptHDChain(m_storage.GetEncryptionKey(), hdChainTmp))
if (!m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {
return DecryptHDChain(encryption_key, hdChainTmp);
})) {
throw std::runtime_error(std::string(__func__) + ": DecryptHDChain failed");
}
// make sure seed matches this chain
if (hdChainTmp.GetID() != hdChainTmp.GetSeedHash())
throw std::runtime_error(std::string(__func__) + ": Wrong HD chain!");
@ -1897,7 +1913,9 @@ std::map<CKeyID, CKey> DescriptorScriptPubKeyMan::GetKeys() const
const CPubKey& pubkey = key_pair.second.first;
const std::vector<unsigned char>& crypted_secret = key_pair.second.second;
CKey key;
DecryptKey(m_storage.GetEncryptionKey(), crypted_secret, pubkey, key);
m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {
return DecryptKey(encryption_key, crypted_secret, pubkey, key);
});
keys[pubkey.GetID()] = key;
}
return keys;
@ -2010,7 +2028,9 @@ bool DescriptorScriptPubKeyMan::AddDescriptorKeyWithDB(WalletBatch& batch, const
std::vector<unsigned char> crypted_secret;
CKeyingMaterial secret(key.begin(), key.end());
if (!EncryptSecret(m_storage.GetEncryptionKey(), secret, pubkey.GetHash(), crypted_secret)) {
if (!m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {
return EncryptSecret(encryption_key, secret, pubkey.GetHash(), crypted_secret);
})) {
return false;
}

View File

@ -21,6 +21,7 @@
#include <boost/signals2/signal.hpp>
#include <functional>
#include <optional>
// Wallet storage things that ScriptPubKeyMans need in order to be able to store things to the wallet database.
@ -38,7 +39,8 @@ public:
virtual void UnsetBlankWalletFlag(WalletBatch&) = 0;
virtual bool CanSupportFeature(enum WalletFeature) const = 0;
virtual void SetMinVersion(enum WalletFeature, WalletBatch* = nullptr) = 0;
virtual const CKeyingMaterial& GetEncryptionKey() const = 0;
//! Pass the encryption key to cb().
virtual bool WithEncryptionKey(std::function<bool (const CKeyingMaterial&)> cb) const = 0;
virtual bool HasEncryptionKeys() const = 0;
virtual bool IsLocked(bool fForMixing) const = 0;
@ -526,7 +528,7 @@ private:
//! keeps track of whether Unlock has run a thorough check before
bool m_decryption_thoroughly_checked = false;
bool AddDescriptorKeyWithDB(WalletBatch& batch, const CKey& key, const CPubKey &pubkey);
bool AddDescriptorKeyWithDB(WalletBatch& batch, const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
KeyMap GetKeys() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);

View File

@ -3681,7 +3681,7 @@ bool CWallet::CreateTransactionInternal(
txNew.vin.emplace_back(coin.outpoint, CScript(), CTxIn::SEQUENCE_FINAL - 1);
}
auto calculateFee = [&](CAmount& nFee) -> bool {
auto calculateFee = [&](CAmount& nFee) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) -> bool {
AssertLockHeld(cs_wallet);
nBytes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
if (nBytes < 0) {
@ -5743,9 +5743,10 @@ void CWallet::SetupLegacyScriptPubKeyMan()
m_spk_managers[spk_manager->GetID()] = std::move(spk_manager);
}
const CKeyingMaterial& CWallet::GetEncryptionKey() const
bool CWallet::WithEncryptionKey(std::function<bool (const CKeyingMaterial&)> cb) const
{
return vMasterKey;
LOCK(cs_wallet);
return cb(vMasterKey);
}
bool CWallet::HasEncryptionKeys() const

View File

@ -516,8 +516,12 @@ public:
CAmount GetImmatureWatchOnlyCredit(const bool fUseCache = true) const;
CAmount GetChange() const;
CAmount GetAnonymizedCredit(const CCoinControl* coinControl = nullptr) const;
CAmount GetDenominatedCredit(bool unconfirmed, bool fUseCache=true) const;
// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
// annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The
// annotation "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid
// having to resolve the issue of member access into incomplete type CWallet.
CAmount GetAnonymizedCredit(const CCoinControl* coinControl = nullptr) const NO_THREAD_SAFETY_ANALYSIS;
CAmount GetDenominatedCredit(bool unconfirmed, bool fUseCache=true) const NO_THREAD_SAFETY_ANALYSIS;
/** Get the marginal bytes if spending the specified output from this transaction */
int GetSpendSize(unsigned int out, bool use_max_sig = false) const
@ -569,7 +573,7 @@ public:
int GetDepthInMainChain() const NO_THREAD_SAFETY_ANALYSIS;
bool IsInMainChain() const { return GetDepthInMainChain() > 0; }
bool IsLockedByInstantSend() const;
bool IsChainLocked() const;
bool IsChainLocked() const NO_THREAD_SAFETY_ANALYSIS;
/**
* @return number of blocks to maturity for this transaction:
@ -1289,7 +1293,7 @@ public:
std::set<uint256> GetConflicts(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Check if a given transaction has any of its outputs spent by another transaction in the wallet
bool HasWalletSpend(const uint256& txid) const;
bool HasWalletSpend(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Flush wallet (bitdb flush)
void Flush();
@ -1386,11 +1390,11 @@ public:
void notifyChainLock(const CBlockIndex* pindexChainLock, const std::shared_ptr<const llmq::CChainLockSig>& clsig) override;
/** Load a CGovernanceObject into m_gobjects. */
bool LoadGovernanceObject(const Governance::Object& obj);
bool LoadGovernanceObject(const Governance::Object& obj) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** Store a CGovernanceObject in the wallet database. This should only be used by governance objects that are created by this wallet via `gobject prepare`. */
bool WriteGovernanceObject(const Governance::Object& obj);
bool WriteGovernanceObject(const Governance::Object& obj) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** Returns a vector containing pointers to the governance objects in m_gobjects */
std::vector<const Governance::Object*> GetGovernanceObjects();
std::vector<const Governance::Object*> GetGovernanceObjects() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Blocks until the wallet state is up-to-date to /at least/ the current
@ -1398,7 +1402,7 @@ public:
* Obviously holding cs_main/cs_wallet when going into this call may cause
* deadlock
*/
void BlockUntilSyncedToCurrentChain() const LOCKS_EXCLUDED(cs_main, cs_wallet);
void BlockUntilSyncedToCurrentChain() const LOCKS_EXCLUDED(::cs_main) EXCLUSIVE_LOCKS_REQUIRED(!cs_wallet);
/** set a single wallet flag */
void SetWalletFlag(uint64_t flags);
@ -1464,7 +1468,8 @@ public:
//! Make a LegacyScriptPubKeyMan and set it for all types, internal, and external.
void SetupLegacyScriptPubKeyMan();
const CKeyingMaterial& GetEncryptionKey() const override;
bool WithEncryptionKey(std::function<bool (const CKeyingMaterial&)> cb) const override;
bool HasEncryptionKeys() const override;
/** Get last block processed height */
@ -1510,7 +1515,7 @@ public:
void DeactivateScriptPubKeyMan(uint256 id, bool internal);
//! Create new DescriptorScriptPubKeyMans and add them to the wallet
void SetupDescriptorScriptPubKeyMans();
void SetupDescriptorScriptPubKeyMans() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Return the DescriptorScriptPubKeyMan for a WalletDescriptor if it is already in the wallet
DescriptorScriptPubKeyMan* GetDescriptorScriptPubKeyMan(const WalletDescriptor& desc) const;