Skip to content

feat: add use_cj feature to RPCs send, sendall and fundrawtransaction - #7261

Merged
PastaPastaPasta merged 1 commit into
dashpay:developfrom
knst:feature-use-cj-rpc-send
Aug 23, 2026
Merged

feat: add use_cj feature to RPCs send, sendall and fundrawtransaction#7261
PastaPastaPasta merged 1 commit into
dashpay:developfrom
knst:feature-use-cj-rpc-send

Conversation

@knst

@knst knst commented Mar 30, 2026

Copy link
Copy Markdown
Collaborator

Issue being fixed or feature implemented

The RPCs send, sendall, fundrawtransaction doesn't have a functionality to use cj to fund transaction.

What was done?

Added flag use_cj to the options and use it coin-select.

How Has This Been Tested?

Call RPC send '[{"yRgziYdxyApbFsvph89JAVMzjsv8en16bA":0.01}]' null "unset" null '{"use_cj":true}'

It produced tx getrawtransaction 8df1998514388b9c19705077e743c5bc2fd6dd378a6287cd865f0a1b91e8afaf 1 which looks as expected.

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 knst added this to the 24 milestone Mar 30, 2026
@knst knst added the RPC Some notable changes to RPC params/behaviour/descriptions label Mar 30, 2026
@github-actions

Copy link
Copy Markdown

✅ No Merge Conflicts Detected

This PR currently has no conflicts with other open PRs.

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

Copy link
Copy Markdown

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: 1160ac9529

ℹ️ 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 thread src/wallet/rpc/spend.cpp
Comment on lines +1149 to +1151
if (options.exists("use_cj")) {
coin_control.UseCoinJoin(options["use_cj"].get_bool());
}

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 Enforce use_cj for manually specified sendall inputs

When sendall is called with both options.use_cj=true and options.inputs, this new flag does not actually constrain the provided inputs to fully mixed coins: the inputs branch later accepts any wallet UTXO and skips the AvailableCoins(..., &coin_control, ...) filtering path. In this scenario, callers can unintentionally spend non-CoinJoin funds despite opting into CoinJoin-only behavior, so this should either validate each specified input as fully mixed or reject the combination as incompatible.

Useful? React with 👍 / 👎.

@coderabbitai

coderabbitai Bot commented Mar 30, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a4ffa740-14bf-45b4-be37-1c24577ca0b4

📥 Commits

Reviewing files that changed from the base of the PR and between 7be28f8 and 99a818c.

📒 Files selected for processing (2)
  • src/wallet/rpc/spend.cpp
  • test/functional/rpc_coinjoin.py

Included review availability: Your plan provides up to 4 included reviews per hour; 3 remain after this review.


Walkthrough

This change adds the use_cj option to wallet spending RPCs. CoinJoin mode selects only fully mixed inputs and rejects non-mixed preset inputs. Completed CoinJoin transactions record DS=1 metadata. Functional tests cover automatic selection, explicit inputs, and ordinary spending.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 99a81

This localized change adds an optional CoinJoin funding flag to selected RPCs and has no actionable merge-blocking risk remaining beyond normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant WalletRPC
  participant FundTransaction
  participant CoinControl
  participant TransactionCommit
  WalletRPC->>FundTransaction: pass use_cj
  FundTransaction->>CoinControl: enable CoinJoin-only selection
  CoinControl-->>FundTransaction: select fully mixed inputs
  FundTransaction->>TransactionCommit: commit transaction with DS=1 metadata
Loading

