Skip to content

fix: dangling point to cj client#7259

Open
knst wants to merge 13 commits intodashpay:developfrom
knst:fix-cj-dangling-ptr
Open

fix: dangling point to cj client#7259
knst wants to merge 13 commits intodashpay:developfrom
knst:fix-cj-dangling-ptr

Conversation

@knst
Copy link
Copy Markdown
Collaborator

@knst knst commented Mar 29, 2026

Depends on #7248

Issue being fixed or feature implemented

Hold lock during flush operations to prevent TOCTOU race condition.

getClient() releases cs_wallet_manager_map before returning the raw pointer. If another thread calls removeWallet() concurrently, the unique_ptr is destroyed while flushWallet() still holds a reference, leading to use-after-free. This is inconsistent with removeWallet() which safely erases under the lock.

Discovered here #7248 (comment)

What was done?

Use doWithClient(name, [](){...}) instead of getClient(name)->...

How Has This Been Tested?

Run unit & functional tests

Breaking Changes

N/A

Checklist:

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

knst added 12 commits March 27, 2026 16:34
…tManagerImpl

CJWalletManagerImpl is private implementation of CJWalletManager
It helps to hide internal details of CJWalletManagerImpl which used public CCoinJoinClientManager
It reduce amount of abstractions and complexity of implementation.
…Impl

It reduce amount of abstractions and levels of inheritance
While CJWalletManagerImpl is already private
    /usr/bin/ld: libbitcoin_node.a(libbitcoin_node_a-walletman.o): in function `CJWalletManagerImpl::UpdatedBlockTip(CBlockIndex const*, CBlockIndex const*, bool)::{lambda(CCoinJoinClientManager&)#1}::operator()(CCoinJoinClientManager&) const':
    DASH/src/coinjoin/walletman.cpp:132:(.text+0x51c): undefined reference to `CCoinJoinClientManager::UpdatedBlockTip(CBlockIndex const*)'
@knst knst added this to the 24 milestone Mar 29, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 29, 2026

⚠️ Potential Merge Conflicts Detected

This PR has potential conflicts with the following open PRs:

Please coordinate with the authors of these PRs to avoid merge conflicts.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: acb27a0efa

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +145 to +148
LOCK(cs_wallet_manager_map);
auto it = m_wallet_manager_map.find(name);
if (it == m_wallet_manager_map.end()) return false;
func(*it->second);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Do not invoke client callbacks while holding manager mutex

doForClient() now keeps cs_wallet_manager_map locked while running arbitrary callbacks, and several new callbacks call client.resetPool() (for example coinjoin reset in src/rpc/coinjoin.cpp and flushWallet() in this file), which takes wallet locks via CCoinJoinClientSession::ResetPool(). At the same time, wallet code can take the opposite order (cs_wallet then CoinJoin manager lock) through CWallet::NewKeyPoolCallback() calling WithClient() in src/wallet/wallet.cpp, creating a lock-order inversion that can deadlock under concurrent RPC/UI keypool operations and CoinJoin reset/flush paths.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

not, the whole point of PR is doing it under cs_wallet_manager_map mutex, otherwhise wallet maybe destroyed

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 29, 2026

Walkthrough

This PR reorganizes CoinJoin queue and client management: it replaces CCoinJoinBaseManager/CCoinJoinClientQueueManager with a standalone CoinJoinQueueManager, moves queue ownership into server/wallet managers, and shifts client lookup from pointer-returning GetClient() to callback-based WithClient()/doForClient() across UI, RPC, and wallet code. CCoinJoinClientManager public methods are renamed to lowerCamelCase and now implement interfaces::CoinJoin::Client. Wallet manager storage moves to a map of client managers with a separate (nullable) CoinJoinQueueManager. Build/test adjustments include moving walletman.cpp between libraries, removing a test, and adding new queue tests.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant Loader as interfaces::CoinJoin::Loader
    participant WalletMan as CJWalletManagerImpl
    participant Client as CCoinJoinClientManager
    participant QueueMan as CoinJoinQueueManager

    Note over Caller,Client: NEW callback-based flow for client ops
    Caller->>Loader: WithClient(wallet_name, callback)
    Loader->>WalletMan: doForClient(wallet_name, callback)
    WalletMan->>Client: (invoke callback with Client&) 
    Client->>Client: isMixing()/getJsonInfo()/startMixing()/stopMixing()
    WalletMan-->>Loader: returns bool
    Loader-->>Caller: returns bool

    Note over QueueMan,Server: Queue operations delegated to QueueMan
    Caller->>Server: ProcessDSQUEUE / ProcessDSACCEPT
    Server->>QueueMan: TryCheckDuplicate(dsq) / TryHasQueueFromMasternode(outpoint)
    alt enqueue accepted
        Server->>QueueMan: TryAddQueue(dsq) or AddQueue(dsq)
    end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested reviewers

  • UdjinM6
  • PastaPastaPasta
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.62% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: dangling point to cj client' directly addresses the main change: replacing unsafe raw-pointer access (getClient()) with a callback-based approach (doWithClient()) to fix a use-after-free race condition.
Description check ✅ Passed The description clearly explains the TOCTOU race condition (getClient() releasing lock before pointer use), the specific problem scenario (concurrent removeWallet()), the solution (doWithClient callback), and notes testing was performed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Makefile.am (1)

656-660: ⚠️ Potential issue | 🔴 Critical

The library split still leaves a no-wallet link hole.

