Skip to content

Conversation

PastaPastaPasta
Copy link
Member

Issue being fixed or feature implemented

Coinjoin CreateDenoms will currently intentionally re-use the input address for change; I see no reason to do this, and it can be non-ideal for certain instances such as if you receive on X and are tracking that address X for further receipts. Say every time you receive on X you send to Y as a part of an exchange or something.

The only time I can image where it's not ideal is if you receive to X multiple times, maybe you receive 1.9 and 0.1 (contrived) in this case, say your first create denoms makes 1.9 -> 1 and 0.9; because that 0.9 is kept in address X, the second 0.1 can be combined with the 0.9 to make another 1 Dash denom.

What was done?

Use fresh change address instead of re-using

How Has This Been Tested?

NOT TESTED (@kwvg please test and confirm functionality of createdenoms)

Breaking Changes

Checklist:

Go over all the following points, and put an x in all the boxes that apply.

  • 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 (for repository code-owners and collaborators only)

@PastaPastaPasta PastaPastaPasta added this to the 23 milestone Oct 3, 2025
Copy link

github-actions bot commented Oct 3, 2025

✅ No Merge Conflicts Detected

This PR currently has no conflicts with other open PRs.

Copy link

coderabbitai bot commented Oct 3, 2025

Walkthrough

  • In src/coinjoin/util.cpp, the CTransactionBuilder constructor changes destination-change handling: coinControl.destChange is assigned from tallyItemIn.txdest only when the wallet flag WALLET_FLAG_AVOID_REUSE is not set; otherwise it is left unset to allow a fresh change address. An include for wallet/walletutil.h is added.
  • doc/release-notes-6870.md adds a Wallet section note stating CoinJoin denomination creation now respects avoid_reuse: with avoid_reuse enabled, change goes to a fresh change address; otherwise it returns to the source address.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly summarizes the primary change by stating that the wallet now supports fresh change addresses to avoid address reuse in CoinJoin denomination creation, accurately reflecting the refactor without extraneous details.
Description Check ✅ Passed The description directly addresses the existing behavior of CoinJoin CreateDenoms, explains the motivation for change, details what was implemented, and indicates testing status, making it clearly related to the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cfae1e6 and efc689c.

