Skip to content

backport: wallet coin selection prerequisites for #7400 - #7479

Draft
thepastaclaw wants to merge 5 commits into
dashpay:developfrom
thepastaclaw:backport-7400-prereq-coin-selection
Draft

backport: wallet coin selection prerequisites for #7400#7479
thepastaclaw wants to merge 5 commits into
dashpay:developfrom
thepastaclaw:backport-7400-prereq-coin-selection

Conversation

@thepastaclaw

@thepastaclaw thepastaclaw commented Jul 23, 2026

Copy link
Copy Markdown

Issue being fixed or feature implemented

Dash PR #7400 currently combines its target wallet changes with a longer Bitcoin Core prerequisite chain. This extracts the first independently mergeable prerequisite batch so the backports can be reviewed and landed in smaller units.

This branch was rebased onto upstream/develop at e25f9925df4f0bafaf5f6d13ac34e14bdc8eecc9 (pre-rebase backup refs/heads/backup/pr7479-pre-develop-rebase-a8c5f714; pre-provenance-fix backup refs/heads/backup/7479-pre-provenance-fixes-182745cf). It contains four logical Bitcoin Core backports plus one Dash CompactSize adaptation, within the requested limit of at most five commits per prerequisite PR. It does not modify or replace #7400; that PR must remain unchanged until the prerequisite PRs merge.

What was done?

Backported:

bitcoin#26699 intentionally travels with bitcoin#25685 because omitting it makes manual coin-control balance checks exclude the selected coins.

Partial backport: bitcoin#26699

Subject form: partial Merge bitcoin/bitcoin#26699 (Dash convention).

Included (adapted to Dash):

Upstream commit Summary
dc1cc1c3599 bugfix: getAvailableBalance includes selected coins after bitcoin#25685
74eac3a82fc test: useAvailableBalance coverage
2f76ac03839 / 306aab5bb47 test helpers (SyncUpWallet / SetupLegacyWatchOnlyWallet; MiniGUI not ported wholesale — Dash constructors differ)
68eed5df865 presentPSBT QMessageBox(this); ConfirmSend StandardButton; legacy watch-only PSBT GUI test

Intentionally omitted:

Upstream commit Why
cd98b717398 gui: getAvailableBalance, include watch-only balance Upstream bitcoin#26687 fixed WalletModel::getAvailableBalance so the cached-balance path from bitcoin-core/gui#598 adds watch_only_balance when private keys are disabled. Dash never landed that WalletModel::getAvailableBalance API. prepareTransaction and useAvailableBalance both call wallet().getAvailableBalance(coin_control) after fAllowWatchOnly is set for private-keys-disabled wallets; AvailableCoins already includes solvable watch-only outputs under that flag; SendCoinsDialog::setBalance already shows watch_only_balance for legacy private-keys-disabled wallets. Porting cd98b717 would require introducing the model-level cache API only to re-express behavior Dash already has on the wallet-interface path — out of scope for this prerequisite batch. Still valid on current develop.

Watch-only PSBT test:

An adapted TestGUIWatchOnly is included. It builds a private-keys-disabled legacy wallet via ImportPubKeys, asserts the send dialog displays watch_only_balance, drives Create Unsigned (QMessageBox::Save), and decodes the clipboard PSBT with DecodeBase64PSBT. Dash adaptations: CWallet requires the CoinJoin loader; no PlatformStyle constructors; no full MiniGUI port.

After rebase onto develop, src/qt/test/wallettests.cpp also retains develop’s CoinJoin autobackup-failure tooltip regression coverage (nWalletBackups 0/-1/-2) from #7479’s new base; both includes (util/strencodings.h + util/system.h) are kept.

Dash-specific adaptations

Commit map (post develop rebase)

Commit Subject
512571f3597 Merge bitcoin#25647
b1a545f183d Merge bitcoin#26203
7ca4e430d2b Merge bitcoin#25685 (+ folded Dash benchmark/input-selection adaptations)
2ffd8e52194 partial Merge bitcoin#26699
66d72b4aa17 Dash adaptations for bitcoin#25647 (CompactSize fee accounting)

Old→new (pre-rebase review-rewrite head → current post-rebase/review-reconciliation head):

  • 56439297b8f512571f3597
  • db880c91dd3b1a545f183d
  • 126566523f87ca4e430d2b
  • d0b79b12c0f2ffd8e52194
  • a8c5f714bab66d72b4aa17

Later sequencing, after this PR merges:

  1. Open a second prerequisite PR with wallet: bugfix, load a wallet with an unknown/corrupt descriptor causes a fatal error bitcoin/bitcoin#26021, wallet: bugfix, invalid crypted key "checksum_valid" set bitcoin/bitcoin#26532, wallet: bugfix, invalid CoinsResult cached total amount bitcoin/bitcoin#26560, wallet: Coin Selection, return accurate error messages bitcoin/bitcoin#26661, and wallet: group outputs only once, decouple it from Coin Selection bitcoin/bitcoin#25806, including the associated Dash cursor-close and test-utility adaptations.
  2. After both prerequisite PRs merge, rebase backport: Bitcoin Core v0.26 backports (batch 2 rebuild) #7400 and retain its reduced target stack: rpc: add descriptorprocesspsbt rpc bitcoin/bitcoin#25796, Introduce MockableDatabase for wallet unit tests bitcoin/bitcoin#26715, walletdb: Remove unused CreateMockWalletDatabase bitcoin/bitcoin#27665, and wallet, bench: Move commonly used functions to their own file and fix a bug bitcoin/bitcoin#27666.

No later prerequisite PR has been opened yet.

How Has This Been Tested?

Tested on macOS arm64 using the repository's autotools build configured against the existing Dash depends prefix.

Build targets at final head 66d72b4aa17:

  • test/test_dash
  • bench/bench_dash
  • qt/test/test_dash-qt

Commit-level buildability:

Unit and benchmark coverage at final head (rerun after review reconciliation):

  • ./src/test/test_dash --run_test=coinselector_tests,spend_tests,wallet_tests — 34 test cases, no errors
  • ./src/bench/bench_dash -filter='WalletCreateTx.*' — both WalletCreateTxUseOnlyPresetInputs and WalletCreateTxUsePresetInputsAndCoinSelection ran successfully

Qt:

  • ./src/qt/test/test_dash-qt -platform minimal — all suites passed; on macOS minimal, WalletTests / AddressBookTests / AppTests bodies skip due to known Qt cocoa/minimal limitations (pre-existing). The new TestGUIWatchOnly is under that same guard and is intended to run on Linux CI / non-minimal platforms.

Additional checks:

  • git diff --check clean on e25f9925df4..HEAD
  • All five commits have valid GPG signatures (git verify-commit Good signature from thepastaclaw)
  • git merge-tree --write-tree --name-only HEAD upstream/develop clean after rebase
  • Rebase conflict resolution in src/qt/test/wallettests.cpp keeps develop CoinJoin tooltip coverage and the adapted watch-only Create Unsigned/clipboard PSBT path

Breaking Changes

None.

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 (not applicable)
  • I have assigned this pull request to a milestone

@thepastaclaw
thepastaclaw force-pushed the backport-7400-prereq-coin-selection branch from b4cf789 to bc52f0a Compare July 23, 2026 12:50
@thepastaclaw

thepastaclaw commented Jul 23, 2026

Copy link
Copy Markdown
Author

✅ Final review complete — no blockers (commit 66d72b4)

@thepastaclaw

Copy link
Copy Markdown
Author

CI triage for current head bc52f0a9c79c: the sole red job is linux64_ubsan-test / Test source, where unrelated feature_governance.py observed 24 votes instead of 25. The PR changes wallet coin-selection prerequisites/adaptations, and every other build/test job passed. I tried rerunning the failed jobs, but GitHub rejected it because this account lacks repository admin rights.

@knst could you rerun the failed job when convenient? No branch change looks warranted from this failure.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Final validation — Codex + Sonnet

The final tree correctly carries the four advertised wallet backports and the Dash-specific CompactSize and BlockInfo adaptations; the omitted upstream legacy watch-only PSBT GUI coverage is an explicit architecture-specific exclusion, while the applicable selected-coin balance behavior is adapted and tested in Dash. No runtime or Dash-subsystem correctness defects remain at the PR head. The only retained finding is commit-stack hygiene: the benchmark is introduced with an uncompilable wallet notification call and repaired three commits later.

Review provenance

  • Codex reviewers: gpt-5.6-sol — general (completed), gpt-5.6-sol — dash-core-commit-history (completed), gpt-5.6-sol — backport-reviewer (completed)
  • Verifier: gpt-5.6-sol — final-verifier (fallback)
  • Sonnet reviewers: claude-sonnet-5 — general (failed), claude-sonnet-5 — dash-core-commit-history (failed), claude-sonnet-5 — backport-reviewer (completed), claude-sonnet-5 — general (completed), claude-sonnet-5 — dash-core-commit-history (completed)

🟡 1 suggestion(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/bench/wallet_create_tx.cpp`:
- [SUGGESTION] src/bench/wallet_create_tx.cpp:73: bench target does not compile for three intermediate commits (fixed by final commit in this same PR)
  Commit `c392040ca08` registers this new source in `bench_bench_dash_SOURCES` but calls `wallet.blockConnected(block, pindex->nHeight)`. At that commit, `CWallet` declares only `blockConnected(const interfaces::BlockInfo&)`, so a wallet-enabled benchmark build fails at `c392040ca08` and remains broken through `0969ce0fe7e` and `cde726cc5e1`. The final commit correctly changes the call to `kernel::MakeBlockInfo`, but Dash preserves commits when merging, so fold that adaptation into `c392040ca08` or place it immediately afterward to keep the permanent history independently buildable and bisectable.

Comment thread src/bench/wallet_create_tx.cpp
@PastaPastaPasta

Copy link
Copy Markdown
Member

I've resolved each script finding against the actual PR head tree, base, and commit chain. Here's the review.


Backport review: #7479backport: wallet coin selection prerequisites for #7400

Verdict: REQUIRES_REVIEW (no HIGH consensus/RPC risk; findings are wallet/GUI/build-commit correctness and declaration issues). PR is currently a draft and mergeable.

The mechanical compare ran against head bc52f0a9c79 / base 87692f7b150. bitcoin#25647, bitcoin#26203, and bitcoin#25685 are substantially faithful (the flagged PARTIALs are Dash adaptations I verified below). The real problems are concentrated in bitcoin#26699 and in commit-level buildability.

Findings (by severity)

1. bitcoin/bitcoin#26699 is an UNDECLARED_PARTIAL — blocker. [MEDIUM/METADATA]
The commit is titled Merge bitcoin/bitcoin#26699, but only 1 of 6 upstream commits landed (dc1cc1c3599 "getAvailableBalance skips selected coins"); cd98b717398 "getAvailableBalance, include watch only balance" is ABSENT (1/12 lines) and three GUI refactor/test commits are dropped. Dash's WalletModel::getAvailableBalance (src/wallet/interfaces.cpp) still has no have_watch_only && privateKeysDisabled() special case, and src/qt/sendcoinsdialog.cpp:906 still calls model->wallet().getAvailableBalance(coin_control) rather than the upstream model-level path. Per Dash convention, the subject must be partial Merge bitcoin/bitcoin#26699, and each omission must be documented. The PR body justifies dropping the test, but does not declare the watch-only balance behavior commit as omitted — those are two different things and the body conflates them.

2. Three intermediate commits do not build until the last commit. [MEDIUM]
The commit chain is 5659105d (#25647) → 48e662ea (#26203) → c392040c (#25685) → 0969ce0f (#26699) → cde726cc (#25647 adapt) → bc52f0a9 (#25685 adapt). The raw #25685 merge wires bench/wallet_create_tx.cpp into the build (src/Makefile.bench.include:94) with wallet.blockConnected(block, pindex->nHeight) (src/bench/wallet_create_tx.cpp:72), but Dash's CWallet::blockConnected takes one const interfaces::BlockInfo& parameter (src/wallet/wallet.h:686). That two-arg call is a compile error at c392040c, 0969ce0f, and cde726cc, and is only fixed by bc52f0a9 switching to kernel::MakeBlockInfo(...). So commits 3-5 are individually non-buildable. AGENTS.md allows intentionally non-building commits only if called out; this is neither called out nor a deliberately-ordered test-before-fix pattern.

3. Upstream watch-only PSBT GUI test coverage dropped without an adapted equivalent. [MEDIUM/TEST]
68eed5df865 ("add coverage for PSBT creation on legacy watch-only wallets", 9/71) did not land; src/qt/test/wallettests.cpp retains only VerifyUseAvailableBalance. The PR's "architecture differs" note (watch-only coins go through the wallet interface rather than the cached-balance path) explains why the exact upstream test can't be ported, but per project rules dropped tests must still be justified behaviorally: if the user-visible watch-only PSBT/balance path exists in Dash, it should have an adapted test, or the PR must explicitly state why it remains untested.

4. Minor GUI refactor drops inside bitcoin#26699. [LOW]
QMessageBox msgBox(this) and the ConfirmSend confirm_type refactor didn't land — sendcoinsdialog.cpp:510 is still QMessageBox msgBox; and wallettests.cpp:59 still uses [text, cancel]. Low risk, but they're part of the undeclared partial in finding 1.

Resolved as Dash adaptations (checked, not real drops)

  • #25647 spend.cpp PARTIAL: the rewritten tx-building region is Dash's CoinJoin / fully-mixed no-change handling plus the CompactSize fee-retry loop in cde726cc, and that commit adds the regression tests. Intended.
  • #25685 bench/Makefile: bench_bench_bitcoinbench_bench_dash and GetTotalAmount()CoinsResult::total_amount are Dash API renames, present at head.
  • #25685 test/util/wallet.cpp/.h: Dash keeps its existing single-arg getNewDestination(CWallet&); the upstream two-arg overload introduced by 3fcb545ab26 was adapted away and isn't required.
  • #25685 functional test PARTIALs (rpc_psbt.py:509, wallet_fundrawtransaction.py:965, wallet_send.py:494): the "Not solvable pre-selected input" asserts are all present; the ONLY difference is full ext_utxo["txid"] vs upstream's [0:10] truncation. Formatting, not dropped coverage.

Bottom line

bitcoin#25647, bitcoin#26203, and bitcoin#25685 look faithful after Dash adaptation. bitcoin#26699 is a real undeclared partial that must be marked partial Merge bitcoin/bitcoin#26699 with the watch-only balance commit and the GUI refactor/test commits itemized, and commits 3-5 should be reordered (fold each Dash adaptation into its own backport commit) or explicitly declared non-building. Before merge, the watch-only PSBT test-coverage gap needs either an adapted Dash test or an explicit in-body justification that covers the behavior, not just the upstream implementation.

Files referenced are at the PR head bc52f0a9c79 (the local worktree is not checked out to the PR branch).

(please rerun backport verification)

@thepastaclaw
thepastaclaw force-pushed the backport-7400-prereq-coin-selection branch from bc52f0a to a8c5f71 Compare August 8, 2026 02:47
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

This pull request has conflicts, please rebase.

@thepastaclaw

Copy link
Copy Markdown
Author

Thanks for the careful review. Addressed in the rewritten branch at a8c5f714bab:

1. Undeclared partial bitcoin#26699
Retitled to partial Merge bitcoin/bitcoin#26699 and documented inclusions/omissions in the commit message and PR body.

  • Included: dc1cc1c (selected-coin balance bugfix), 74eac3a (useAvailableBalance test), helper refactors adapted to Dash, and 68eed5d pieces (QMessageBox(this), ConfirmSend StandardButton, watch-only PSBT test).
  • Omitted with behavioral justification: cd98b717 (watch-only via WalletModel::getAvailableBalance cache). Dash does not have that model method / gui#598 cache path. Watch-only spendability for private-keys-disabled wallets already goes through wallet().getAvailableBalance + fAllowWatchOnly in updateCoinControlState / useAvailableBalance, and the send dialog already labels watch_only_balance. Introducing the model cache API solely for that one-liner is out of scope for this prereq batch.

2. Intermediate commits not buildable
Folded the BlockInfo bench adaptation into the bitcoin#25685 merge commit itself (kernel::MakeBlockInfo). Verified make -C src bench/bench_dash at that commit succeeds (object + link). The separate trailing “Dash adaptations for bitcoin#25685” commit is gone. CompactSize adaptation for bitcoin#25647 remains its own follow-up (it only touches spend path/tests and was already buildable).

3. Watch-only PSBT test
Added adapted TestGUIWatchOnly covering the user-visible Create Unsigned / clipboard PSBT path on a legacy private-keys-disabled wallet, plus watch-only balance label check. (Runs wherever WalletTests is not skipped by the macOS minimal guard.)

4. Minor GUI refactors
QMessageBox msgBox(this) and ConfirmSend confirm_type landed with the expanded bitcoin#26699 commit.

Old head bc52f0a9c79 was replaced by a8c5f714bab (5 commits, all signed). The automated backport verification should rerun against this exact head.

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

This pull request has conflicts, please rebase.

4fef534 wallet: use GetChange() when computing waste (S3RK)
87e0ef9 wallet: use GetChange() in tx building (S3RK)
15e97a6 wallet: add SelectionResult::GetChange (S3RK)
72cad28 wallet: calculate and store min_viable_change (S3RK)
e3210a7 wallet: account for preselected inputs in target (S3RK)
f8e7963 wallet: add SelectionResult::Merge (S3RK)
06f558e wallet: accurate SelectionResult::m_target (S3RK)
c8cf08e wallet: ensure m_min_change_target always covers change fee (S3RK)

Pull request description:

  Benefits:
  1. more accurate waste calculation for knapsack. Waste calculation is now consistent with tx building code. Before we always assumed change for knapsack even when the solution is changeless4.
  2. simpler tx building code. Only create change output when it's needed
  3. makes it easier to correctly account for fees for CPFP inputs (should be done in a follow up)

  In the first three commits we fix the code to accurately track selection target in `SelectionResult::m_target`
  Then we introduce new variable `min_change` that represents the minimum viable change amount
  Then we introduce `SelectionResult::GetChange()` which incapsulates dropping change for fee logic and uses correct values of `SelectionResult::m_target`
  Then we use `SelectionResult::GetChange()` in both tx building and waste calculation code

  This PR is a refactoring and shouldn't change the behaviour.
  There is only one known small change (arguably a bug fix). Before we dropped change output if it's smaller than `cost_of_change` after paying change fees. This is incorrect as `cost_of_change` already includes `change_fee`.

ACKs for top commit:
  achow101:
    ACK 4fef534
  Xekyo:
    crACK 4fef534
  furszy:
    Code review ACK 4fef534
  w0xlt:
    ACK bitcoin@4fef534

Tree-SHA512: 31a7455d4129bc39a444da0f16ad478d690d4d9627b2b8fdb5605facc6488171926bf02f5d7d9a545b2b59efafcf5bb3d404005e4da15c7b44b3f7d441afb941

(cherry picked from commit 2bd9aa5)
…g target

d0d9cf7 test: Check external coin effective value is used in CoinSelection (Aurèle Oulès)
76b79c1 wallet: Use correct effective value when checking target (Aurèle Oulès)

Pull request description:

  Fixes bitcoin#26185. The following assert failed because it was not checked in the parent function.

  https://github.com/bitcoin/bitcoin/blob/2bd9aa5a44b88c866c4d98f8a7bf7154049cba31/src/wallet/coinselection.cpp#L391

ACKs for top commit:
  glozow:
    reACK d0d9cf7
  furszy:
    ACK d0d9cf7

Tree-SHA512: e126daba1115e9d143f2a582c6953e7ea55e96853b6e819c7744fd7a23668f7d9854681d43ef55d8774655bc54e7e87c1c9fccd746d9e30fbf3caa82ef808ae9

(cherry picked from commit cda6c79)
@thepastaclaw
thepastaclaw force-pushed the backport-7400-prereq-coin-selection branch from a8c5f71 to 182745c Compare August 8, 2026 02:59
@thepastaclaw

Copy link
Copy Markdown
Author

Thanks — I addressed all four findings and force-pushed the corrected series at 182745cf320.

1. Undeclared partial bitcoin#26699

The commit is now partial Merge bitcoin/bitcoin#26699, with included and omitted upstream commits documented in both its commit message and the PR body. The only behavior commit intentionally omitted is cd98b717398: it fixes Bitcoin Core’s cached WalletModel::getAvailableBalance path, which Dash never landed. Dash’s corresponding watch-only flow already uses wallet().getAvailableBalance(coin_control) with fAllowWatchOnly, and SendCoinsDialog::setBalance already displays watch_only_balance.

2. Intermediate buildability

The kernel::MakeBlockInfo benchmark adaptation is folded into the bitcoin#25685 commit (f09f7d71b7c), where the benchmark source is first registered. I independently built bench/bench_dash at that commit, so the three previously broken intermediate states are gone.

3. Watch-only PSBT coverage

Added an adapted TestGUIWatchOnly covering a legacy private-keys-disabled wallet, watch-only balance display, Create Unsigned, clipboard PSBT output, and PSBT decoding.

4. Minor GUI pieces

Included QMessageBox msgBox(this) and the ConfirmSend(QMessageBox::StandardButton) refactor from the upstream series.

I also rebased the rewritten five-commit series onto current develop (e25f9925df4) because the PR had a real merge conflict. The conflict resolution retains both develop’s CoinJoin autobackup-tooltip coverage and the new watch-only PSBT test. All five commits are signed.

Validation at 182745cf320:

  • built test_dash, bench_dash, and test_dash-qt
  • focused coinselector_tests,spend_tests,wallet_tests: 34 cases, no errors
  • both WalletCreateTx.* benchmarks passed
  • Qt -platform minimal passed; macOS still skips the WalletTests body under the existing platform guard, so Linux CI is the authoritative execution of TestGUIWatchOnly
  • git diff --check, range-diff, signature checks, and merge-tree checks are clean

Rerunning exact-head backport verification now.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Final validation — Codex + Sonnet

All three specialist lanes (general, dash-core-commit-history, backport-reviewer) report zero findings at head 182745c, and independent verification confirms the sole prior finding is genuinely fixed: commit f09f7d7 now both registers src/bench/wallet_create_tx.cpp in bench_bench_dash_SOURCES and calls wallet.blockConnected(kernel::MakeBlockInfo(pindex, &block)) at that same commit, matching CWallet's single-argument blockConnected(const interfaces::BlockInfo&) signature — verified directly via git show f09f7d71b7c:src/bench/wallet_create_tx.cpp and git show f09f7d71b7c:src/wallet/wallet.h. No new defects were introduced by the rebase/rewrite, and the five-commit stack (four faithful Bitcoin Core wallet backports plus one well-documented Dash CompactSize adaptation) is clean, bisectable, and independently buildable at every commit.
Source: reviewer backend model gpt-5.6-sol (Codex general, dash-core-commit-history, backport-reviewer) and claude-sonnet-5 (Sonnet general, dash-core-commit-history, backport-reviewer); final verifier backend model claude-sonnet-5. openclaw-agent/cliproxy/gpt-5.6-sol is orchestration-only and is not reviewer evidence.

Review provenance

  • Codex reviewers: gpt-5.6-sol — general (completed), gpt-5.6-sol — dash-core-commit-history (completed), gpt-5.6-sol — backport-reviewer (completed)
  • Verifier: claude-sonnet-5 — final-verifier
  • Sonnet reviewers: claude-sonnet-5 — general (completed), claude-sonnet-5 — dash-core-commit-history (completed), claude-sonnet-5 — backport-reviewer (completed)

Canonical verifier result: APPROVE. GitHub does not allow authors to approve or request changes on their own pull requests, so this review was submitted using COMMENT transport. The findings and blocker status above are unchanged.

@thepastaclaw

Copy link
Copy Markdown
Author

CI triage for current head 182745cf320: the failed linux64_asan-build / Build source job is unrelated to this PR and the branch is proper as-is.

All Qt tests pass, after which LeakSanitizer reports 86 bytes in four allocations from Qt's QDBusConnectionManager::executeConnectionRequest. The identical stack, allocation count, and byte count already fail on develop at e25f9925df4 in run 31222050806 / job 93009844804; that commit is also this PR's merge base. The PR's failed job therefore does not indicate a regression in the wallet coin-selection changes.

Tracked the pre-existing ASan/Qt DBus failure in #7561. Per the unrelated-CI guardrail, no commit, push, rebase, or rerun was performed on this PR branch.

…pre-set-inputs fetching responsibility from Coin Selection

3fcb545 bench: benchmark transaction creation process (furszy)
a8a7534 wallet: SelectCoins, return early if target is covered by preset-inputs (furszy)
f41712a wallet: simplify preset inputs selection target check (furszy)
5baedc3 wallet: remove fetch pre-selected-inputs responsibility from SelectCoins (furszy)
295852f wallet: encapsulate pre-selected-inputs lookup into its own function (furszy)
37e7887 wallet: skip manually selected coins from 'AvailableCoins' result (furszy)
94c0766 wallet: skip available coins fetch if "other inputs" are disallowed (furszy)

Dash adaptations (folded for bisectability, so this commit builds on its own
without a follow-up adaptation commit):

- bench/wallet_create_tx.cpp: Dash's CWallet constructor takes a CoinJoin
  loader, SetupDescriptorScriptPubKeyMans() takes mnemonic and mnemonic
  passphrase arguments, and CreateTransaction() takes wallet::RANDOM_CHANGE_POSITION
  rather than a bare -1.
- bench/wallet_create_tx.cpp and test/util/wallet.{h,cpp}: Dash only supports
  OutputType::LEGACY, so getNewDestination() takes no output type and the
  benchmark drops upstream's output_type parameter.
- bench/wallet_create_tx.cpp: Dash's BlockManager::AddToBlockIndex() takes the
  block hash as an explicit argument.
- bench/wallet_create_tx.cpp: Dash's CoinsResult exposes legacy/other vectors
  instead of a map keyed by OutputType, so the pre-selected inputs are read
  from res.legacy rather than res.coins.at(output_type).
- wallet/spend.cpp: TrimPreSelectedInputs() keeps Dash's
  CCoinControl::fRequireAllInputs=false behaviour, which upstream does not
  have, working on top of the new "return early when the preset inputs already
  cover the target" path. The trimmed subset is now walked in
  PreSelectedInputs::coins order (std::set<COutput>, ordered by outpoint)
  instead of the previous AvailableCoins order. The invariant is unchanged:
  the trimmed set still covers the target, and it is only ever computed on a
  preset set that already covers it. Which particular subset is chosen may
  differ, so coinselector_tests' Dash-specific minimum_inputs_test now asserts
  the input count and that the target is covered, rather than one exact
  selected value.
- rpc/evo.cpp: upstream flips the CCoinControl::m_allow_other_inputs default
  from false to true, so FundSpecialTx() now sets it to false explicitly to
  keep funding restricted to the requested address. Upstream makes the
  equivalent adjustment for the Qt send dialog.

Pull request description:

  #### # Context (Current Flow on Master)

  In the transaction creation process, in order to select which coins the new transaction will spend,
  we first obtain all the available coins known by the wallet, which means walking-through the
  wallet txes map, gathering the ones that fulfill certain spendability requirements in a vector.

  This coins vector is then provided to the Coin Selection process, which first checks if the user
  has manually selected any input (which could be internal, aka known by the wallet, or external),
  and if it does, it fetches them by searching each of them inside the wallet and/or inside the
  Coin Control external tx data.

  Then, after finding the pre-selected-inputs and gathering them in a vector, the Coin Selection
  process walks-through the entire available coins vector once more just to erase coins that are
  in both vectors. So the Coin Selection process doesn’t pick them twice (duplicate inputs inside
  the same transaction).

  #### # Process Workflow Changes

  Now, a new method, `FetchCoins` will be responsible for:
  1) Lookup the user pre-selected-inputs (which can be internal or external).
  2) And, fetch the available coins in the wallet (excluding the already fetched ones).

  Which will occur prior to the Coin Selection process. Which allows us to never include the
  pre-selected-inputs inside the available coins vector in the first place, as well as doing other
  nice improvements (written below).

  So, Coin Selection can perform its main responsibility without mixing it with having to fetch
  internal/external coins nor any slow and unneeded duplicate coins verification.

  #### # Summarizing the Improvements:

  1) If any pre-selected-input lookup fail, the process will return the error right away.
  2) The pre-selected-inputs lookup failure causes are properly described on the return error.
  3) Faster Coin Selection: no longer need to remove the pre-set inputs from the available coins vector.
  4) Faster transaction creation for transactions that only use manually selected inputs.

ACKs for top commit:
  S3RK:
    Code Review ACK 3fcb545
  achow101:
    ACK 3fcb545
  aureleoules:
    reACK 3fcb545

Tree-SHA512: 42f833e92f40c348007ca565a4c98039e6f1ff25d8322bc2b27115824744779baf0b0a38452e4e2cdcba45076473f1028079bbd0f670020481ec5d3db42e4731

(cherry picked from commit f37bd15)
… skips selected coins

Included upstream commits (adapted to Dash):
dc1cc1c gui: bugfix, getAvailableBalance skips selected coins (furszy)
74eac3a test: add coverage for 'useAvailableBalance' functionality (furszy)
2f76ac0 test,gui: decouple chain and wallet initialization from test case (furszy)
306aab5 test,gui: decouple widgets and model into a MiniGui struct (furszy)
68eed5d test,gui: add coverage for PSBT creation on legacy watch-only wallets (furszy)

BACKPORT NOTE (partial):
Intentionally omitted:
  cd98b71 gui: 'getAvailableBalance', include watch only balance

Why omitted:
  Upstream bitcoin#26687 fixed WalletModel::getAvailableBalance so the cached-balance
  path (from bitcoin-core/gui#598) also adds watch_only_balance when private
  keys are disabled. Dash never landed that WalletModel::getAvailableBalance
  cached path. prepareTransaction and useAvailableBalance both call
  wallet().getAvailableBalance(coin_control) after updateCoinControlState /
  useAvailableBalance set fAllowWatchOnly for private-keys-disabled wallets.
  AvailableCoins already includes solvable watch-only outputs under that flag,
  and SendCoinsDialog::setBalance already displays watch_only_balance for
  legacy private-keys-disabled wallets. Porting cd98b71 would require
  introducing the model-level cache API just to re-express behavior Dash
  already has on the wallet-interface path; that is out of scope for this
  prerequisite batch.

Included with Dash adaptations:
  - Core bugfix: interfaces::Wallet::getAvailableBalance sums selected
    inputs via FetchSelectedInputs and optional AvailableCoins when other
    inputs are allowed (regression from bitcoin#25685).
  - useAvailableBalance coverage via VerifyUseAvailableBalance.
  - presentPSBT QMessageBox parented to the dialog (msgBox(this)).
  - ConfirmSend accepts QMessageBox::StandardButton so "Create Unsigned"
    (Save) can be driven from tests.
  - Adapted legacy watch-only PSBT GUI test (TestGUIWatchOnly): constructs a
    private-keys-disabled legacy wallet with ImportPubKeys, asserts the send
    dialog shows watch_only_balance, clicks Create Unsigned, and decodes the
    clipboard PSBT. Dash CWallet requires the CoinJoin loader; no PlatformStyle
    constructors; DecodeBase64PSBT used for decoding. MiniGUI was not ported
    wholesale because Dash SendCoinsDialog/TransactionView/WalletModel
    constructors differ; SyncUpWallet + SetupLegacyWatchOnlyWallet helpers
    provide the same separation of concerns.

Pull request description:

  Fixes bitcoin-core/gui#688 and bitcoin#26687.

  First Issue Description (bitcoin-core/gui#688):

  The previous behavior for getAvailableBalance, when the coin control had selected coins, was to return the sum of them. Instead, we are currently returning the wallet's available total balance minus the selected coins total amount.

  Reason:
  Missed to update the GetAvailableBalance function to include the coin control selected coins on bitcoin#25685.

  Context:
  Since bitcoin#25685 we skip the selected coins inside AvailableCoins.

  Places Where This Generates Issues (only when the user manually select coins via coin control):
  1) The GUI balance check prior the transaction creation process.
  2) The GUI "useAvailableBalance" functionality.

  Second Issue Description (bitcoin#26687):
  Upstream fixed WalletModel cached getAvailableBalance for watch-only. Dash
  does not use that path; see BACKPORT NOTE above.

ACKs for top commit:
  Sjors:
    tACK 68eed5d
  achow101:
    ACK 68eed5d
  theStack:
    ACK 68eed5d

Tree-SHA512: 674f3e050024dabda2ff4a04b9ed3750cf54a040527204c920e1e38bd3d7f5fd4d096e4fd08a0fea84ee6abb5070f022b5c0d450c58fd30202ef05ebfd7af6d3

(cherry picked from commit 27dcc07)
…ing)

Dash-specific follow-up to the bitcoin#25647 backport. Coin selection estimates CompactSize prefixes before the final input and output counts are known. Wider vin-count prefixes at 253 or 65,536 inputs, or a wider vout-count prefix when change raises 252 or 65,535 recipients across a boundary, can leave the selected value a few duffs short of the accurately measured final fee.

After selecting inputs, recompute the non-input fee with the actual vin-count prefix and the prospective change output count, then retry selection if the target must increase. This keeps change-bearing and exact no-change transactions on the same path and preserves the final Fee needed > fee paid check for unrelated accounting defects.

Add regression coverage for 252 inputs, 253 inputs with change, an exact-target 253-input transaction without change, and 252 recipients plus change.
@thepastaclaw
thepastaclaw force-pushed the backport-7400-prereq-coin-selection branch from 182745c to 66d72b4 Compare August 8, 2026 17:33

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Final validation — Codex + Sonnet

This delta (182745c..66d72b4) contains exactly two one-line changes: a correct EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet) annotation restored on FetchSelectedInputs's definition in spend.cpp (matches the header declaration and the sole caller, which already holds cs_wallet), and a benchmark comment edit changing 'minus 100 due COINBASE_MATURITY' to 'minus 200 due COINBASE_MATURITY' in wallet_create_tx.cpp. I verified against bitcoin/bitcoin@f37bd15 that upstream's own merged text says 'minus 200' despite upstream's own COINBASE_MATURITY also being 100 — this is a pre-existing upstream Bitcoin Core comment inaccuracy carried over verbatim, not a Dash-introduced error, so per the backport review policy against re-reviewing/flagging upstream text it is dropped. No in-scope findings remain; the PR is clean and mergeable.
Source: reviewer backend model gpt-5.6-sol (Codex general, dash-core-commit-history, backport-reviewer) and claude-sonnet-5 (Sonnet general, dash-core-commit-history, backport-reviewer); final verifier backend model claude-sonnet-5. openclaw-agent/cliproxy/gpt-5.6-sol is orchestration-only and is not reviewer evidence.

Review provenance

  • Codex reviewers: gpt-5.6-sol — general (completed), gpt-5.6-sol — dash-core-commit-history (completed), gpt-5.6-sol — backport-reviewer (completed)
  • Verifier: claude-sonnet-5 — final-verifier
  • Sonnet reviewers: claude-sonnet-5 — general (completed), claude-sonnet-5 — dash-core-commit-history (completed), claude-sonnet-5 — backport-reviewer (completed)

Canonical verifier result: APPROVE. GitHub does not allow authors to approve or request changes on their own pull requests, so this review was submitted using COMMENT transport. The findings and blocker status above are unchanged.

@thepastaclaw

Copy link
Copy Markdown
Author

CI triage for current head 66d72b4aa17c: the failed linux64_asan-build / Build source job is unrelated to this PR, and the branch is proper as-is.

All Qt tests pass, then LeakSanitizer reports 86 bytes in four allocations from Qt's QDBusConnectionManager::executeConnectionRequest. The same stack, allocation count, and byte count fail on both this PR's merge base (develop at e25f9925df4, job 93009844804) and current develop at 5c44379d739, job 93140878081. GitHub's PR file list is wallet coin-selection code/tests only, so this is not a regression from the PR.

The pre-existing Qt/DBus LSAN failure remains tracked in #7561. Per the unrelated-CI guardrail, no commit, push, rebase, or rerun was performed on this branch.

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