The new #ifdef ENABLE_WALLET in src/init.cpp removes the direct CJWalletManager::make() call, but src/net_processing.cpp is still built into libbitcoin_node.a and still uses CJWalletManager through PeerManager plus the hasQueue() / processMessage() call sites. With coinjoin/walletman.cpp moved only into libbitcoin_wallet.a, a --disable-wallet build no longer has an implementation owner for that surface. Either keep coinjoin/walletman.cpp in libbitcoin_node.a for now, or finish moving the remaining CJWalletManager references behind wallet-only code first.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Makefile.am` around lines 656 - 660, The build splits wallet sources into
libbitcoin_wallet.a but leaves references to CJWalletManager in code still built
into libbitcoin_node.a (notably net_processing.cpp via PeerManager and the
hasQueue()/processMessage() call sites), causing unresolved links when
--disable-wallet is used; either move coinjoin/walletman.cpp back into the node
archive or gate all usages of CJWalletManager (and any calls to
PeerManager::hasQueue()/processMessage() that forward to it) behind the same
ENABLE_WALLET conditional as used in init.cpp so that net_processing.cpp no
longer references CJWalletManager when wallets are disabled; update the
Makefile.am entry for libbitcoin_node_a_SOURCES or wrap the
CJWalletManager-using code paths in net_processing.cpp/PeerManager with `#ifdef`
ENABLE_WALLET to restore a consistent implementation owner.
🧹 Nitpick comments (7)
src/coinjoin/walletman.h (1)

51-53: Document the callback as non-reentrant.

func runs while the wallet-manager lock is held. A short note that callbacks must not call back into CJWalletManager would make that deadlock boundary explicit for future call sites.

📝 Suggested doc tweak
-    //! Execute func under the wallet manager lock for the client identified by name.
+    //! Execute func under the wallet manager lock for the client identified by name.
+    //! The callback must not re-enter CJWalletManager methods.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/coinjoin/walletman.h` around lines 51 - 53, The comment for doForClient
should explicitly document that the callback func is non-reentrant: add a short
sentence to the function comment (for doForClient) stating that func is invoked
while the wallet-manager lock is held and must not call back into
CJWalletManager or invoke any methods that may acquire the same wallet-manager
lock (to avoid deadlocks); reference CCoinJoinClientManager as the callback
parameter type and make the non-reentrancy constraint explicit so future callers
know not to call CJWalletManager methods from within func.
src/qt/overviewpage.cpp (2)

599-607: Potential incorrect state if client unavailable.

When withCoinJoin() fails to find the client, notMixing remains false (its initialization), which means the code path after line 607 treats this as if mixing is active. This could display incorrect UI state.

Consider initializing notMixing to true as a safer default:

♻️ Suggested safer default
-    bool notMixing{false};
+    bool notMixing{true};  // Default to not mixing if client unavailable
     walletModel->withCoinJoin([&](auto& client) {
         notMixing = !client.isMixing();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/qt/overviewpage.cpp` around lines 599 - 607, The current logic leaves
notMixing=false when withCoinJoin() can't find a client, causing the UI to
incorrectly show "mixing" active; change the default to true (bool
notMixing{true};) so absence of a client is treated as not mixing, and keep the
existing update inside walletModel->withCoinJoin(...) that sets notMixing =
!client.isMixing(), while preserving the cached-blocks check
(client.getCachedBlocks(), client.setCachedBlocks(...),
updateCoinJoinProgress()) so state stays correct when a client is present.

581-585: Unchecked return value could mask client availability issues.

The withCoinJoin() return value is ignored. If the client is not found, tooFast remains false and the UI update proceeds. While this is likely benign (the UI updates when it shouldn't skip), consider whether silent failure is the desired behavior across all these call sites, or if a missing client should be logged/handled differently.

This pattern is repeated throughout this file (lines 599-607, 665-666, 690-697, 714-715, 734, 745-755, 794).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/qt/overviewpage.cpp` around lines 581 - 585, withCoinJoin's boolean
return value is ignored so a missing client can be silently skipped; change the
call sites (e.g. the block using walletModel->withCoinJoin, the tooFast
assignment that references client.getCachedBlocks()) to capture the return value
and handle the false case explicitly: if withCoinJoin(...) returns false, log a
warning (or otherwise handle client-unavailable) and avoid treating tooFast as
valid, otherwise run the lambda to set tooFast using nBestHeight,
client.getCachedBlocks(), GetTime() and nLastDSProgressBlockTime; apply the same
pattern to all repeated sites in this file.
src/coinjoin/server.cpp (2)

193-193: Inconsistent synchronization strategy: TryAddQueue vs AddQueue.

ProcessDSQUEUE uses the non-blocking TryAddQueue() which silently fails if the lock is contended, while CreateNewSession (line 728) and CheckForCompleteQueue (line 520) use the blocking AddQueue().

This inconsistency means:

  • Incoming DSQ messages from peers may be silently dropped under load
  • Server-initiated queues (session creation) always succeed

This appears intentional (peer messages are less critical to drop vs server-initiated actions), but the behavioral difference should be documented.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/coinjoin/server.cpp` at line 193, ProcessDSQUEUE currently uses the
non-blocking m_queueman.TryAddQueue(dsq) while CreateNewSession and
CheckForCompleteQueue use the blocking AddQueue(), causing peer DSQ messages to
be silently dropped under contention; update the codebase to either (a) unify
behavior by replacing TryAddQueue with AddQueue in ProcessDSQUEUE or (b)
explicitly document the intended difference—add a clear comment at the
ProcessDSQUEUE call site referencing TryAddQueue, explaining that peer DSQ drops
are intentional and why, and update any developer docs; ensure you reference the
methods TryAddQueue, AddQueue and the functions ProcessDSQUEUE,
CreateNewSession, CheckForCompleteQueue so reviewers can locate and verify the
change.

90-97: Silent return on lock contention may drop legitimate requests.

When TryHasQueueFromMasternode() returns std::nullopt due to lock contention, the DSACCEPT message is silently dropped without any response to the peer. This differs from the previous blocking behavior where the message would always be processed.

While this prevents potential deadlocks, peers may experience intermittent failures to join mixing sessions under load. Consider logging when this occurs for debugging purposes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/coinjoin/server.cpp` around lines 90 - 97, When TryHasQueueFromMasternode
(called on m_queueman with m_mn_activeman.GetOutPoint()) returns std::nullopt in
the DSACCEPT handling, do not silently return: add a LogPrint(BCLog::COINJOIN,
...) that records the lock-contention case including the masternode outpoint and
peer info for debugging, and reply to the peer so the request is not dropped
(e.g. call PushStatus(peer, STATUS_REJECTED, ERR_BUSY) or another appropriate
temporary-failure code) so callers get a clear transient-failure response
instead of no response.
src/coinjoin/coinjoin.h (1)

352-376: Fix formatting to pass CI pipeline.

The pipeline reports clang-format differences on lines 360-364. The [[nodiscard]] attribute declarations for TryHasQueueFromMasternode and TryCheckDuplicate need formatting adjustments.

🔧 Suggested formatting fix
-    //! TRY_LOCK variant: returns nullopt if lock can't be acquired; true if any queue entry has this
-    //! outpoint (any readiness).
-    [[nodiscard]] std::optional<bool> TryHasQueueFromMasternode(const COutPoint& outpoint) const EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue);
-    //! TRY_LOCK combined duplicate check: returns nullopt if lock can't be acquired; true if dsq is
-    //! an exact duplicate or the masternode is sending too many dsqs with the same readiness.
-    [[nodiscard]] std::optional<bool> TryCheckDuplicate(const CCoinJoinQueue& dsq) const EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue);
+    //! TRY_LOCK variant: returns nullopt if lock can't be acquired; true if any queue entry has this
+    //! outpoint (any readiness).
+    [[nodiscard]] std::optional<bool> TryHasQueueFromMasternode(const COutPoint& outpoint) const
+        EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue);
+    //! TRY_LOCK combined duplicate check: returns nullopt if lock can't be acquired; true if dsq is
+    //! an exact duplicate or the masternode is sending too many dsqs with the same readiness.
+    [[nodiscard]] std::optional<bool> TryCheckDuplicate(const CCoinJoinQueue& dsq) const
+        EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/coinjoin/coinjoin.h` around lines 352 - 376, Reformat the two function
declarations so clang-format is happy: put the [[nodiscard]] attribute directly
adjacent to the function declaration rather than on its own line and ensure
spacing matches the surrounding declarations; specifically update the
declarations for TryHasQueueFromMasternode and TryCheckDuplicate so the
[[nodiscard]] attribute precedes the function signature on the same line and
keep the EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue) annotation and ending semicolon
intact.
src/coinjoin/client.cpp (1)

140-152: Fix formatting to pass CI pipeline.

The pipeline reports clang-format differences on lines 137-155. The method signatures for startMixing, stopMixing, and isMixing need formatting adjustments.

🔧 Suggested formatting fix
-bool CCoinJoinClientManager::startMixing() {
+bool CCoinJoinClientManager::startMixing()
+{
     bool expected{false};
     return fMixing.compare_exchange_strong(expected, true);
 }

-void CCoinJoinClientManager::stopMixing() {
+void CCoinJoinClientManager::stopMixing()
+{
     fMixing = false;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/coinjoin/client.cpp` around lines 140 - 152, Reformat the three methods
