Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: subsume CoinJoin objects under CJContext, deglobalize coinJoin{ClientQueueManager,Server} #5337

Merged
merged 13 commits into from
Sep 13, 2023

Conversation

kwvg
Copy link
Collaborator

@kwvg kwvg commented Apr 18, 2023

Motivation

CoinJoin's subsystems are initialized by variables and managers that occupy the global context. The extent to which these subsystems entrench themselves into the codebase is difficult to assess and moving them out of the global context forces us to enumerate the subsystems in the codebase that rely on CoinJoin logic and enumerate the order in which components are initialized and destroyed.

Keeping this in mind, the scope of this pull request aims to:

  • Reduce the amount of CoinJoin-specific entities present in the global scope
  • Make the remaining usage of these entities in the global scope explicit and easily searchable

Additional Information

  • The initialization of CCoinJoinClientQueueManager is dependent on blocks-only mode being disabled (which can be alternatively interpreted as enabling the relay of transactions). The same applies to CBlockPolicyEstimator, which CCoinJoinClientQueueManager depends.

    Therefore, CCoinJoinClientQueueManager is only initialized if transaction relaying is enabled and so is its scheduled maintenance task. This can be found by looking at init.cpp here, here and here.

    For this reason, CBlockPolicyEstimator is not a member of CJContext and its usage is fulfilled by passing it as a reference when initializing the scheduling task.

  • CJClientManager has not used CConnman or CTxMemPool as const as existing code that is outside the scope of this PR would cast away constness, which would be unacceptable. Furthermore, some logical paths are taken that will grind to a halt if they are stored as const.

    Examples of such a call chains would be:

    • CJClientManager::DoMaintenance > CCoinJoinClientManager::DoMaintenance > DoAutomaticDenominating > CCoinJoinClientSession::DoAutomaticDenominating > CCoinJoinClientSession::StartNewQueue > CConnman::AddPendingMasternode which modifies CConnman::vPendingMasternodes, which is non-const behaviour

    • CJClientManager::DoMaintenance > CCoinJoinClientManager::DoMaintenance > DoAutomaticDenominating > CCoinJoin::IsCollateralValid > AcceptToMemoryPool which adds a transaction to the memory pool, which is non-const behaviour

  • There were cppcheck linter failures that seemed to be caused by the usage of Assert in coinjoin/client.h. This seems to be resolved by backporting bitcoin#24714. (Thanks @knst!)

@kwvg kwvg force-pushed the coinjoin_ctx branch 7 times, most recently from 1c0f04d to 0d673b0 Compare April 20, 2023 15:22
@kwvg kwvg force-pushed the coinjoin_ctx branch 4 times, most recently from 503777d to 26d1bb3 Compare May 11, 2023 08:34
@github-actions
Copy link

This pull request has conflicts, please rebase.

@github-actions
Copy link

github-actions bot commented Jun 4, 2023

This pull request has conflicts, please rebase.

@kwvg kwvg force-pushed the coinjoin_ctx branch 2 times, most recently from f8e495d to 8c86a5e Compare July 31, 2023 16:53
@kwvg kwvg marked this pull request as ready for review July 31, 2023 19:40
@kwvg kwvg marked this pull request as draft July 31, 2023 19:40
@kwvg kwvg requested a review from UdjinM6 July 31, 2023 19:40
@kwvg kwvg changed the title refactor: cleanup coinJoinClientManagers global invocations, wrap in struct refactor: subsume CoinJoin objects under CJContext, deglobalize coinJoin{ClientQueueManager,Server} Aug 3, 2023
@kwvg kwvg marked this pull request as ready for review August 3, 2023 16:13
@kwvg kwvg requested review from knst and PastaPastaPasta August 3, 2023 16:15
@github-actions
Copy link

This pull request has conflicts, please rebase.

@PastaPastaPasta
Copy link
Member

Please take a look at this patch