📒 Files selected for processing (1)
  • src/coinjoin/util.cpp (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.{cpp,h,cc,cxx,hpp}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.{cpp,h,cc,cxx,hpp}: Dash Core C++ codebase must be written in C++20 and require at least Clang 16 or GCC 11.1
Dash uses unordered_lru_cache for efficient caching with LRU eviction

Files:

  • src/coinjoin/util.cpp
🧬 Code graph analysis (1)
src/coinjoin/util.cpp (1)
src/wallet/interfaces.cpp (15)
  • m_wallet (158-161)
  • m_wallet (166-166)
  • m_wallet (175-175)
  • m_wallet (176-176)
  • m_wallet (182-182)
  • m_wallet (183-183)
  • m_wallet (438-441)
  • m_wallet (442-445)
  • m_wallet (517-517)
  • m_wallet (518-518)
  • m_wallet (519-519)
  • m_wallet (520-520)
  • m_wallet (521-521)
  • m_wallet (526-526)
  • m_wallet (586-586)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: linux64_tsan-build / Build source
  • GitHub Check: linux64_nowallet-build / Build source
  • GitHub Check: arm-linux-build / Build source
  • GitHub Check: linux64_sqlite-build / Build source
  • GitHub Check: linux64_ubsan-build / Build source
  • GitHub Check: linux64-build / Build source
  • GitHub Check: linux64_fuzz-build / Build source
🔇 Additional comments (2)
src/coinjoin/util.cpp (2)

12-12: LGTM: Include is necessary for WALLET_FLAG_AVOID_REUSE.

The new include provides access to the WALLET_FLAG_AVOID_REUSE wallet flag used in the conditional logic below.


129-135: Address testing, keypool handling, and formatting before merge

The change correctly uses WALLET_FLAG_AVOID_REUSE to pick legacy vs. fresh change, but:

  • Untested code: the PR is marked “NOT TESTED.” Add unit/integration tests covering both flag states in the CoinJoin flow.
  • Keypool consumption: using fresh change consumes an extra key. Manually review the CoinJoin transaction creation paths (e.g. util.cpp and client.cpp) to ensure keypool size is checked and errors are handled if depleted.
  • Formatting: run clang-format on src/coinjoin/util.cpp to satisfy CI.

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.

@kxcd
Copy link

kxcd commented Oct 3, 2025

Please abandon this PR, it is not a contrived use case, but a very common one instead.

Consider the following uses cases.

  1. I am mixing on my MN payout address. I will get paid every 9 days. I do CJ, the dust is sent back to my payout address, next time I get paid, all is denominated, dust is reduces.
  2. I am on ⁠🫂-crowdnode my withdrawal address is fixed. I need to mix....
  3. I am in the DAO, my address gets paid each month, I mix.....

We have enough dust in wallets as it is. 🥺 Please don't change the current behaviour.

…e address handling

Added a new command-line option `-coinjoinfreshchange` to allow users to control whether change from CoinJoin denomination creation is sent to a fresh change address or back to the source address. This change improves privacy by preventing address reuse. Updated relevant documentation and code to support this feature.
@PastaPastaPasta
Copy link
Member Author

@kxcd Consider the latest commit; behavior would be to retain the current legacy logic unless a user specifies in their config -coinjoinfreshchange; I would say this typically shouldn't be used, as it changes your appearance on the network, and if someone sees a few make denoms that uses this separate change logic, it is a red flag, though statistically, it shouldn't reduce your final privacy much, it may be used to link some of your inputs you received to separate addresses.

Copy link

@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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6ca08f6 and d4044d4.

📒 Files selected for processing (5)
  • doc/release-notes-6870.md (1 hunks)
  • src/coinjoin/options.cpp (2 hunks)
  • src/coinjoin/options.h (4 hunks)
  • src/coinjoin/util.cpp (2 hunks)
  • src/wallet/init.cpp (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.{cpp,h,cc,cxx,hpp}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.{cpp,h,cc,cxx,hpp}: Dash Core C++ codebase must be written in C++20 and require at least Clang 16 or GCC 11.1
Dash uses unordered_lru_cache for efficient caching with LRU eviction

Files:

  • src/wallet/init.cpp
  • src/coinjoin/options.h
  • src/coinjoin/options.cpp
  • src/coinjoin/util.cpp
doc/**

📄 CodeRabbit inference engine (CLAUDE.md)

Unless specifically prompted, avoid making changes to the doc directory (documentation)

Files:

  • doc/release-notes-6870.md
🧬 Code graph analysis (4)
src/wallet/init.cpp (1)
src/dummywallet.cpp (1)
  • argsman (31-31)
src/coinjoin/options.h (1)
src/coinjoin/options.cpp (4)
  • Get (15-20)
  • Get (15-15)
  • SetFreshChange (64-68)
  • SetFreshChange (64-64)
src/coinjoin/options.cpp (1)
src/qt/optionsmodel.cpp (2)
  • Init (50-335)
  • Init (50-50)
src/coinjoin/util.cpp (1)
src/coinjoin/options.h (1)
  • GetFreshChange (64-64)
🪛 GitHub Actions: Clang Diff Format Check
src/coinjoin/util.cpp

[error] 1-1: Clang-format-diff detected format differences for src/coinjoin/util.cpp. The script reported differences; formatting needs to be applied. Command failed with exit code 1: git diff -U0 origin/develop -- $(git ls-files -- $(cat test/util/data/non-backported.txt)) | ./contrib/devtools/clang-format-diff.py -p1 > diff_output.txt

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: linux64_multiprocess-build / Build source
  • GitHub Check: linux64_tsan-build / Build source
  • GitHub Check: win64-build / Build source
  • GitHub Check: mac-build / Build source
  • GitHub Check: linux64_ubsan-build / Build source
  • GitHub Check: linux64_sqlite-build / Build source
  • GitHub Check: linux64_fuzz-build / Build source
  • GitHub Check: linux64-build / Build source
  • GitHub Check: linux64_nowallet-build / Build source
  • GitHub Check: arm-linux-build / Build source
🔇 Additional comments (4)
src/wallet/init.cpp (1)

108-108: New wallet arg exposure looks correct

Flag is well-scoped under WALLET_COINJOIN and uses DEFAULT_COINJOIN_FRESHCHANGE.

doc/release-notes-6870.md (1)

1-9: Release note is clear and accurate

Accurately describes default and behavior when enabled.

src/coinjoin/options.h (1)

34-34: Option surface and state additions look consistent

Default, getters/setters, and atomic storage align with existing options.

Also applies to: 64-65, 73-74, 92-93

src/coinjoin/options.cpp (1)

64-69: Fresh-change flag wiring is correct
Setter, Init() parsing, JSON exposure, and runtime usage align with existing patterns—no issues found.


#include <coinjoin/util.h>
#include <policy/fees.h>
#include <coinjoin/options.h>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

❓ Verification inconclusive

Functional change LGTM; fix formatting to unblock CI

  • Behavior toggle is correct: only set destChange when fresh-change is disabled; otherwise let wallet choose a fresh change address.
  • CI is failing on clang-format for this file. Please run the project’s formatter to resolve.

To fix:

  • Run contrib/devtools/clang-format-diff.py as in the CI message, or clang-format the file to project style.

Also, please sanity-check keypool usage with fresh-change enabled (one extra key per tx) to ensure no unexpected keypool depletion during CreateDenoms.

Also applies to: 129-134


Fix formatting in src/coinjoin/util.cpp to unblock CI

  • Apply contrib/devtools/clang-format-diff.py (or run clang-format) on this file.
  • Sanity-check keypool consumption with fresh-change enabled (one extra key per tx) to avoid depletion during CreateDenoms.
🤖 Prompt for AI Agents
In src/coinjoin/util.cpp around line 7, the file needs clang-format applied and
the keypool consumption logic must account for fresh-change using one extra key
per transaction; run contrib/devtools/clang-format-diff.py or clang-format on
this file to fix formatting, then update the sanity-check that verifies keypool
availability during CreateDenoms to reserve one additional key per tx when
fresh-change is enabled (or decrement the available key count by one per tx) so
the check reflects the true consumption and prevents depletion.

@UdjinM6
Copy link

UdjinM6 commented Oct 5, 2025

I don't see any value in having CJ specific change in a fresh new address tbh. It's pretty obvious that all outputs are yours and the only output with non-denom value is a change. No privacy is gained from using a fresh new address imo.

Why would anyone use it? If it's simply for the sake of following "avoid reuse" principle then maybe we could do this only for wallets with WALLET_FLAG_AVOID_REUSE flag (instead of introducing a CJ specific config option)?

@PastaPastaPasta
Copy link
Member Author

"No privacy is gained from using a fresh new address imo." Agreed; the problem becomes, what if you have some dumb automation tied to the receiving of fund on an address. Your dumb automation just sees, "Oh another transaction came in to our address. Guess we should convert X dash to USDT and send it to the address on file", or whatever their automation is configured to do.

… avoid_reuse setting

Modified CoinJoin denomination creation to utilize the wallet's "avoid_reuse" setting. When enabled, change is sent to a fresh change address to prevent address reuse; otherwise, it defaults to the legacy behavior of returning change to the source address. Removed the now obsolete `-coinjoinfreshchange` option and updated relevant documentation.
Copy link

@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: 0

🧹 Nitpick comments (1)
src/coinjoin/options.cpp (1)

65-76: LGTM! Thread-safe singleton initialization.

The Init() method correctly initializes the singleton using a static local variable (line 68), which guarantees thread-safe initialization in C++11 and later. The use of std::call_once in Get() (line 17) ensures Init() is called exactly once, and the assert (line 67) catches any programming errors. All command-line arguments are properly validated and clamped to safe ranges.

Optional refactors:

  1. Prefer static_cast over C-style casts: Lines 70-74 use C-style casts (int). For a C++20 codebase, consider using static_cast<int>() for type safety, or remove the casts if GetIntArg() already returns int.

  2. Extract validation helper: The repetitive std::min(std::max(...), ...) pattern on lines 70-74 could be extracted into a helper function to reduce duplication:

template<typename T>
T ClampValue(T value, T min_val, T max_val) {
    return std::min(std::max(value, min_val), max_val);
}

Then use it as:

instance.nCoinJoinSessions = ClampValue(gArgs.GetIntArg("-coinjoinsessions", DEFAULT_COINJOIN_SESSIONS), 
                                         MIN_COINJOIN_SESSIONS, MAX_COINJOIN_SESSIONS);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4800e2f and cfae1e6.

📒 Files selected for processing (4)
  • doc/release-notes-6870.md (1 hunks)
  • src/coinjoin/options.cpp (1 hunks)
  • src/coinjoin/util.cpp (2 hunks)
  • src/wallet/init.cpp (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • doc/release-notes-6870.md
  • src/wallet/init.cpp
  • src/coinjoin/util.cpp
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.{cpp,h,cc,cxx,hpp}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.{cpp,h,cc,cxx,hpp}: Dash Core C++ codebase must be written in C++20 and require at least Clang 16 or GCC 11.1
Dash uses unordered_lru_cache for efficient caching with LRU eviction

Files:

  • src/coinjoin/options.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: x86_64-pc-linux-gnu_multiprocess / Build depends
  • GitHub Check: arm-linux-gnueabihf / Build depends
  • GitHub Check: Lint / Run linters
  • GitHub Check: x86_64-pc-linux-gnu / Build depends
  • GitHub Check: x86_64-w64-mingw32 / Build depends
  • GitHub Check: x86_64-apple-darwin / Build depends
  • GitHub Check: x86_64-pc-linux-gnu_nowallet / Build depends

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 efc689c

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.

3 participants