to match project clang-format style: place the opening brace on the same line as
the function signature, use consistent spacing for initializers (e.g., bool
expected = false or bool expected{false} per project style), and ensure
return/statements and braces follow the same indentation and spacing
conventions; update CCoinJoinClientManager::startMixing,
CCoinJoinClientManager::stopMixing, and CCoinJoinClientManager::isMixing
accordingly so clang-format no longer reports differences.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/coinjoin/server.cpp`:
- Line 904: Clang-format flagged inconsistent spacing on the obj.pushKV line;
reformat the line that calls obj.pushKV("queue_size", m_queueman.GetQueueSize())
so spacing and alignment match project clang-format rules (run clang-format or
your editor's format command), ensuring the call to obj.pushKV and the
m_queueman.GetQueueSize() argument use the same spacing style as surrounding
pushKV calls.
- Around line 159-165: Run clang-format on the affected file and fix the
formatting of the LogPrint line in the block where you call
m_queueman.TryCheckDuplicate(dsq); specifically reflow/wrap the long LogPrint
invocation (the line referencing BCLog::COINJOIN, the format string "DSQUEUE --
Peer %d is sending WAY too many dsq messages for a masternode with collateral
%s\n", and the arguments from and dsq.masternodeOutpoint.ToStringShort()) so it
matches the project's clang-format style (proper indentation and line breaks)
and then commit the formatted change.

In `@src/rpc/coinjoin.cpp`:
- Around line 281-286: The coinjoin salt generation path (coinjoinsalt_generate)
currently calls node.coinjoin_loader->WithClient(...) and silently proceeds if
WithClient returns false, skipping the mixing check; update the call to assert
the client lookup succeeded using CHECK_NONFATAL so the RPC fails early when the
client isn't found (i.e., wrap the WithClient invocation result in
CHECK_NONFATAL and preserve the existing mixing check that throws JSONRPCError
if client.isMixing()), referencing coinjoin_loader->WithClient,
coinjoinsalt_generate, CHECK_NONFATAL, and client.isMixing to locate and fix the
code.

---

Outside diff comments:
In `@src/Makefile.am`:
- Around line 656-660: The build splits wallet sources into libbitcoin_wallet.a
but leaves references to CJWalletManager in code still built into
libbitcoin_node.a (notably net_processing.cpp via PeerManager and the
hasQueue()/processMessage() call sites), causing unresolved links when
--disable-wallet is used; either move coinjoin/walletman.cpp back into the node
archive or gate all usages of CJWalletManager (and any calls to
PeerManager::hasQueue()/processMessage() that forward to it) behind the same
ENABLE_WALLET conditional as used in init.cpp so that net_processing.cpp no
longer references CJWalletManager when wallets are disabled; update the
Makefile.am entry for libbitcoin_node_a_SOURCES or wrap the
CJWalletManager-using code paths in net_processing.cpp/PeerManager with `#ifdef`
ENABLE_WALLET to restore a consistent implementation owner.

---

Nitpick comments:
In `@src/coinjoin/client.cpp`:
- Around line 140-152: Reformat the three methods to match project clang-format
style: place the opening brace on the same line as the function signature, use
consistent spacing for initializers (e.g., bool expected = false or bool
expected{false} per project style), and ensure return/statements and braces
follow the same indentation and spacing conventions; update
CCoinJoinClientManager::startMixing, CCoinJoinClientManager::stopMixing, and
CCoinJoinClientManager::isMixing accordingly so clang-format no longer reports
differences.

In `@src/coinjoin/coinjoin.h`:
- Around line 352-376: Reformat the two function declarations so clang-format is
happy: put the [[nodiscard]] attribute directly adjacent to the function
declaration rather than on its own line and ensure spacing matches the
surrounding declarations; specifically update the declarations for
TryHasQueueFromMasternode and TryCheckDuplicate so the [[nodiscard]] attribute
precedes the function signature on the same line and keep the
EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue) annotation and ending semicolon intact.

In `@src/coinjoin/server.cpp`:
- Line 193: ProcessDSQUEUE currently uses the non-blocking
m_queueman.TryAddQueue(dsq) while CreateNewSession and CheckForCompleteQueue use
the blocking AddQueue(), causing peer DSQ messages to be silently dropped under
contention; update the codebase to either (a) unify behavior by replacing
TryAddQueue with AddQueue in ProcessDSQUEUE or (b) explicitly document the
intended difference—add a clear comment at the ProcessDSQUEUE call site
referencing TryAddQueue, explaining that peer DSQ drops are intentional and why,
and update any developer docs; ensure you reference the methods TryAddQueue,
AddQueue and the functions ProcessDSQUEUE, CreateNewSession,
CheckForCompleteQueue so reviewers can locate and verify the change.
- Around line 90-97: When TryHasQueueFromMasternode (called on m_queueman with
m_mn_activeman.GetOutPoint()) returns std::nullopt in the DSACCEPT handling, do
not silently return: add a LogPrint(BCLog::COINJOIN, ...) that records the
lock-contention case including the masternode outpoint and peer info for
debugging, and reply to the peer so the request is not dropped (e.g. call
PushStatus(peer, STATUS_REJECTED, ERR_BUSY) or another appropriate
temporary-failure code) so callers get a clear transient-failure response
instead of no response.

In `@src/coinjoin/walletman.h`:
- Around line 51-53: The comment for doForClient should explicitly document that
the callback func is non-reentrant: add a short sentence to the function comment
(for doForClient) stating that func is invoked while the wallet-manager lock is
held and must not call back into CJWalletManager or invoke any methods that may
acquire the same wallet-manager lock (to avoid deadlocks); reference
CCoinJoinClientManager as the callback parameter type and make the
non-reentrancy constraint explicit so future callers know not to call
CJWalletManager methods from within func.

In `@src/qt/overviewpage.cpp`:
- Around line 599-607: The current logic leaves notMixing=false when
withCoinJoin() can't find a client, causing the UI to incorrectly show "mixing"
active; change the default to true (bool notMixing{true};) so absence of a
client is treated as not mixing, and keep the existing update inside
walletModel->withCoinJoin(...) that sets notMixing = !client.isMixing(), while
preserving the cached-blocks check (client.getCachedBlocks(),
client.setCachedBlocks(...), updateCoinJoinProgress()) so state stays correct
when a client is present.
- Around line 581-585: withCoinJoin's boolean return value is ignored so a
missing client can be silently skipped; change the call sites (e.g. the block
using walletModel->withCoinJoin, the tooFast assignment that references
client.getCachedBlocks()) to capture the return value and handle the false case
explicitly: if withCoinJoin(...) returns false, log a warning (or otherwise
handle client-unavailable) and avoid treating tooFast as valid, otherwise run
the lambda to set tooFast using nBestHeight, client.getCachedBlocks(), GetTime()
and nLastDSProgressBlockTime; apply the same pattern to all repeated sites in
this file.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 252bc396-6bb7-4302-80d6-7814b1bb0bef

📥 Commits

Reviewing files that changed from the base of the PR and between d1eab6c and acb27a0.

📒 Files selected for processing (25)
  • contrib/auto_gdb/simple_class_obj.py
  • src/Makefile.am
  • src/Makefile.test.include
  • src/coinjoin/client.cpp
  • src/coinjoin/client.h
  • src/coinjoin/coinjoin.cpp
  • src/coinjoin/coinjoin.h
  • src/coinjoin/interfaces.cpp
  • src/coinjoin/server.cpp
  • src/coinjoin/server.h
  • src/coinjoin/walletman.cpp
  • src/coinjoin/walletman.h
  • src/init.cpp
  • src/interfaces/coinjoin.h
  • src/qt/bitcoingui.cpp
  • src/qt/optionsdialog.cpp
  • src/qt/overviewpage.cpp
  • src/qt/walletmodel.cpp
  • src/qt/walletmodel.h
  • src/rpc/coinjoin.cpp
  • src/test/coinjoin_basemanager_tests.cpp
  • src/test/coinjoin_queue_tests.cpp
  • src/wallet/init.cpp
  • src/wallet/test/coinjoin_tests.cpp
  • src/wallet/wallet.cpp
💤 Files with no reviewable changes (2)
  • src/Makefile.test.include
  • src/test/coinjoin_basemanager_tests.cpp

Comment on lines +159 to +165
const auto isDup = m_queueman.TryCheckDuplicate(dsq);
if (!isDup.has_value()) return;
if (*isDup) {
LogPrint(BCLog::COINJOIN, "DSQUEUE -- Peer %d is sending WAY too many dsq messages for a masternode with collateral %s\n", from, dsq.masternodeOutpoint.ToStringShort());
return;
}
} // cs_vecqueue
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Pipeline failure: Clang-format issue.

The CI pipeline reports a formatting difference on these lines. Please run clang-format to fix the indentation/wrapping of the LogPrint statement.

🧰 Tools
🪛 GitHub Actions: Clang Diff Format Check

[error] 159-161: Clang-format-diff.py reported formatting differences (LogPrint wrapped/indented). This step failed due to detected diff; exit code 1.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/coinjoin/server.cpp` around lines 159 - 165, Run clang-format on the
affected file and fix the formatting of the LogPrint line in the block where you
call m_queueman.TryCheckDuplicate(dsq); specifically reflow/wrap the long
LogPrint invocation (the line referencing BCLog::COINJOIN, the format string
"DSQUEUE -- Peer %d is sending WAY too many dsq messages for a masternode with
collateral %s\n", and the arguments from and
dsq.masternodeOutpoint.ToStringShort()) so it matches the project's clang-format
style (proper indentation and line breaks) and then commit the formatted change.

obj.clear();
obj.setObject();
obj.pushKV("queue_size", GetQueueSize());
obj.pushKV("queue_size", m_queueman.GetQueueSize());
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Pipeline failure: Clang-format issue.

The CI pipeline reports a formatting difference on this line (spacing/alignment of pushKV). Please run clang-format to fix.

🧰 Tools
🪛 GitHub Actions: Clang Diff Format Check

[error] 901-904: Clang-format-diff.py reported formatting differences (obj.pushKV queue_size spacing alignment). This step failed due to detected diff; exit code 1.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/coinjoin/server.cpp` at line 904, Clang-format flagged inconsistent
spacing on the obj.pushKV line; reformat the line that calls
obj.pushKV("queue_size", m_queueman.GetQueueSize()) so spacing and alignment
match project clang-format rules (run clang-format or your editor's format
command), ensuring the call to obj.pushKV and the m_queueman.GetQueueSize()
argument use the same spacing style as surrounding pushKV calls.

Comment on lines +281 to +286
node.coinjoin_loader->WithClient(wallet->GetName(), [&](auto& client) {
if (client.isMixing()) {
throw JSONRPCError(RPC_WALLET_ERROR,
strprintf("Wallet \"%s\" is currently mixing, cannot change salt!", str_wallet));
}
});
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Mar 29, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Silent no-op when client not found during salt generation.

Unlike other RPC handlers that use CHECK_NONFATAL, coinjoinsalt_generate silently continues if WithClient returns false (client not found). This means the mixing check is skipped, which could allow salt generation while mixing if the client lookup fails for some reason.

Consider adding CHECK_NONFATAL for consistency:

♻️ Suggested fix for consistency
     if (node.coinjoin_loader != nullptr) {
-        node.coinjoin_loader->WithClient(wallet->GetName(), [&](auto& client) {
+        CHECK_NONFATAL(node.coinjoin_loader->WithClient(wallet->GetName(), [&](auto& client) {
             if (client.isMixing()) {
                 throw JSONRPCError(RPC_WALLET_ERROR,
                                    strprintf("Wallet \"%s\" is currently mixing, cannot change salt!", str_wallet));
             }
-        });
+        }));
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
node.coinjoin_loader->WithClient(wallet->GetName(), [&](auto& client) {
if (client.isMixing()) {
throw JSONRPCError(RPC_WALLET_ERROR,
strprintf("Wallet \"%s\" is currently mixing, cannot change salt!", str_wallet));
}
});
CHECK_NONFATAL(node.coinjoin_loader->WithClient(wallet->GetName(), [&](auto& client) {
if (client.isMixing()) {
throw JSONRPCError(RPC_WALLET_ERROR,
strprintf("Wallet \"%s\" is currently mixing, cannot change salt!", str_wallet));
}
}));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/coinjoin.cpp` around lines 281 - 286, The coinjoin salt generation
path (coinjoinsalt_generate) currently calls
node.coinjoin_loader->WithClient(...) and silently proceeds if WithClient
returns false, skipping the mixing check; update the call to assert the client
lookup succeeded using CHECK_NONFATAL so the RPC fails early when the client
isn't found (i.e., wrap the WithClient invocation result in CHECK_NONFATAL and
preserve the existing mixing check that throws JSONRPCError if
client.isMixing()), referencing coinjoin_loader->WithClient,
coinjoinsalt_generate, CHECK_NONFATAL, and client.isMixing to locate and fix the
code.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

logic is matched between versions; no new bugs introduced or any logic changed.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@knst, you're right — I missed the equivalence. The original code explicitly guarded with cj_clientman != nullptr, meaning the mixing check was intentionally skipped when no client was found. The new WithClient call preserves exactly that behavior: when the client doesn't exist, WithClient returns false without invoking the callback, so the salt generation proceeds unimpeded — just as before. Adding CHECK_NONFATAL would have been a behavioral regression, not a fix. Thanks for the clarification!

@knst knst force-pushed the fix-cj-dangling-ptr branch from acb27a0 to b017394 Compare March 30, 2026 09:09
@thepastaclaw
Copy link
Copy Markdown

thepastaclaw commented Mar 30, 2026

Review Gate

Commit: 92d0f348

  • Debounce: 115m ago (need 30m)

  • CI checks: check(s) failed

  • CodeRabbit review: comment found

  • Run review now (check to override)

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b017394a9c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +211 to 213
if (fAutoStart) {
mgr.startMixing();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Skip CoinJoin autostart when wallet is locked

InitCoinJoinSettings() now calls mgr.startMixing() unconditionally when -coinjoinautostart=1, but this function no longer has any wallet-lock check. For encrypted wallets that start locked, this sets isMixing() to true even though DoAutomaticDenominating() exits early on lock, so the client reports a running mix session that cannot proceed; this also blocks operations like coinjoinsalt_generate/coinjoinsalt_set that reject while mixing is active. The previous logic explicitly stopped mixing for locked wallets, so this is a behavioral regression for locked-at-startup nodes.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/coinjoin/client.cpp`:
- Around line 140-152: Reformat the three CCoinJoinClientManager methods
(CCoinJoinClientManager::startMixing, ::stopMixing, and ::isMixing) to match
project clang-format style: place the opening brace on the same line as the
function signature, ensure spacing around return types and parameters is
consistent, and keep single-line bodies compact (e.g., return fMixing; or
fMixing = false; as appropriate); run clang-format or apply the repo's style so
these method definitions and use of the atomic fMixing variable conform to CI
formatting rules.

In `@src/coinjoin/interfaces.cpp`:
- Around line 44-50: The lambda passed to manager().doForClient in AddWallet()
is mis-indented and fails clang-format; reformat the call so the lambda's
opening bracket and body align with the call (ensure the capture/signature and
body are indented consistently), e.g. place the lambda on the next line with its
body indented and the closing "});" aligned with the start of the
manager().doForClient call; target the AddWallet function and the
manager().doForClient(..., [&](CCoinJoinClientManager& mgr) { ... }) lambda for
the fix.

In `@src/coinjoin/walletman.cpp`:
- Around line 39-79: The function declarations with EXCLUSIVE_LOCKS_REQUIRED
annotations (doForClient, processMessage, getMixingMasternodes, addWallet,
removeWallet, flushWallet, UpdatedBlockTip, DoMaintenance, and ProcessDSQueue)
fail clang-format; reflow each declaration so the EXCLUSIVE_LOCKS_REQUIRED
attribute is placed on its own trailing line (not inline with parameters) per
project style — keep the return type, function name and parameter list together,
then place the EXCLUSIVE_LOCKS_REQUIRED(...) on the next line for each of the
listed symbols to satisfy formatting checks.

In `@src/wallet/init.cpp`:
- Around line 204-213: The default for -enablecoinjoin in
WalletInit::InitCoinJoinSettings is currently true
(gArgs.GetBoolArg("-enablecoinjoin", true)) which contradicts the documented
default of 0; change the fallback value to false (or to whatever project-wide
DEFAULT_COINJOIN_ENABLED constant you use) in the GetBoolArg call and keep
CCoinJoinClientOptions::SetEnabled(...) as is — alternatively, update the help
text to reflect that coinjoin is enabled by default; see
WalletInit::InitCoinJoinSettings, CCoinJoinClientOptions::SetEnabled, and
gArgs.GetBoolArg("-enablecoinjoin", ...) to make the fix.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c6feb52a-bb86-4e9a-88f9-6dc0853dd1b8

📥 Commits

Reviewing files that changed from the base of the PR and between acb27a0 and b017394.

📒 Files selected for processing (18)
  • src/Makefile.am
  • src/coinjoin/client.cpp
  • src/coinjoin/client.h
  • src/coinjoin/interfaces.cpp
  • src/coinjoin/walletman.cpp
  • src/coinjoin/walletman.h
  • src/init.cpp
  • src/interfaces/coinjoin.h
  • src/qt/bitcoingui.cpp
  • src/qt/optionsdialog.cpp
  • src/qt/overviewpage.cpp
  • src/qt/walletmodel.cpp
  • src/qt/walletmodel.h
  • src/rpc/coinjoin.cpp
  • src/wallet/init.cpp
  • src/wallet/test/coinjoin_tests.cpp
  • src/wallet/wallet.cpp
  • src/walletinitinterface.h
✅ Files skipped from review due to trivial changes (4)
  • src/Makefile.am
  • src/wallet/wallet.cpp
  • src/init.cpp
  • src/interfaces/coinjoin.h
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/qt/optionsdialog.cpp
  • src/wallet/test/coinjoin_tests.cpp
  • src/qt/bitcoingui.cpp
  • src/coinjoin/walletman.h
  • src/qt/walletmodel.cpp
  • src/qt/overviewpage.cpp
  • src/rpc/coinjoin.cpp

Comment on lines +140 to 152
bool CCoinJoinClientManager::startMixing() {
bool expected{false};
return fMixing.compare_exchange_strong(expected, true);
}

void CCoinJoinClientManager::StopMixing() {
void CCoinJoinClientManager::stopMixing() {
fMixing = false;
}

bool CCoinJoinClientManager::IsMixing() const
bool CCoinJoinClientManager::isMixing() const
{
return fMixing;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix formatting to pass CI checks.

The CI pipeline reports clang-format errors for the method declarations/definitions at lines 140-152. The brace placement and method signatures need reformatting.

🔧 Suggested formatting fix
-bool CCoinJoinClientManager::startMixing() {
+bool CCoinJoinClientManager::startMixing()
+{
     bool expected{false};
     return fMixing.compare_exchange_strong(expected, true);
 }

-void CCoinJoinClientManager::stopMixing() {
+void CCoinJoinClientManager::stopMixing()
+{
     fMixing = false;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
bool CCoinJoinClientManager::startMixing() {
bool expected{false};
return fMixing.compare_exchange_strong(expected, true);
}
void CCoinJoinClientManager::StopMixing() {
void CCoinJoinClientManager::stopMixing() {
fMixing = false;
}
bool CCoinJoinClientManager::IsMixing() const
bool CCoinJoinClientManager::isMixing() const
{
return fMixing;
}
bool CCoinJoinClientManager::startMixing()
{
bool expected{false};
return fMixing.compare_exchange_strong(expected, true);
}
void CCoinJoinClientManager::stopMixing()
{
fMixing = false;
}
bool CCoinJoinClientManager::isMixing() const
{
return fMixing;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/coinjoin/client.cpp` around lines 140 - 152, Reformat the three
CCoinJoinClientManager methods (CCoinJoinClientManager::startMixing,
::stopMixing, and ::isMixing) to match project clang-format style: place the
opening brace on the same line as the function signature, ensure spacing around
return types and parameters is consistent, and keep single-line bodies compact
(e.g., return fMixing; or fMixing = false; as appropriate); run clang-format or
apply the repo's style so these method definitions and use of the atomic fMixing
variable conform to CI formatting rules.

Comment on lines 44 to 50
void AddWallet(const std::shared_ptr<CWallet>& wallet) override
{
manager().addWallet(wallet);
g_wallet_init_interface.InitCoinJoinSettings(*this, wallet_loader());
manager().doForClient(wallet->GetName(), [&](CCoinJoinClientManager& mgr) {
g_wallet_init_interface.InitCoinJoinSettings(mgr);
});
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix lambda indentation to pass CI checks.

The CI pipeline reports clang-format errors for the lambda call indentation in AddWallet().

🔧 Suggested formatting fix
     void AddWallet(const std::shared_ptr<CWallet>& wallet) override
     {
         manager().addWallet(wallet);
-        manager().doForClient(wallet->GetName(), [&](CCoinJoinClientManager& mgr) {
-                g_wallet_init_interface.InitCoinJoinSettings(mgr);
-        });
+        manager().doForClient(wallet->GetName(),
+                              [&](CCoinJoinClientManager& mgr) { g_wallet_init_interface.InitCoinJoinSettings(mgr); });
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void AddWallet(const std::shared_ptr<CWallet>& wallet) override
{
manager().addWallet(wallet);
g_wallet_init_interface.InitCoinJoinSettings(*this, wallet_loader());
manager().doForClient(wallet->GetName(), [&](CCoinJoinClientManager& mgr) {
g_wallet_init_interface.InitCoinJoinSettings(mgr);
});
}
void AddWallet(const std::shared_ptr<CWallet>& wallet) override
{
manager().addWallet(wallet);
manager().doForClient(wallet->GetName(),
[&](CCoinJoinClientManager& mgr) { g_wallet_init_interface.InitCoinJoinSettings(mgr); });
}
🧰 Tools
🪛 GitHub Actions: Clang Diff Format Check

[error] 44-44: Clang-format would reformat lambda call indentation in AddWallet().

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/coinjoin/interfaces.cpp` around lines 44 - 50, The lambda passed to
manager().doForClient in AddWallet() is mis-indented and fails clang-format;
reformat the call so the lambda's opening bracket and body align with the call
(ensure the capture/signature and body are indented consistently), e.g. place
the lambda on the next line with its body indented and the closing "});" aligned
with the start of the manager().doForClient call; target the AddWallet function
and the manager().doForClient(..., [&](CCoinJoinClientManager& mgr) { ... })
lambda for the fix.

Comment on lines 39 to 79

public:
bool hasQueue(const uint256& hash) const override;
CCoinJoinClientManager* getClient(const std::string& name) override;
bool doForClient(const std::string& name, const std::function<void(CCoinJoinClientManager&)>& func) override EXCLUSIVE_LOCKS_REQUIRED(!cs_wallet_manager_map);
MessageProcessingResult processMessage(CNode& peer, CChainState& chainstate, CConnman& connman, CTxMemPool& mempool,
std::string_view msg_type, CDataStream& vRecv) override;
std::string_view msg_type, CDataStream& vRecv) override EXCLUSIVE_LOCKS_REQUIRED(!cs_ProcessDSQueue, !cs_wallet_manager_map);
std::optional<CCoinJoinQueue> getQueueFromHash(const uint256& hash) const override;
std::optional<int> getQueueSize() const override;
std::vector<CDeterministicMNCPtr> getMixingMasternodes() override;
void addWallet(const std::shared_ptr<wallet::CWallet>& wallet) override;
void removeWallet(const std::string& name) override;
void flushWallet(const std::string& name) override;
std::vector<CDeterministicMNCPtr> getMixingMasternodes() override EXCLUSIVE_LOCKS_REQUIRED(!cs_wallet_manager_map);
void addWallet(const std::shared_ptr<wallet::CWallet>& wallet) override EXCLUSIVE_LOCKS_REQUIRED(!cs_wallet_manager_map);
void removeWallet(const std::string& name) override EXCLUSIVE_LOCKS_REQUIRED(!cs_wallet_manager_map);
void flushWallet(const std::string& name) override EXCLUSIVE_LOCKS_REQUIRED(!cs_wallet_manager_map);

protected:
// CValidationInterface
void UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload) override;
void UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload) override EXCLUSIVE_LOCKS_REQUIRED(!cs_wallet_manager_map);

private:
const bool m_relay_txes;
ChainstateManager& m_chainman;
CDeterministicMNManager& m_dmnman;
CMasternodeMetaMan& m_mn_metaman;
CTxMemPool& m_mempool;
const CMasternodeSync& m_mn_sync;
const llmq::CInstantSendManager& m_isman;

// m_queueman is declared before the wallet map so that it is destroyed
// after all CCoinJoinClientManager instances (which hold a raw pointer to it).
// Null when relay_txes is false (no queue processing).
const std::unique_ptr<CoinJoinQueueManager> m_queueman;

mutable Mutex cs_ProcessDSQueue;

mutable Mutex cs_wallet_manager_map;
std::map<const std::string, std::unique_ptr<CCoinJoinClientManager>> m_wallet_manager_map GUARDED_BY(cs_wallet_manager_map);

void DoMaintenance(CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(!cs_wallet_manager_map);

[[nodiscard]] MessageProcessingResult ProcessDSQueue(NodeId from, CConnman& connman, std::string_view msg_type,
CDataStream& vRecv) EXCLUSIVE_LOCKS_REQUIRED(!cs_ProcessDSQueue, !cs_wallet_manager_map);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix formatting to pass CI checks.

The CI pipeline reports clang-format errors for the function declarations with EXCLUSIVE_LOCKS_REQUIRED annotations. These need to be reflowed onto multiple lines per the project's formatting rules.

🧰 Tools
🪛 GitHub Actions: Clang Diff Format Check

[error] 39-79: Clang-format would reflow function declarations/override lines with EXCLUSIVE_LOCKS_REQUIRED onto multiple lines.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/coinjoin/walletman.cpp` around lines 39 - 79, The function declarations
with EXCLUSIVE_LOCKS_REQUIRED annotations (doForClient, processMessage,
getMixingMasternodes, addWallet, removeWallet, flushWallet, UpdatedBlockTip,
DoMaintenance, and ProcessDSQueue) fail clang-format; reflow each declaration so
the EXCLUSIVE_LOCKS_REQUIRED attribute is placed on its own trailing line (not
inline with parameters) per project style — keep the return type, function name
and parameter list together, then place the EXCLUSIVE_LOCKS_REQUIRED(...) on the
next line for each of the listed symbols to satisfy formatting checks.

Comment on lines +204 to 213
void WalletInit::InitCoinJoinSettings(CCoinJoinClientManager& mgr) const
{
const auto& wallets{wallet_loader.getWallets()};
CCoinJoinClientOptions::SetEnabled(!wallets.empty() ? gArgs.GetBoolArg("-enablecoinjoin", true) : false);
CCoinJoinClientOptions::SetEnabled(gArgs.GetBoolArg("-enablecoinjoin", true));
if (!CCoinJoinClientOptions::IsEnabled()) {
return;
}
bool fAutoStart = gArgs.GetBoolArg("-coinjoinautostart", DEFAULT_COINJOIN_AUTOSTART);
for (auto& wallet : wallets) {
auto manager = Assert(coinjoin_loader.GetClient(wallet->getWalletName()));
if (wallet->isLocked(/*fForMixing=*/false)) {
manager->stopMixing();
LogPrintf("CoinJoin: Mixing stopped for locked wallet \"%s\"\n", wallet->getWalletName());
} else if (fAutoStart) {
manager->startMixing();
LogPrintf("CoinJoin: Automatic mixing started for wallet \"%s\"\n", wallet->getWalletName());
}
if (fAutoStart) {
mgr.startMixing();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the documented default value vs actual behavior
echo "=== Checking -enablecoinjoin arg registration ==="
rg -n "enablecoinjoin" --type=cpp -C2

echo ""
echo "=== Checking DEFAULT_COINJOIN values ==="
rg -n "DEFAULT_COINJOIN" --type=cpp src/coinjoin/

Repository: dashpay/dash

Length of output: 5546


Fix the -enablecoinjoin default value mismatch.

At line 206, GetBoolArg("-enablecoinjoin", true) defaults to true, but the help text at line 103 documents the default as 0 (false). Either update the fallback value to match the documented default or update the documentation to reflect that CoinJoin is enabled by default during wallet initialization.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/wallet/init.cpp` around lines 204 - 213, The default for -enablecoinjoin
in WalletInit::InitCoinJoinSettings is currently true
(gArgs.GetBoolArg("-enablecoinjoin", true)) which contradicts the documented
default of 0; change the fallback value to false (or to whatever project-wide
DEFAULT_COINJOIN_ENABLED constant you use) in the GetBoolArg call and keep
CCoinJoinClientOptions::SetEnabled(...) as is — alternatively, update the help
text to reflect that coinjoin is enabled by default; see
WalletInit::InitCoinJoinSettings, CCoinJoinClientOptions::SetEnabled, and
gArgs.GetBoolArg("-enablecoinjoin", ...) to make the fix.

@knst knst force-pushed the fix-cj-dangling-ptr branch from b017394 to 5c2f753 Compare March 30, 2026 10:29
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5c2f753b78

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

// Dash Specific WalletInitInterface
virtual void AutoLockMasternodeCollaterals(interfaces::WalletLoader& wallet_loader) const = 0;
virtual void InitCoinJoinSettings(interfaces::CoinJoin::Loader& coinjoin_loader, interfaces::WalletLoader& wallet_loader) const = 0;
virtual void InitCoinJoinSettings(CCoinJoinClientManager& mgr) const = 0;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve no-wallet InitCoinJoinSettings override compatibility

Changing WalletInitInterface::InitCoinJoinSettings to take CCoinJoinClientManager& here leaves the non-wallet implementation out of sync: DummyWalletInit still declares InitCoinJoinSettings(interfaces::CoinJoin::Loader&, interfaces::WalletLoader&) in src/dummywallet.cpp. In --disable-wallet builds that signature no longer overrides the pure virtual method, so the dummy wallet target fails to compile (or remains abstract).

Useful? React with 👍 / 👎.

@knst knst force-pushed the fix-cj-dangling-ptr branch from 5c2f753 to 92d0f34 Compare March 30, 2026 12:56
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 92d0f348f8

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +43 to +45
if (!wallet->IsLocked()) {
g_wallet_init_interface.InitCoinJoinSettings(mgr);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Initialize CoinJoin settings for locked wallets too

CoinJoinLoaderImpl::AddWallet now calls InitCoinJoinSettings only when !wallet->IsLocked(), but CoinJoinLoaderImpl sets CCoinJoinClientOptions::SetEnabled(false) in its constructor and this is the only place settings are re-initialized. For encrypted wallets that start locked, CoinJoin stays globally disabled forever (even after later unlock), so coinjoin start/status/stop/reset fail in ValidateCoinJoinArguments() with the internal-disabled error. This is a functional regression for the default locked-at-startup wallet flow.

Useful? React with 👍 / 👎.

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.

2 participants