Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions doc/developer-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
9 changes: 7 additions & 2 deletions src/coinjoin/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
13 changes: 9 additions & 4 deletions src/coinjoin/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,21 +161,26 @@ class CCoinJoinClientSession : public CCoinJoinBaseSession

/// 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);

/// 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);

Expand Down
4 changes: 2 additions & 2 deletions src/coinjoin/coinjoin.h
Original file line number Diff line number Diff line change
Expand Up @@ -398,8 +398,8 @@ class CDSTXManager
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)
Expand Down
12 changes: 7 additions & 5 deletions src/evo/deterministicmns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
7 changes: 4 additions & 3 deletions src/evo/simplifiedmns.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,9 @@ class CSimplifiedMNListDiff
[[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
7 changes: 4 additions & 3 deletions src/evo/specialtxman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
9 changes: 5 additions & 4 deletions src/governance/governance.h
Original file line number Diff line number Diff line change
Expand Up @@ -347,9 +347,9 @@ class CGovernanceManager : public GovernanceStore
* - 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:

Expand All @@ -373,7 +373,8 @@ class CGovernanceManager : public GovernanceStore

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,
Expand Down
3 changes: 2 additions & 1 deletion src/governance/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,8 @@ class CGovernanceObject
/// 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);

Expand Down
2 changes: 1 addition & 1 deletion src/index/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class BaseIndex : public CValidationInterface
/// 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();

Expand Down
56 changes: 35 additions & 21 deletions src/llmq/signing_shares.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
});
Expand Down Expand Up @@ -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);
});
Expand Down Expand Up @@ -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);
});
Expand Down
2 changes: 1 addition & 1 deletion src/llmq/snapshot.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ class CQuorumRotationInfo

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
Expand Down
6 changes: 5 additions & 1 deletion src/masternode/meta.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,11 @@ class CMasternodeMetaInfo
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; }

Expand Down
10 changes: 4 additions & 6 deletions src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -643,8 +643,6 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo

void CNode::CloseSocketDisconnect(CConnman* connman)
{
AssertLockHeld(connman->m_nodes_mutex);
Comment thread
UdjinM6 marked this conversation as resolved.
Outdated

fDisconnect = true;
LOCK2(connman->cs_mapSocketToNode, m_sock_mutex);
if (!m_sock) {
Expand Down Expand Up @@ -3512,8 +3510,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) {
Expand Down Expand Up @@ -3551,8 +3549,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);
Expand Down
12 changes: 6 additions & 6 deletions src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -1275,6 +1275,8 @@ friend class CNode;
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){
Expand Down Expand Up @@ -1331,10 +1333,9 @@ friend class CNode;
}
};

template<typename Callable>
void ForEachNode(Callable&& func)
void ForEachNode(const NodeFn& fn)
{
ForEachNode(FullyConnectedOnly, func);
ForEachNode(FullyConnectedOnly, fn);
}

template<typename Condition, typename Callable>
Expand All @@ -1347,10 +1348,9 @@ friend class CNode;
}
};

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>
Expand Down
Loading