Suggested reviewers: pastapastapasta, udjinm6

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ 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%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the addition of the use_cj feature to the main RPCs changed by the pull request.
Description check ✅ Passed The description explains the use_cj feature, testing, and affected RPCs, so it is related to the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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/wallet/rpc/spend.cpp`:
- Line 525: The shared options schema for FundTransaction() currently includes
the undocumented "use_cj" flag which unintentionally exposes it on
walletcreatefundedpsbt; either remove "use_cj" from the shared schema and add it
only to the RPCs that should accept it (e.g. include the {"use_cj", ...} entry
in the specific RPC handler's options instead of the common list used by
FundTransaction()), or keep it in the schema but add the corresponding
documentation for walletcreatefundedpsbt; locate the {"use_cj",
UniValueType(UniValue::VBOOL)} entry, the FundTransaction() call sites, and the
walletcreatefundedpsbt RPC handler and apply one of these changes so the option
is either documented or only accepted by the intended RPCs.
- Around line 1149-1151: In sendall, options.inputs bypass CoinJoin
restrictions: when coin_control.UseCoinJoin is true and options.inputs is
provided, validate and enforce CoinJoin eligibility on those explicit inputs
instead of accepting them unconditionally; specifically, in the branch handling
options.inputs check coin_control.UseCoinJoin() (or the internal flag set by
options["use_cj"]) and filter the provided inputs to only include UTXOs that
meet the wallet's CoinJoin/mixing eligibility (or return an error if any
provided input is not eligible), updating the inputs used for constructing the
transaction so CoinJoin rules are enforced.
🪄 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: 6c7262d7-30a0-4177-87ea-ca0b6144b1f2

📥 Commits

Reviewing files that changed from the base of the PR and between ac94680 and 1160ac9.

📒 Files selected for processing (1)
  • src/wallet/rpc/spend.cpp

Comment thread src/wallet/rpc/spend.cpp
Comment thread src/wallet/rpc/spend.cpp
@knst
knst marked this pull request as draft March 30, 2026 20:14
@knst
knst marked this pull request as ready for review April 13, 2026 08:38
@knst
knst requested review from PastaPastaPasta and UdjinM6 April 13, 2026 08:38

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

Copy link
Copy Markdown

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: 1160ac9529

ℹ️ 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 thread src/wallet/rpc/spend.cpp
Comment on lines +576 to +578
if (options.exists("use_cj")) {
coinControl.UseCoinJoin(options["use_cj"].get_bool());
}

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 Reject non-mixed preset inputs when enabling use_cj

Setting options.use_cj=true here is unsafe for fundrawtransaction (and send when add_inputs=true) if callers provide preselected non-mixed inputs: SelectCoins skips those preset inputs under ONLY_FULLY_MIXED (in src/wallet/spend.cpp), but the RPC funding flow later keeps the original tx.vin entries when merging the funded transaction, so those non-CoinJoin inputs are still spent while fee/change were computed as if they were absent. In that path users can unintentionally spend non-CoinJoin funds and overpay fees, so this option should reject incompatible preset inputs instead of silently proceeding.

Useful? React with 👍 / 👎.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Code Review

I validated this PR directly from the checked-out diff because both ACP reviewer lanes stalled before producing any findings. The new use_cj plumbing is wired into the automatic coin-selection paths, but sendall has one behavioral hole: when callers also pass explicit inputs, the RPC accepts and spends them without checking whether they are actually mixed, so the newly documented privacy constraint is silently bypassed.

Reviewed commit: 1160ac9

🔴 1 blocking

1 additional finding

🔴 blocking: `sendall` ignores `use_cj=true` when the caller provides explicit inputs

src/wallet/rpc/spend.cpp (lines 1172-1182)

This new option only affects the automatic branch at line 1184, where AvailableCoins(*pwallet, &coin_control, fee_rate, ...) filters the wallet UTXO set using coin_control.nCoinType. In the explicit-input branch above, the code just validates ownership/spentness and sums the values, so sendall(..., {"inputs": [...], "use_cj": true}) will still spend non-mixed wallet UTXOs. That makes the newly advertised "Use CoinJoin funds only" flag a no-op for one of the supported call patterns and can leak unmixed coins in exactly the RPC the PR is extending. The branch needs to reject non-mixed selected inputs (or declare use_cj incompatible with inputs) instead of silently bypassing the mixed-only requirement.

💡 Suggested change
            } else if (options.exists("inputs")) {
                for (const CTxIn& input : rawTx.vin) {
                    if (pwallet->IsSpent(input.prevout)) {
                        throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Input not available. UTXO (%s:%d) was already spent.", input.prevout.hash.ToString(), input.prevout.n));
                    }
                    const CWalletTx* tx{pwallet->GetWalletTx(input.prevout.hash)};
                    if (!tx || input.prevout.n >= tx->tx->vout.size() || !(pwallet->IsMine(tx->tx->vout[input.prevout.n]) & (coin_control.fAllowWatchOnly ? ISMINE_ALL : ISMINE_SPENDABLE))) {
                        throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Input not found. UTXO (%s:%d) is not part of wallet.", input.prevout.hash.ToString(), input.prevout.n));
                    }
                    if (coin_control.IsUsingCoinJoin() && !pwallet->IsFullyMixed(input.prevout)) {
                        throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Input not available. UTXO (%s:%d) is not fully mixed.", input.prevout.hash.ToString(), input.prevout.n));
                    }
                    total_input_value += tx->tx->vout[input.prevout.n].nValue;
                }
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `src/wallet/rpc/spend.cpp`:
- [BLOCKING] lines 1172-1182: `sendall` ignores `use_cj=true` when the caller provides explicit inputs
  This new option only affects the automatic branch at line 1184, where `AvailableCoins(*pwallet, &coin_control, fee_rate, ...)` filters the wallet UTXO set using `coin_control.nCoinType`. In the explicit-input branch above, the code just validates ownership/spentness and sums the values, so `sendall(..., {"inputs": [...], "use_cj": true})` will still spend non-mixed wallet UTXOs. That makes the newly advertised "Use CoinJoin funds only" flag a no-op for one of the supported call patterns and can leak unmixed coins in exactly the RPC the PR is extending. The branch needs to reject non-mixed selected inputs (or declare `use_cj` incompatible with `inputs`) instead of silently bypassing the mixed-only requirement.

@thepastaclaw

thepastaclaw commented May 8, 2026

Copy link
Copy Markdown
Collaborator

✅ Final review complete — no blockers (commit 99a818c)

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Code Review

Three convergent issues confirmed against the head commit. The blocking bug — sendall's explicit-input branch never enforces CoinJoin-only selection — is real: the loop at lines 1172-1182 only checks IsSpent/IsMine and never consults coin_control.IsUsingCoinJoin() or IsFullyMixed, while the auto-selection branch correctly funnels through AvailableCoins/SelectCoins. The DS=1 wallet marker is also genuinely missing from the new send/sendall commit path (FinishTransaction calls CommitTransaction with an empty mapValue, while the legacy SendMoney path sets it). And the PR ships with no automated coverage at all.

Reviewed commit: 1160ac9

Fresh dispatcher run for this queue item. A same-SHA review already existed, so this records the fresh verification without duplicating inline threads.

Code Review

Reviewed commit: 1160ac9

🔴 1 blocking | 🟡 2 suggestion(s) | 💬 1 nitpick(s)

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `src/wallet/rpc/spend.cpp`:
- [BLOCKING] lines 1172-1182: `sendall` does not enforce CoinJoin-only spending when explicit `inputs` are supplied
  `sendall` enables CoinJoin-only selection by calling `coin_control.UseCoinJoin(options["use_cj"].get_bool())` at lines 1149-1151, which sets `nCoinType = CoinType::ONLY_FULLY_MIXED`. The auto-selection branch at line 1184 then funnels through `AvailableCoins(*pwallet, &coin_control, ...)`, and `SelectCoins` drops non-mixed preset inputs at `src/wallet/spend.cpp:548-552`. However, the explicit-input branch at lines 1172-1182 hand-builds the input set using only `IsSpent`/`IsMine`/`fAllowWatchOnly` checks — it never consults `coin_control.IsUsingCoinJoin()` or `wallet.IsFullyMixed(input.prevout)`. A caller that passes non-mixed UTXOs together with `use_cj=true` will have those non-mixed coins spent silently, defeating the privacy guarantee documented as "Use CoinJoin funds only". Reject `use_cj=true` combined with non-mixed explicit `inputs` by validating each input via `wallet.IsFullyMixed`.
- [SUGGESTION] lines 106-115: `send`/`sendall` CoinJoin spends are committed without the existing `DS=1` wallet marker
  `SendMoney()` (used by `sendtoaddress`/`sendmany`) sets `map_value["DS"] = "1"` at line 154 whenever `coin_control.IsUsingCoinJoin()` is true, so `CommitTransaction` records the outgoing tx in wallet history as a CoinJoin send. The new `use_cj` paths in `send` (line 1022) and `sendall` (line 1263) both go through `FinishTransaction()`, which commits with an empty `mapValue` at line 111: `pwallet->CommitTransaction(tx, {}, /*orderForm*/ {})`. Downstream classification in `src/wallet/rpc/transactions.cpp:355` and `src/qt/transactionrecord.cpp:153,271` keys on the `DS` marker to distinguish CoinJoin sends from ordinary sends, so spends made via these new paths will be misclassified in `listtransactions` and the Qt UI. `fundrawtransaction` is unaffected since it does not commit. Plumb a `mapValue_t` with `DS=1` into `FinishTransaction` when `coin_control.IsUsingCoinJoin()` is set.
- [SUGGESTION] lines 522-1151: No automated tests added for `use_cj` on `send`/`sendall`/`fundrawtransaction`
  The PR adds a new user-visible option that alters coin selection semantics on three RPCs but touches no unit or functional tests. The author-supplied manual test covers only `send`'s auto-selection happy path. Cases worth covering: (1) `sendall` with explicit `inputs` + `use_cj=true` (the blocking finding above), (2) `send`/`fundrawtransaction` with non-mixed explicit `inputs` + `use_cj=true` — `SelectCoins` silently drops them at `src/wallet/spend.cpp:548-552`, (3) `use_cj=true` when the wallet has zero fully-mixed denominations (clear error expected), (4) interactions with `add_inputs`, `send_max`, `include_watching`, and the PSBT path (`psbt=true`, `add_to_wallet=false`). A functional test modeled on `wallet_sendall.py` / `feature_coinjoin.py` would have caught the explicit-input regression immediately. Per CLAUDE.md, new Dash-specific features should have functional tests where applicable.

@knst
knst marked this pull request as draft June 26, 2026 17:03
@knst
knst force-pushed the feature-use-cj-rpc-send branch from 1160ac9 to 04bb8b0 Compare August 21, 2026 20:39
@knst
knst force-pushed the feature-use-cj-rpc-send branch from 04bb8b0 to 99a818c Compare August 21, 2026 20:55
@knst

knst commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator Author

🔴 blocking: sendall ignores use_cj=true when the caller provides explicit inputs

src/wallet/rpc/spend.cpp (lines 1172-1182)

This new option only affects the automatic branch at line 1184, where AvailableCoins(*pwallet, &coin_control, fee_rate, ...) filters the wallet UTXO set using coin_control.nCoinType. In the explicit-input branch above, the code just validates ownership/spentness and sums the values, so sendall(..., {"inputs": [...], "use_cj": true}) will still spend non-mixed wallet UTXOs. That makes the newly advertised "Use CoinJoin funds only" flag a no-op for one of the supported call patterns and can leak unmixed coins in exactly the RPC the PR is extending. The branch needs to reject non-mixed selected inputs (or declare use_cj incompatible with inputs) instead of silently bypassing the mixed-only requirement.

@thepastaclaw
Addressed in 99a818c: the explicit-inputs branch now rejects any input that
is not fully mixed when use_cj=true ("Input not available. UTXO (txid:n) is
not fully mixed.", -8), and the same check covers preset inputs in
FundTransaction() for send/fundrawtransaction/walletcreatefundedpsbt.
Covered by test_use_cj_option in test/functional/rpc_coinjoin.py.

@knst
knst marked this pull request as ready for review August 22, 2026 17:44
@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Final validation — Codex/Sol only (Phase 2 disabled)

The exact head correctly enforces mixed-only selection for automatic and preset inputs, documents the shared walletcreatefundedpsbt support, preserves the DS=1 wallet marker, and adds functional coverage; the local rpc_coinjoin.py run passed. The only remaining in-scope issue is a commit-history nitpick: the subject enumerates three RPCs while the commit deliberately exposes, documents, and tests a fourth.
Source: Codex reviewer (exact backend model ID was not supplied in the evidence); Claude Agent SDK verifier (exact backend model ID was not exposed in the runtime context); openclaw-agent/cliproxy/gpt-5.6-sol is orchestration-only and not reviewer evidence.

Validated zero-blocker Codex/Sol precheck evidence was promoted to final because Phase 2 (Sonnet/Opus) is temporarily disabled. This is Codex/Sol-only final validation, not Codex + Sonnet/Opus coverage.

Review provenance

  • Codex reviewers: gpt-5.6-sol — general (completed), gpt-5.6-sol — dash-core-commit-history (completed)
  • Verifier: gpt-5.6-sol — verifier
  • Sonnet/Opus: not run (Phase 2 disabled — temporary Codex/Sol-only final)
  • Secondary pass: disabled (temporary_phase2_sonnet_disable)

💬 1 nitpick(s)

1 additional finding(s) omitted (not in diff).

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `<commit:99a818c>`:
- [NITPICK] <commit:99a818c>:1: Commit subject omits `walletcreatefundedpsbt`
  The subject explicitly lists `send`, `sendall`, and `fundrawtransaction`, but this commit also exposes, documents, and tests `use_cj` for `walletcreatefundedpsbt`. Reword the subject to include all four RPCs or use a complete summary such as `feat: add use_cj to transaction funding RPCs` so the permanent history accurately describes the change.

@PastaPastaPasta
PastaPastaPasta merged commit fb26484 into dashpay:develop Aug 23, 2026
52 of 57 checks passed
PastaPastaPasta added a commit that referenced this pull request Aug 23, 2026
2821f84 test: extract CoinJoin mixing helper (pasta)
1cb2b49 test: cover use_cj success path in rpc_coinjoin.py (pasta)

Pull request description:

  ## Issue being fixed or feature implemented
  Follow-up to #7261. The `use_cj` tests added there only exercise failure paths: the test wallet never holds a fully mixed coin, so nothing verifies that a successful `use_cj` spend actually selects mixed inputs, suppresses change, or records the `DS="1"` CoinJoin marker that `FinishTransaction()` now attaches via `CommitTransaction()`. A regression in any of those would pass the suite unnoticed.

  ## What was done?
  Added a success-path subtest to `test/functional/rpc_coinjoin.py`. It builds two fully mixed coins by simulating mixing: same-denomination, fee-free self-spends advance a denominated output by one round each, and they are mined directly via `generateblock` since zero-fee transactions are not relayed. Chaining `COINJOIN_ROUNDS_MIN + COINJOIN_RANDOM_ROUNDS` rounds makes `IsFullyMixed()` deterministic regardless of the wallet's salt (the salt-based coin flip only applies strictly below that threshold).

  It then asserts:
  - both coins report full `coinjoin_rounds` in `listunspent` and count towards the `getbalances` anonymized balance;
  - `send` accepts a fully mixed preset input (the positive counterpart of the rejection tests from #7261), spends exactly that input, creates no change output (the remainder is paid as fee, as fully mixed coins are spent in whole denominations) and records `DS="1"`;
  - `sendall` with automatic selection sweeps exactly the remaining fully mixed coin and records `DS="1"`.

  `setcoinjoinrounds` is a node-global setting; the subtest pins it to `COINJOIN_ROUNDS_MIN` and restores the default at the end so later subtests are unaffected.

  ## How Has This Been Tested?
  `test/functional/test_runner.py rpc_coinjoin.py` passes locally (macOS arm64, `--enable-debug` build, both descriptor and legacy wallet paths of the runner's default). `test/lint/lint-python.py` clean.

  ## Breaking Changes
  None, test-only change.

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas
  - [x] 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)_

Top commit has no ACKs.

Tree-SHA512: dc6a730ae9bac1a8b69abc3b051e12f3d4b289ceacda7c4048b440611fd9c90acc0e3dffce94db8cf4611b63ea3000a48e1da6751274634572f8ee606a32a274
PastaPastaPasta added a commit that referenced this pull request Aug 28, 2026
…n to the wallet

5fa6b4a fix(wallet): don't cache CoinJoin rounds for txes unknown to the wallet (pasta)

Pull request description:

  ## Issue being fixed or feature implemented
  Follow-up to #7261. `CWallet::GetRealOutpointCoinJoinRounds()` memoizes its result in `mapOutpointRoundsCache`, including the `-1` it returns for an outpoint whose transaction the wallet has no record of. Nothing invalidates that entry when the wallet later learns about the transaction — the only invalidation is `ClearCoinJoinRoundsCache()`, called from the `coinjoinsalt` RPCs.

  This used to be unreachable in practice because every caller passed wallet-owned outpoints. Since #7261, `fundrawtransaction`, `send` and `walletcreatefundedpsbt` feed arbitrary user-supplied preset inputs into `IsFullyMixed()`, so querying an outpoint before the wallet knows its transaction (e.g. while a rescan is still running) permanently poisons the cache: once the wallet does add that transaction, a coin that is in fact a fully mixed denomination keeps reporting `-1` rounds for the lifetime of the wallet in memory. It is then excluded from `use_cj` spends, missing from the anonymized balance in `getbalances`, and re-qualifies for mixing, so the client would re-mix (and pay fees on) an already-mixed coin.

  ## What was done?
  Don't memoize the unknown-transaction result: drop the just-inserted cache entry on that path and return `-1` directly. The recursion in `GetRealOutpointCoinJoinRounds()` only descends into inputs for which `InputIsMine()` is true, which requires the previous transaction to be in `mapWallet`, so the directly queried outpoint is the only case that can be unknown — the emplace-based cycle protection for the recursive path is unaffected.

  Added a unit regression test that queries an outpoint before the wallet knows its transaction (expects `-1`) and again after `AddToWallet()` (expects the real rounds). The test fails with `-1 != 1` without the fix.

  ## How Has This Been Tested?
  - New regression test in `src/wallet/test/coinjoin_tests.cpp`: `./src/test/test_dash --run_test=coinjoin_tests` passes with the fix and fails without it.
  - `test/functional/test_runner.py rpc_coinjoin.py wallet_send.py wallet_fundrawtransaction.py` all pass locally (macOS arm64, `--enable-debug`).

  ## Breaking Changes
  None. The only behavior change is that an unknown outpoint is re-evaluated on subsequent queries instead of being permanently cached as having no rounds.

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas
  - [x] 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)_

Top commit has no ACKs.

Tree-SHA512: 297e018459b6b129e8c59df1e85f2b6d7f6d7d07df8744658b1f3a50906a6af901c2b05a22c6f860bc3aacc906b6eabb3336f3d39a2caf914d1fc97708527ba3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

RPC Some notable changes to RPC params/behaviour/descriptions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants