refactor(coinjoin): remove CCriticalSection from coinjoin code, use mutexes instead (#4739)

* refactor: remove CCriticalSection from coinjoin code, use mutexes instead

* refactor(coinjoin): more annotations

* fix cs_coinjoin double-lock deadlock

Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
This commit is contained in:
PastaPastaPasta 2022-04-25 04:28:37 -05:00 committed by GitHub
parent 43152b2b35
commit 407873e024
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 110 additions and 79 deletions

View File

@ -99,9 +99,11 @@ void CCoinJoinClientQueueManager::ProcessMessage(CNode* pfrom, const std::string
ranges::any_of(coinJoinClientManagers,
[&dsq](const auto& pair){ return pair.second->MarkAlreadyJoinedQueueAsTried(dsq); });
TRY_LOCK(cs_vecqueue, lockRecv);
if (!lockRecv) return;
vecCoinJoinQueue.push_back(dsq);
{
TRY_LOCK(cs_vecqueue, lockRecv);
if (!lockRecv) return;
vecCoinJoinQueue.push_back(dsq);
}
dsq.Relay(connman);
}
@ -124,6 +126,7 @@ void CCoinJoinClientManager::ProcessMessage(CNode* pfrom, const std::string& msg
if (msg_type == NetMsgType::DSSTATUSUPDATE ||
msg_type == NetMsgType::DSFINALTX ||
msg_type == NetMsgType::DSCOMPLETE) {
AssertLockNotHeld(cs_deqsessions);
LOCK(cs_deqsessions);
for (auto& session : deqSessions) {
session.ProcessMessage(pfrom, msg_type, vRecv, connman, enable_bip61);
@ -214,13 +217,14 @@ void CCoinJoinClientSession::ResetPool()
txMyCollateral = CMutableTransaction();
UnlockCoins();
keyHolderStorage.ReturnAll();
SetNull();
WITH_LOCK(cs_coinjoin, SetNull());
}
void CCoinJoinClientManager::ResetPool()
{
nCachedLastSuccessBlock = 0;
vecMasternodesUsed.clear();
AssertLockNotHeld(cs_deqsessions);
LOCK(cs_deqsessions);
for (auto& session : deqSessions) {
session.ResetPool();
@ -230,6 +234,7 @@ void CCoinJoinClientManager::ResetPool()
void CCoinJoinClientSession::SetNull()
{
AssertLockHeld(cs_coinjoin);
// Client side
mixingMasternode = nullptr;
pendingDsaRequest = CPendingDsaRequest();
@ -303,6 +308,7 @@ bilingual_str CCoinJoinClientManager::GetStatuses()
bilingual_str strStatus;
bool fWaitForBlock = WaitForAnotherBlock();
AssertLockNotHeld(cs_deqsessions);
LOCK(cs_deqsessions);
for (const auto& session : deqSessions) {
strStatus = strStatus + session.GetStatus(fWaitForBlock) + Untranslated("; ");
@ -314,6 +320,7 @@ std::string CCoinJoinClientManager::GetSessionDenoms()
{
std::string strSessionDenoms;
AssertLockNotHeld(cs_deqsessions);
LOCK(cs_deqsessions);
for (const auto& session : deqSessions) {
strSessionDenoms += CCoinJoin::DenominationToString(session.nSessionDenom);
@ -330,6 +337,7 @@ bool CCoinJoinClientSession::GetMixingMasternodeInfo(CDeterministicMNCPtr& ret)
bool CCoinJoinClientManager::GetMixingMasternodesInfo(std::vector<CDeterministicMNCPtr>& vecDmnsRet) const
{
AssertLockNotHeld(cs_deqsessions);
LOCK(cs_deqsessions);
for (const auto& session : deqSessions) {
CDeterministicMNCPtr dmn;
@ -353,7 +361,7 @@ bool CCoinJoinClientSession::CheckTimeout()
if (GetTime() - nTimeLastSuccessfulStep >= 10) {
// reset after being in POOL_STATE_ERROR for 10 or more seconds
LogPrint(BCLog::COINJOIN, "CCoinJoinClientSession::%s -- resetting session %d\n", __func__, nSessionID);
SetNull();
WITH_LOCK(cs_coinjoin, SetNull());
}
return false;
}
@ -381,6 +389,7 @@ bool CCoinJoinClientSession::CheckTimeout()
//
void CCoinJoinClientManager::CheckTimeout()
{
AssertLockNotHeld(cs_deqsessions);
if (fMasternodeMode) return;
if (!CCoinJoinClientOptions::IsEnabled() || !IsMixing()) return;
@ -414,14 +423,14 @@ bool CCoinJoinClientSession::SendDenominate(const std::vector<std::pair<CTxDSIn,
LogPrint(BCLog::COINJOIN, "CCoinJoinClientSession::SendDenominate -- No Masternode has been selected yet.\n");
UnlockCoins();
keyHolderStorage.ReturnAll();
SetNull();
WITH_LOCK(cs_coinjoin, SetNull());
return false;
}
if (!CheckDiskSpace(GetDataDir())) {
UnlockCoins();
keyHolderStorage.ReturnAll();
SetNull();
WITH_LOCK(cs_coinjoin, SetNull());
LogPrint(BCLog::COINJOIN, "CCoinJoinClientSession::SendDenominate -- Not enough disk space.\n");
return false;
}
@ -634,7 +643,7 @@ void CCoinJoinClientSession::CompletedTransaction(PoolMessage nMessageID)
keyHolderStorage.ReturnAll();
}
UnlockCoins();
SetNull();
WITH_LOCK(cs_coinjoin, SetNull());
strLastMessage = CCoinJoin::GetMessageByID(nMessageID);
}
@ -931,6 +940,7 @@ bool CCoinJoinClientManager::DoAutomaticDenominating(CConnman& connman, bool fDr
}
bool fResult = true;
AssertLockNotHeld(cs_deqsessions);
LOCK(cs_deqsessions);
if ((int)deqSessions.size() < CCoinJoinClientOptions::GetSessions()) {
deqSessions.emplace_back(mixingWallet);
@ -1152,7 +1162,7 @@ bool CCoinJoinClientSession::ProcessPendingDsaRequest(CConnman& connman)
pendingDsaRequest = CPendingDsaRequest();
} else if (pendingDsaRequest.IsExpired()) {
LogPrint(BCLog::COINJOIN, "CCoinJoinClientSession::%s -- failed to connect to %s\n", __func__, pendingDsaRequest.GetAddr().ToString());
SetNull();
WITH_LOCK(cs_coinjoin, SetNull());
}
return fDone;
@ -1160,6 +1170,7 @@ bool CCoinJoinClientSession::ProcessPendingDsaRequest(CConnman& connman)
void CCoinJoinClientManager::ProcessPendingDsaRequest(CConnman& connman)
{
AssertLockNotHeld(cs_deqsessions);
LOCK(cs_deqsessions);
for (auto& session : deqSessions) {
if (session.ProcessPendingDsaRequest(connman)) {
@ -1170,6 +1181,7 @@ void CCoinJoinClientManager::ProcessPendingDsaRequest(CConnman& connman)
bool CCoinJoinClientManager::TrySubmitDenominate(const CService& mnAddr, CConnman& connman)
{
AssertLockNotHeld(cs_deqsessions);
LOCK(cs_deqsessions);
for (auto& session : deqSessions) {
CDeterministicMNCPtr mnMixing;
@ -1183,6 +1195,7 @@ bool CCoinJoinClientManager::TrySubmitDenominate(const CService& mnAddr, CConnma
bool CCoinJoinClientManager::MarkAlreadyJoinedQueueAsTried(CCoinJoinQueue& dsq) const
{
AssertLockNotHeld(cs_deqsessions);
LOCK(cs_deqsessions);
for (const auto& session : deqSessions) {
CDeterministicMNCPtr mnMixing;
@ -1818,6 +1831,7 @@ void CCoinJoinClientManager::GetJsonInfo(UniValue& obj) const
obj.pushKV("running", IsMixing());
UniValue arrSessions(UniValue::VARR);
AssertLockNotHeld(cs_deqsessions);
LOCK(cs_deqsessions);
for (const auto& session : deqSessions) {
if (session.GetState() != POOL_STATE_IDLE) {

View File

@ -101,7 +101,7 @@ private:
/// 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);
/// step 2: send denominated inputs and outputs prepared in step 1
bool SendDenominate(const std::vector<std::pair<CTxDSIn, CTxOut> >& vecPSInOutPairsIn, CConnman& connman);
bool SendDenominate(const std::vector<std::pair<CTxDSIn, CTxOut> >& vecPSInOutPairsIn, CConnman& connman) LOCKS_EXCLUDED(cs_coinjoin);
/// Process Masternode updates about the progress of mixing
void ProcessPoolStateUpdate(CCoinJoinStatusUpdate psssup);
@ -111,11 +111,11 @@ private:
void CompletedTransaction(PoolMessage nMessageID);
/// As a client, check and sign the final transaction
bool SignFinalTransaction(const CTransaction& finalTransactionNew, CNode* pnode, CConnman& connman);
bool SignFinalTransaction(const CTransaction& finalTransactionNew, CNode* pnode, CConnman& connman) LOCKS_EXCLUDED(cs_coinjoin);
void RelayIn(const CCoinJoinEntry& entry, CConnman& connman) const;
void SetNull();
void SetNull() EXCLUSIVE_LOCKS_REQUIRED(cs_coinjoin);
public:
explicit CCoinJoinClientSession(CWallet& pwallet) :
@ -127,14 +127,14 @@ public:
void UnlockCoins();
void ResetPool();
void ResetPool() LOCKS_EXCLUDED(cs_coinjoin);
bilingual_str GetStatus(bool fWaitForBlock) const;
bool GetMixingMasternodeInfo(CDeterministicMNCPtr& ret) const;
/// Passively run mixing in the background according to the configuration in settings
bool DoAutomaticDenominating(CConnman& connman, bool fDryRun = false);
bool DoAutomaticDenominating(CConnman& connman, bool fDryRun = false) LOCKS_EXCLUDED(cs_coinjoin);
/// As a client, submit part of a future mixing transaction to a Masternode to start the process
bool SubmitDenominate(CConnman& connman);
@ -151,7 +151,7 @@ public:
class CCoinJoinClientQueueManager : public CCoinJoinBaseManager
{
public:
void ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRecv, CConnman& connman, bool enable_bip61);
void ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRecv, CConnman& connman, bool enable_bip61) LOCKS_EXCLUDED(cs_vecqueue);
void DoMaintenance();
};
@ -164,7 +164,7 @@ private:
// Keep track of the used Masternodes
std::vector<COutPoint> vecMasternodesUsed;
mutable CCriticalSection cs_deqsessions;
mutable Mutex cs_deqsessions;
// TODO: or map<denom, CCoinJoinClientSession> ??
std::deque<CCoinJoinClientSession> deqSessions GUARDED_BY(cs_deqsessions);
@ -195,27 +195,27 @@ public:
explicit CCoinJoinClientManager(CWallet& wallet) :
mixingWallet(wallet) {}
void ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRecv, CConnman& connman, bool enable_bip61);
void ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRecv, CConnman& connman, bool enable_bip61) LOCKS_EXCLUDED(cs_deqsessions);
bool StartMixing();
void StopMixing();
bool IsMixing() const;
void ResetPool();
void ResetPool() LOCKS_EXCLUDED(cs_deqsessions);
bilingual_str GetStatuses();
std::string GetSessionDenoms();
bilingual_str GetStatuses() LOCKS_EXCLUDED(cs_deqsessions);
std::string GetSessionDenoms() LOCKS_EXCLUDED(cs_deqsessions);
bool GetMixingMasternodesInfo(std::vector<CDeterministicMNCPtr>& vecDmnsRet) const;
bool GetMixingMasternodesInfo(std::vector<CDeterministicMNCPtr>& vecDmnsRet) const LOCKS_EXCLUDED(cs_deqsessions);
/// Passively run mixing in the background according to the configuration in settings
bool DoAutomaticDenominating(CConnman& connman, bool fDryRun = false);
bool DoAutomaticDenominating(CConnman& connman, bool fDryRun = false) LOCKS_EXCLUDED(cs_deqsessions);
bool TrySubmitDenominate(const CService& mnAddr, CConnman& connman);
bool MarkAlreadyJoinedQueueAsTried(CCoinJoinQueue& dsq) const;
bool TrySubmitDenominate(const CService& mnAddr, CConnman& connman) LOCKS_EXCLUDED(cs_deqsessions);
bool MarkAlreadyJoinedQueueAsTried(CCoinJoinQueue& dsq) const LOCKS_EXCLUDED(cs_deqsessions);
void CheckTimeout();
void CheckTimeout() LOCKS_EXCLUDED(cs_deqsessions);
void ProcessPendingDsaRequest(CConnman& connman);
void ProcessPendingDsaRequest(CConnman& connman) LOCKS_EXCLUDED(cs_deqsessions);
void AddUsedMasternode(const COutPoint& outpointMn);
CDeterministicMNCPtr GetRandomNotUsedMasternode();
@ -226,7 +226,7 @@ public:
void DoMaintenance(CConnman& connman);
void GetJsonInfo(UniValue& obj) const;
void GetJsonInfo(UniValue& obj) const LOCKS_EXCLUDED(cs_deqsessions);
};

View File

@ -145,7 +145,7 @@ bool CCoinJoinBroadcastTx::IsValidStructure() const
void CCoinJoinBaseSession::SetNull()
{
// Both sides
LOCK(cs_coinjoin);
AssertLockHeld(cs_coinjoin);
nState = POOL_STATE_IDLE;
nSessionID = 0;
nSessionDenom = 0;
@ -301,7 +301,7 @@ bool CCoinJoinBaseSession::IsValidInOuts(const std::vector<CTxIn>& vin, const st
}
// Definitions for static data members
CCriticalSection CCoinJoin::cs_mapdstx;
Mutex CCoinJoin::cs_mapdstx;
std::map<uint256, CCoinJoinBroadcastTx> CCoinJoin::mapDSTX GUARDED_BY(CCoinJoin::cs_mapdstx);
// check to make sure the collateral provided by the client is valid
@ -429,12 +429,14 @@ bilingual_str CCoinJoin::GetMessageByID(PoolMessage nMessageID)
void CCoinJoin::AddDSTX(const CCoinJoinBroadcastTx& dstx)
{
AssertLockNotHeld(cs_mapdstx);
LOCK(cs_mapdstx);
mapDSTX.insert(std::make_pair(dstx.tx->GetHash(), dstx));
}
CCoinJoinBroadcastTx CCoinJoin::GetDSTX(const uint256& hash)
{
AssertLockNotHeld(cs_mapdstx);
LOCK(cs_mapdstx);
auto it = mapDSTX.find(hash);
return (it == mapDSTX.end()) ? CCoinJoinBroadcastTx() : it->second;
@ -442,6 +444,7 @@ CCoinJoinBroadcastTx CCoinJoin::GetDSTX(const uint256& hash)
void CCoinJoin::CheckDSTXes(const CBlockIndex* pindex)
{
AssertLockNotHeld(cs_mapdstx);
LOCK(cs_mapdstx);
auto it = mapDSTX.begin();
while (it != mapDSTX.end()) {
@ -483,12 +486,14 @@ void CCoinJoin::UpdateDSTXConfirmedHeight(const CTransactionRef& tx, int nHeight
void CCoinJoin::TransactionAddedToMempool(const CTransactionRef& tx)
{
AssertLockNotHeld(cs_mapdstx);
LOCK(cs_mapdstx);
UpdateDSTXConfirmedHeight(tx, -1);
}
void CCoinJoin::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex, const std::vector<CTransactionRef>& vtxConflicted)
{
AssertLockNotHeld(cs_mapdstx);
LOCK(cs_mapdstx);
for (const auto& tx : vtxConflicted) {
UpdateDSTXConfirmedHeight(tx, -1);
@ -501,6 +506,7 @@ void CCoinJoin::BlockConnected(const std::shared_ptr<const CBlock>& pblock, cons
void CCoinJoin::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex*)
{
AssertLockNotHeld(cs_mapdstx);
LOCK(cs_mapdstx);
for (const auto& tx : pblock->vtx) {
UpdateDSTXConfirmedHeight(tx, -1);

View File

@ -308,7 +308,7 @@ public:
class CCoinJoinBaseSession
{
protected:
mutable CCriticalSection cs_coinjoin;
mutable Mutex cs_coinjoin;
std::vector<CCoinJoinEntry> vecEntries GUARDED_BY(cs_coinjoin); // Masternode/clients entries
@ -319,7 +319,7 @@ protected:
CMutableTransaction finalMutableTransaction GUARDED_BY(cs_coinjoin); // the finalized transaction ready for signing
void SetNull();
void SetNull() EXCLUSIVE_LOCKS_REQUIRED(cs_coinjoin);
bool IsValidInOuts(const std::vector<CTxIn>& vin, const std::vector<CTxOut>& vout, PoolMessage& nMessageIDRet, bool* fConsumeCollateralRet) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@ -331,26 +331,27 @@ public:
int GetState() const { return nState; }
std::string GetStateString() const;
int GetEntriesCount() const { LOCK(cs_coinjoin); return vecEntries.size(); }
int GetEntriesCount() const LOCKS_EXCLUDED(cs_coinjoin) { LOCK(cs_coinjoin); return vecEntries.size(); }
int GetEntriesCountLocked() const EXCLUSIVE_LOCKS_REQUIRED(cs_coinjoin) { return vecEntries.size(); }
};
// base class
class CCoinJoinBaseManager
{
protected:
mutable CCriticalSection cs_vecqueue;
mutable Mutex cs_vecqueue;
// The current mixing sessions in progress on the network
std::vector<CCoinJoinQueue> vecCoinJoinQueue GUARDED_BY(cs_vecqueue);
void SetNull();
void CheckQueue();
void SetNull() LOCKS_EXCLUDED(cs_vecqueue);
void CheckQueue() LOCKS_EXCLUDED(cs_vecqueue);
public:
CCoinJoinBaseManager() = default;
int GetQueueSize() const { LOCK(cs_vecqueue); return vecCoinJoinQueue.size(); }
bool GetQueueItemAndTry(CCoinJoinQueue& dsqRet);
int GetQueueSize() const LOCKS_EXCLUDED(cs_vecqueue) { LOCK(cs_vecqueue); return vecCoinJoinQueue.size(); }
bool GetQueueItemAndTry(CCoinJoinQueue& dsqRet) LOCKS_EXCLUDED(cs_vecqueue);
};
// helper class
@ -374,9 +375,9 @@ private:
static std::map<uint256, CCoinJoinBroadcastTx> mapDSTX;
static CCriticalSection cs_mapdstx;
static Mutex cs_mapdstx;
static void CheckDSTXes(const CBlockIndex* pindex);
static void CheckDSTXes(const CBlockIndex* pindex) LOCKS_EXCLUDED(cs_mapdstx);
public:
static constexpr std::array<CAmount, 5> GetStandardDenominations() { return vecStandardDenominations; }
@ -476,16 +477,16 @@ public:
}
static void AddDSTX(const CCoinJoinBroadcastTx& dstx);
static CCoinJoinBroadcastTx GetDSTX(const uint256& hash);
static void AddDSTX(const CCoinJoinBroadcastTx& dstx) LOCKS_EXCLUDED(cs_mapdstx);
static CCoinJoinBroadcastTx GetDSTX(const uint256& hash) LOCKS_EXCLUDED(cs_mapdstx);
static void UpdatedBlockTip(const CBlockIndex* pindex);
static void NotifyChainLock(const CBlockIndex* pindex);
static void UpdateDSTXConfirmedHeight(const CTransactionRef& tx, int nHeight);
static void TransactionAddedToMempool(const CTransactionRef& tx);
static void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex, const std::vector<CTransactionRef>& vtxConflicted);
static void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex*);
static void TransactionAddedToMempool(const CTransactionRef& tx) LOCKS_EXCLUDED(cs_mapdstx);
static void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex, const std::vector<CTransactionRef>& vtxConflicted) LOCKS_EXCLUDED(cs_mapdstx);
static void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex*) LOCKS_EXCLUDED(cs_mapdstx);
};

View File

@ -186,6 +186,7 @@ void CCoinJoinServer::ProcessDSVIN(CNode* pfrom, const std::string& msg_type, CD
if (AddEntry(connman, entry, nMessageID)) {
PushStatus(pfrom, STATUS_ACCEPTED, nMessageID, connman);
CheckPool(connman);
LOCK(cs_coinjoin);
RelayStatus(STATUS_ACCEPTED, connman);
} else {
PushStatus(pfrom, STATUS_REJECTED, nMessageID, connman);
@ -206,6 +207,7 @@ void CCoinJoinServer::ProcessDSSIGNFINALTX(CNode* pfrom, const std::string& msg_
nTxInIndex++;
if (!AddScriptSig(txin)) {
LogPrint(BCLog::COINJOIN, "DSSIGNFINALTX -- AddScriptSig() failed at %d/%d, session: %d\n", nTxInIndex, nTxInsCount, nSessionID);
LOCK(cs_coinjoin);
RelayStatus(STATUS_REJECTED, connman);
return;
}
@ -217,6 +219,7 @@ void CCoinJoinServer::ProcessDSSIGNFINALTX(CNode* pfrom, const std::string& msg_
void CCoinJoinServer::SetNull()
{
AssertLockHeld(cs_coinjoin);
// MN side
vecSessionCollaterals.clear();
@ -261,6 +264,7 @@ void CCoinJoinServer::CheckPool(CConnman& connman)
void CCoinJoinServer::CreateFinalTransaction(CConnman& connman)
{
AssertLockNotHeld(cs_coinjoin);
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::CreateFinalTransaction -- FINALIZE TRANSACTIONS\n");
LOCK(cs_coinjoin);
@ -268,7 +272,7 @@ void CCoinJoinServer::CreateFinalTransaction(CConnman& connman)
CMutableTransaction txNew;
// make our new transaction
for (int i = 0; i < GetEntriesCount(); i++) {
for (int i = 0; i < GetEntriesCountLocked(); i++) {
for (const auto& txout : vecEntries[i].vecTxOut) {
txNew.vout.push_back(txout);
}
@ -290,6 +294,7 @@ void CCoinJoinServer::CreateFinalTransaction(CConnman& connman)
void CCoinJoinServer::CommitFinalTransaction(CConnman& connman)
{
AssertLockNotHeld(cs_coinjoin);
if (!fMasternodeMode) return; // check and relay final tx only on masternode
CTransactionRef finalTransaction = WITH_LOCK(cs_coinjoin, return MakeTransactionRef(finalMutableTransaction));
@ -304,7 +309,7 @@ void CCoinJoinServer::CommitFinalTransaction(CConnman& connman)
mempool.PrioritiseTransaction(hashTx, 0.1 * COIN);
if (!lockMain || !AcceptToMemoryPool(mempool, validationState, finalTransaction, nullptr /* pfMissingInputs */, false /* bypass_limits */, DEFAULT_MAX_RAW_TX_FEE /* nAbsurdFee */)) {
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::CommitFinalTransaction -- AcceptToMemoryPool() error: Transaction not valid\n");
SetNull();
WITH_LOCK(cs_coinjoin, SetNull());
// not much we can do in this case, just notify clients
RelayCompletedTransaction(ERR_INVALID_TX, connman);
return;
@ -333,7 +338,7 @@ void CCoinJoinServer::CommitFinalTransaction(CConnman& connman)
// Reset
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::CommitFinalTransaction -- COMPLETED -- RESETTING\n");
SetNull();
WITH_LOCK(cs_coinjoin, SetNull());
}
//
@ -350,6 +355,7 @@ void CCoinJoinServer::CommitFinalTransaction(CConnman& connman)
//
void CCoinJoinServer::ChargeFees(CConnman& connman) const
{
AssertLockNotHeld(cs_coinjoin);
if (!fMasternodeMode) return;
//we don't need to charge collateral for every offence.
@ -465,7 +471,7 @@ void CCoinJoinServer::CheckTimeout(CConnman& connman)
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::CheckTimeout -- %s timed out -- resetting\n",
(nState == POOL_STATE_SIGNING) ? "Signing" : "Session");
ChargeFees(connman);
SetNull();
WITH_LOCK(cs_coinjoin, SetNull());
}
/*
@ -491,6 +497,7 @@ void CCoinJoinServer::CheckForCompleteQueue(CConnman& connman)
// Check to make sure a given input matches an input in the pool and its scriptSig is valid
bool CCoinJoinServer::IsInputScriptSigValid(const CTxIn& txin) const
{
AssertLockHeld(cs_coinjoin);
CMutableTransaction txNew;
txNew.vin.clear();
txNew.vout.clear();
@ -499,7 +506,6 @@ bool CCoinJoinServer::IsInputScriptSigValid(const CTxIn& txin) const
CScript sigPubKey = CScript();
{
LOCK(cs_coinjoin);
int i = 0;
for (const auto &entry: vecEntries) {
for (const auto &txout: entry.vecTxOut) {
@ -538,6 +544,7 @@ bool CCoinJoinServer::IsInputScriptSigValid(const CTxIn& txin) const
//
bool CCoinJoinServer::AddEntry(CConnman& connman, const CCoinJoinEntry& entry, PoolMessage& nMessageIDRet)
{
AssertLockNotHeld(cs_coinjoin);
if (!fMasternodeMode) return false;
if (size_t(GetEntriesCount()) >= vecSessionCollaterals.size()) {
@ -600,6 +607,7 @@ bool CCoinJoinServer::AddEntry(CConnman& connman, const CCoinJoinEntry& entry, P
bool CCoinJoinServer::AddScriptSig(const CTxIn& txinNew)
{
AssertLockNotHeld(cs_coinjoin);
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::AddScriptSig -- scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0, 24));
LOCK(cs_coinjoin);
@ -624,7 +632,7 @@ bool CCoinJoinServer::AddScriptSig(const CTxIn& txinNew)
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::AddScriptSig -- adding to finalMutableTransaction, scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0, 24));
}
}
for (int i = 0; i < GetEntriesCount(); i++) {
for (int i = 0; i < GetEntriesCountLocked(); i++) {
if (vecEntries[i].AddScriptSig(txinNew)) {
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::AddScriptSig -- adding to entries, scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0, 24));
return true;
@ -638,6 +646,7 @@ bool CCoinJoinServer::AddScriptSig(const CTxIn& txinNew)
// Check to make sure everything is signed
bool CCoinJoinServer::IsSignaturesComplete() const
{
AssertLockNotHeld(cs_coinjoin);
LOCK(cs_coinjoin);
return ranges::all_of(vecEntries, [](const auto& entry){
@ -757,11 +766,11 @@ bool CCoinJoinServer::IsSessionReady() const
void CCoinJoinServer::RelayFinalTransaction(const CTransaction& txFinal, CConnman& connman)
{
AssertLockHeld(cs_coinjoin);
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::%s -- nSessionID: %d nSessionDenom: %d (%s)\n",
__func__, nSessionID, nSessionDenom, CCoinJoin::DenominationToString(nSessionDenom));
// final mixing tx with empty signatures should be relayed to mixing participants only
LOCK(cs_coinjoin);
for (const auto& entry : vecEntries) {
bool fOk = connman.ForNode(entry.addr, [&txFinal, &connman, this](CNode* pnode) {
CNetMsgMaker msgMaker(pnode->GetSendVersion());
@ -785,9 +794,9 @@ void CCoinJoinServer::PushStatus(CNode* pnode, PoolStatusUpdate nStatusUpdate, P
void CCoinJoinServer::RelayStatus(PoolStatusUpdate nStatusUpdate, CConnman& connman, PoolMessage nMessageID)
{
AssertLockHeld(cs_coinjoin);
unsigned int nDisconnected{};
// status updates should be relayed to mixing participants only
LOCK(cs_coinjoin);
for (const auto& entry : vecEntries) {
// make sure everyone is still connected
bool fOk = connman.ForNode(entry.addr, [&nStatusUpdate, &nMessageID, &connman, this](CNode* pnode) {
@ -822,6 +831,7 @@ void CCoinJoinServer::RelayStatus(PoolStatusUpdate nStatusUpdate, CConnman& conn
void CCoinJoinServer::RelayCompletedTransaction(PoolMessage nMessageID, CConnman& connman)
{
AssertLockNotHeld(cs_coinjoin);
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::%s -- nSessionID: %d nSessionDenom: %d (%s)\n",
__func__, nSessionID, nSessionDenom, CCoinJoin::DenominationToString(nSessionDenom));

View File

@ -26,12 +26,12 @@ private:
bool fUnitTest;
/// Add a clients entry to the pool
bool AddEntry(CConnman& connman, const CCoinJoinEntry& entry, PoolMessage& nMessageIDRet);
bool AddEntry(CConnman& connman, const CCoinJoinEntry& entry, PoolMessage& nMessageIDRet) LOCKS_EXCLUDED(cs_coinjoin);
/// Add signature to a txin
bool AddScriptSig(const CTxIn& txin);
bool AddScriptSig(const CTxIn& txin) LOCKS_EXCLUDED(cs_coinjoin);
/// Charge fees to bad actors (Charge clients a fee if they're abusive)
void ChargeFees(CConnman& connman) const;
void ChargeFees(CConnman& connman) const LOCKS_EXCLUDED(cs_coinjoin);
/// Rarely charge fees to pay miners
void ChargeRandomFees(CConnman& connman) const;
/// Consume collateral in cases when peer misbehaved
@ -40,36 +40,36 @@ private:
/// Check for process
void CheckPool(CConnman& connman);
void CreateFinalTransaction(CConnman& connman);
void CommitFinalTransaction(CConnman& connman);
void CreateFinalTransaction(CConnman& connman) LOCKS_EXCLUDED(cs_coinjoin);
void CommitFinalTransaction(CConnman& connman) LOCKS_EXCLUDED(cs_coinjoin);
/// Is this nDenom and txCollateral acceptable?
bool IsAcceptableDSA(const CCoinJoinAccept& dsa, PoolMessage& nMessageIDRet) const;
bool CreateNewSession(const CCoinJoinAccept& dsa, PoolMessage& nMessageIDRet, CConnman& connman);
bool CreateNewSession(const CCoinJoinAccept& dsa, PoolMessage& nMessageIDRet, CConnman& connman) LOCKS_EXCLUDED(cs_vecqueue);
bool AddUserToExistingSession(const CCoinJoinAccept& dsa, PoolMessage& nMessageIDRet);
/// Do we have enough users to take entries?
bool IsSessionReady() const;
/// Check that all inputs are signed. (Are all inputs signed?)
bool IsSignaturesComplete() const;
bool IsSignaturesComplete() const LOCKS_EXCLUDED(cs_coinjoin);
/// Check to make sure a given input matches an input in the pool and its scriptSig is valid
bool IsInputScriptSigValid(const CTxIn& txin) const;
bool IsInputScriptSigValid(const CTxIn& txin) const EXCLUSIVE_LOCKS_REQUIRED(cs_coinjoin);
// Set the 'state' value, with some logging and capturing when the state changed
void SetState(PoolState nStateNew);
/// Relay mixing Messages
void RelayFinalTransaction(const CTransaction& txFinal, CConnman& connman);
void RelayFinalTransaction(const CTransaction& txFinal, CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(cs_coinjoin);
void PushStatus(CNode* pnode, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID, CConnman& connman) const;
void RelayStatus(PoolStatusUpdate nStatusUpdate, CConnman& connman, PoolMessage nMessageID = MSG_NOERR);
void RelayCompletedTransaction(PoolMessage nMessageID, CConnman& connman);
void RelayStatus(PoolStatusUpdate nStatusUpdate, CConnman& connman, PoolMessage nMessageID = MSG_NOERR) EXCLUSIVE_LOCKS_REQUIRED(cs_coinjoin);
void RelayCompletedTransaction(PoolMessage nMessageID, CConnman& connman) LOCKS_EXCLUDED(cs_coinjoin);
void ProcessDSACCEPT(CNode* pfrom, const std::string& msg_type, CDataStream& vRecv, CConnman& connman, bool enable_bip61);
void ProcessDSQUEUE(CNode* pfrom, const std::string& msg_type, CDataStream& vRecv, CConnman& connman, bool enable_bip61);
void ProcessDSVIN(CNode* pfrom, const std::string& msg_type, CDataStream& vRecv, CConnman& connman, bool enable_bip61);
void ProcessDSSIGNFINALTX(CNode* pfrom, const std::string& msg_type, CDataStream& vRecv, CConnman& connman, bool enable_bip61);
void ProcessDSACCEPT(CNode* pfrom, const std::string& msg_type, CDataStream& vRecv, CConnman& connman, bool enable_bip61) LOCKS_EXCLUDED(cs_vecqueue);
void ProcessDSQUEUE(CNode* pfrom, const std::string& msg_type, CDataStream& vRecv, CConnman& connman, bool enable_bip61) LOCKS_EXCLUDED(cs_vecqueue);
void ProcessDSVIN(CNode* pfrom, const std::string& msg_type, CDataStream& vRecv, CConnman& connman, bool enable_bip61) LOCKS_EXCLUDED(cs_coinjoin);
void ProcessDSSIGNFINALTX(CNode* pfrom, const std::string& msg_type, CDataStream& vRecv, CConnman& connman, bool enable_bip61) LOCKS_EXCLUDED(cs_coinjoin);
void SetNull();
void SetNull() EXCLUSIVE_LOCKS_REQUIRED(cs_coinjoin);
public:
CCoinJoinServer() :

View File

@ -29,13 +29,13 @@ public:
class CKeyHolderStorage
{
private:
mutable CCriticalSection cs_storage;
mutable Mutex cs_storage;
std::vector<std::unique_ptr<CKeyHolder> > storage GUARDED_BY(cs_storage);
public:
CScript AddKey(CWallet* pwalletIn);
void KeepAll();
void ReturnAll();
CScript AddKey(CWallet* pwalletIn) LOCKS_EXCLUDED(cs_storage);
void KeepAll() LOCKS_EXCLUDED(cs_storage);
void ReturnAll() LOCKS_EXCLUDED(cs_storage);
};
/**
@ -93,7 +93,7 @@ class CTransactionBuilder
/// Call KeepKey for all keys in destructor if fKeepKeys is true, call ReturnKey for all key if its false.
bool fKeepKeys{false};
/// Protect vecOutputs
mutable CCriticalSection cs_outputs;
mutable Mutex cs_outputs;
/// Contains all outputs already added to the transaction
std::vector<std::unique_ptr<CTransactionBuilderOutput>> vecOutputs GUARDED_BY(cs_outputs);
/// Needed by CTransactionBuilderOutput::UpdateAmount to lock cs_outputs
@ -107,7 +107,7 @@ public:
/// Check if its possible to add multiple outputs as vector of amounts. Returns true if its possible to add all of them and false if not.
bool CouldAddOutputs(const std::vector<CAmount>& vecOutputAmounts) const;
/// Add an output with the amount nAmount. Returns a pointer to the output if it could be added and nullptr if not due to insufficient amount left.
CTransactionBuilderOutput* AddOutput(CAmount nAmountOutput = 0);
CTransactionBuilderOutput* AddOutput(CAmount nAmountOutput = 0) LOCKS_EXCLUDED(cs_outputs);
/// Get amount we had available when we started
CAmount GetAmountInitial() const { return tallyItem.nAmount; }
/// Get the amount currently left to add more outputs. Does respect fees.
@ -117,25 +117,25 @@ public:
/// Get the total number of added outputs
int CountOutputs() const { LOCK(cs_outputs); return vecOutputs.size(); }
/// Create and Commit the transaction to the wallet
bool Commit(bilingual_str& strResult);
bool Commit(bilingual_str& strResult) LOCKS_EXCLUDED(cs_outputs);
/// Convert to a string
std::string ToString() const;
private:
/// Clear the output vector and keep/return the included keys depending on the value of fKeepKeys
void Clear();
void Clear() LOCKS_EXCLUDED(cs_outputs);
/// Get the total number of bytes used already by this transaction
unsigned int GetBytesTotal() const;
unsigned int GetBytesTotal() const LOCKS_EXCLUDED(cs_outputs);
/// Helper to calculate static amount left by simply subtracting an used amount and a fee from a provided initial amount.
static CAmount GetAmountLeft(CAmount nAmountInitial, CAmount nAmountUsed, CAmount nFee);
/// Get the amount currently used by added outputs. Does not include fees.
CAmount GetAmountUsed() const;
CAmount GetAmountUsed() const LOCKS_EXCLUDED(cs_outputs);
/// Get fees based on the number of bytes and the feerate set in CoinControl.
/// NOTE: To get the total transaction fee this should only be called once with the total number of bytes for the transaction to avoid
/// calling CFeeRate::GetFee multiple times with subtotals as this may add rounding errors with each further call.
CAmount GetFee(unsigned int nBytes) const;
/// Helper to get GetSizeOfCompactSizeDiff(vecOutputs.size(), vecOutputs.size() + nAdd)
int GetSizeOfCompactSizeDiff(size_t nAdd) const;
int GetSizeOfCompactSizeDiff(size_t nAdd) const LOCKS_EXCLUDED(cs_outputs);
};
#endif // BITCOIN_COINJOIN_UTIL_H