Subject: [PATCH] patch
---
Index: src/coinjoin/client.cpp
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/coinjoin/client.cpp b/src/coinjoin/client.cpp
--- a/src/coinjoin/client.cpp	(revision 15722d75cff7105725d2ce54d8f21eac14e26797)
+++ b/src/coinjoin/client.cpp	(date 1694453117373)
@@ -1050,14 +1050,13 @@
 bool CCoinJoinClientSession::JoinExistingQueue(CAmount nBalanceNeedsAnonymized, CConnman& connman)
 {
     if (!CCoinJoinClientOptions::IsEnabled()) return false;
-    if (m_queueman == nullptr) return false;
 
     const auto mnList = deterministicMNManager->GetListAtChainTip();
     const int nWeightedMnCount = mnList.GetValidWeightedMNsCount();
 
     // Look through the queues and see if anything matches
     CCoinJoinQueue dsq;
-    while (m_queueman->GetQueueItemAndTry(dsq)) {
+    while (m_queueman.GetQueueItemAndTry(dsq)) {
         auto dmn = mnList.GetValidMNByCollateral(dsq.masternodeOutpoint);
 
         if (!dmn) {
@@ -1880,14 +1879,14 @@
 }
 
 void CJClientManager::Add(CWallet& wallet) {
-    m_wallet_manager_map.emplace(
+    m_wallet_manager_map.try_emplace(
         wallet.GetName(),
         std::make_unique<CCoinJoinClientManager>(wallet, *this, m_mn_sync, m_queueman)
     );
 }
 
 void CJClientManager::DoMaintenance(CBlockPolicyEstimator& fee_estimator) {
-    for (auto& pair : m_wallet_manager_map) {
-        pair.second->DoMaintenance(m_connman, fee_estimator, m_mempool);
+    for (auto& [_, cj_man] : m_wallet_manager_map) {
+        cj_man->DoMaintenance(m_connman, fee_estimator, m_mempool);
     }
 }
Index: src/coinjoin/context.cpp
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/coinjoin/context.cpp b/src/coinjoin/context.cpp
--- a/src/coinjoin/context.cpp	(revision 15722d75cff7105725d2ce54d8f21eac14e26797)
+++ b/src/coinjoin/context.cpp	(date 1694444524652)
@@ -18,7 +18,7 @@
     clientman {
         [&]() -> CJClientManager* const {
             assert(::coinJoinClientManagers == nullptr);
-            ::coinJoinClientManagers = std::make_unique<CJClientManager>(connman, mempool, mn_sync, queueman);
+            ::coinJoinClientManagers = std::make_unique<CJClientManager>(connman, mempool, mn_sync, *queueman);
             return ::coinJoinClientManagers.get();
         }()
     },
Index: src/coinjoin/client.h
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/coinjoin/client.h b/src/coinjoin/client.h
--- a/src/coinjoin/client.h	(revision 15722d75cff7105725d2ce54d8f21eac14e26797)
+++ b/src/coinjoin/client.h	(date 1694453552985)
@@ -73,11 +73,11 @@
 class CJClientManager {
 public:
     CJClientManager(CConnman& connman, CTxMemPool& mempool, const CMasternodeSync& mn_sync,
-                    const std::unique_ptr<CCoinJoinClientQueueManager>& queueman)
+                    CCoinJoinClientQueueManager& queueman)
         : m_connman(connman), m_mempool(mempool), m_mn_sync(mn_sync), m_queueman(queueman) {}
     ~CJClientManager() {
-        for (auto& pair : m_wallet_manager_map) {
-            pair.second.reset();
+        for (auto& [wallet_name, cj_man] : m_wallet_manager_map) {
+            cj_man.reset();
         }
     }
 
@@ -85,8 +85,7 @@
     void DoMaintenance(CBlockPolicyEstimator& fee_estimator);
 
     void Remove(const std::string& name) {
-        auto it = m_wallet_manager_map.find(name);
-        if (it != m_wallet_manager_map.end()) { m_wallet_manager_map.erase(it); }
+        m_wallet_manager_map.erase(name);
     }
 
     CCoinJoinClientManager* Get(const CWallet& wallet) const {
@@ -94,27 +93,27 @@
         return (it != m_wallet_manager_map.end()) ? it->second.get() : nullptr;
     }
 
-    const std::map<const std::string, std::unique_ptr<CCoinJoinClientManager>>& raw() const { return m_wallet_manager_map; }
+    using wallet_name_cjman_map = std::map<const std::string, std::unique_ptr<CCoinJoinClientManager>>;
+    const wallet_name_cjman_map& raw() const { return m_wallet_manager_map; }
 
 private:
     CConnman& m_connman;
     CTxMemPool& m_mempool;
 
     const CMasternodeSync& m_mn_sync;
-    const std::unique_ptr<CCoinJoinClientQueueManager>& m_queueman;
+    CCoinJoinClientQueueManager& m_queueman;
 
-    std::map<const std::string, std::unique_ptr<CCoinJoinClientManager>> m_wallet_manager_map;
+    wallet_name_cjman_map m_wallet_manager_map;
 };
 
 class CCoinJoinClientSession : public CCoinJoinBaseSession
 {
 private:
     CWallet& m_wallet;
-    CJClientManager& m_clientman;
     CCoinJoinClientManager& m_manager;
 
     const CMasternodeSync& m_mn_sync;
-    const std::unique_ptr<CCoinJoinClientQueueManager>& m_queueman;
+    CCoinJoinClientQueueManager& m_queueman;
 
     std::vector<COutPoint> vecOutPointLocked;
 
@@ -163,8 +162,8 @@
 
 public:
     explicit CCoinJoinClientSession(CWallet& pwallet, CJClientManager& clientman, const CMasternodeSync& mn_sync,
-                                    const std::unique_ptr<CCoinJoinClientQueueManager>& queueman) :
-        m_wallet(pwallet), m_clientman(clientman), m_manager(*Assert(clientman.Get(pwallet))), m_mn_sync(mn_sync), m_queueman(queueman) {}
+                                    CCoinJoinClientQueueManager& queueman) :
+        m_wallet(pwallet), m_manager(*Assert(clientman.Get(pwallet))), m_mn_sync(mn_sync), m_queueman(queueman) {}
 
     void ProcessMessage(CNode& peer, PeerManager& peerman, CConnman& connman, const CTxMemPool& mempool, std::string_view msg_type, CDataStream& vRecv);
 
@@ -217,7 +216,7 @@
     CJClientManager& m_clientman;
 
     const CMasternodeSync& m_mn_sync;
-    const std::unique_ptr<CCoinJoinClientQueueManager>& m_queueman;
+    CCoinJoinClientQueueManager& m_queueman;
 
     // Keep track of the used Masternodes
     std::vector<COutPoint> vecMasternodesUsed;
@@ -249,7 +248,7 @@
     CCoinJoinClientManager& operator=(CCoinJoinClientManager const&) = delete;
 
     explicit CCoinJoinClientManager(CWallet& wallet, CJClientManager& clientman, const CMasternodeSync& mn_sync,
-                                    const std::unique_ptr<CCoinJoinClientQueueManager>& queueman) :
+                                    CCoinJoinClientQueueManager& queueman) :
         m_wallet(wallet), m_clientman(clientman), m_mn_sync(mn_sync), m_queueman(queueman) {}
 
     void ProcessMessage(CNode& peer, PeerManager& peerman, CConnman& connman, const CTxMemPool& mempool, std::string_view msg_type, CDataStream& vRecv) LOCKS_EXCLUDED(cs_deqsessions);

@kwvg kwvg requested review from PastaPastaPasta and removed request for PastaPastaPasta September 11, 2023 20:02
@kwvg
Copy link
Collaborator Author

kwvg commented Sep 12, 2023

Dropped 954bbcf due to test failures (pipeline)

Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com>
@knst
Copy link
Collaborator

knst commented Sep 12, 2023

Dropped 954bbcf due to test failures (pipeline)

   clientman {
        [&]() -> CJClientManager* const {
            assert(::coinJoinClientManagers == nullptr);
            ::coinJoinClientManagers = std::make_unique<CJClientManager>(connman, mempool, mn_sync, *queueman);
            return ::coinJoinClientManagers.get();
        }()
    },
queueman {relay_txes ? std::make_unique<CCoinJoinClientQueueManager>(connman, *clientman, mn_sync) : nullptr},

queueman requires clientman for init; clientman requires queueman for init. unique_ptr is required at the moment for an implementation indeed, 954bbcf can't be applied as it is implemented now.

@kwvg
Copy link
Collaborator Author

kwvg commented Sep 12, 2023

queueman requires clientman for init; clientman requires queueman for init. unique_ptr is required at the moment for an implementation indeed

This was initially why I used unique_ptr, The patch seemed to indicate that perhaps in between the time I started working on this PR and the time it's being reviewed at, circumstances have changed (or that I had missed something). Tests passed successfully on my local machine so it was added in.

CI has shown that the patch caused problems (the exact same I found early on when working on this PR) and so I've reversed it.

Copy link
Member

@PastaPastaPasta PastaPastaPasta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK for squash merge

@knst
Copy link
Collaborator

knst commented Sep 12, 2023

Dropped 954bbcf due to test failures (pipeline)

btw, check this one: knst@be2494f

@kwvg
Copy link
Collaborator Author

kwvg commented Sep 12, 2023

btw, check this one: knst@be2494f

It is a clever fix, though, this mildly obscures the circular dependency. Part of the deglobalization effort is to make the web of dependencies more self-documenting.

I'm a bit uncertain about adding this, at least as part of this PR but would be fine if it was applied consistently for circular dependencies and then documented explicitly. Would be easier to document the origin of this practice.

Copy link
Collaborator

@knst knst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO reference is [almost] always better than pointer because you never need to think can this object be nullptr? Should I check for nullptr?. There's still question who own it? but it is common between pointer and reference. if you want to show circular dependency clear as possible, may be need to add extra comment here?

btw, I am ok with knst@be2494f or whevener without it to approve PR because this PR looks as complete and whole anyway and my commit is just one more minor improvement that can be part of this or any next PR of coinjoin's refactorings.

Copy link

@UdjinM6 UdjinM6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK

@PastaPastaPasta PastaPastaPasta merged commit 41a6613 into dashpay:develop Sep 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants