AI-generated analysisPublished automatically and not human-verified. Validated context appears in community notes below.
← Watch feed
Low 38 Monero

Privacy: randomized coin selection (BnB changeless + single random draw) (#3408)

Public commit record

What the developer wrote

Authored by Cindy

100/100 · Strong
Privacy: randomized coin selection (BnB changeless + single random draw) (#3408)

* Randomize coin selection with single random draw

The greedy selection walked the unspent pool in a predictable order (address generation order, then per-address server order), a fingerprint an analyst can exploit. Shuffle the pool before the accumulate-until-covered loop so the input set is chosen non-deterministically (single random draw). MWEB coins are still kept last.

Branch-and-bound (changeless exact match) is a larger follow-up.

* Add BnB + SRD coin selection primitives

Pure, wallet-independent coin selector: branch-and-bound for an exact
changeless match within the cost-of-change window, single-random-draw
fallback (shuffled, injectable RNG) when no exact match exists, and
selectCoins tying them together over effective values.

This is the basis for replacing the greedy accumulation in _createUTXOS
so sends produce no change when possible and a non-deterministic
selection otherwise. Validated with dart test (9 cases); wiring into
_createUTXOS is a separate step.

* Use BnB changeless matching in coin selection

Before the shuffled greedy walk, run branch-and-bound over effective
values (value minus per-input fee cost) looking for an input set whose
excess over amount plus fee stays below dust. When found, order those
inputs first and cap the walk at their count: the caller then computes
a change below dust, drops the change output, and absorbs the residue
into the fee, producing a changeless transaction with no residual
change fingerprint.

When no match exists, selection falls back to the shuffled pool, which
the greedy walk turns into a single random draw. BnB is skipped for
sendAll, forced input counts and pools holding MWEB coins.

changelessMatch filters non-positive effective values and maps indices
back to the original pool.

* Cover changeless fee bounds and BnB termination

Assert the end-to-end arithmetic the wallet performs around a
branch-and-bound match: with the 68/34/10 vBytes model, the leftover
the caller absorbs into the fee is never negative (no re-selection
loop) and never exceeds the dust limit (bounded fee overpay).

Also pin termination: a large pool with no possible match returns null
through the maxTries cap instead of exploring the full search tree.

* Use a secure RNG for coin selection randomness

The selection shuffle and the single-random-draw fallback exist to be
unpredictable, so seed them from Random.secure() instead of the default
PRNG, matching what Core and bdk use for coin selection. Pool sizes are
small, so the cost is negligible. Tests keep injecting a seeded Random
for determinism.

* Randomize RBF and payjoin input candidate order

Two secondary paths still consumed unspentCoins in wallet scan order
(address generation order, then per-address age), the same predictable
order removed from the main selection path:

- replaceByFee walked the unused-UTXO list in scan order when the fee
bump needed extra inputs
- the payjoin receiver handed input candidates to the payjoin library
in scan order, letting ties inside its selection mirror that order

Shuffle both with a secure RNG so no input-selection path exposes the
wallet's address or coin age ordering.

* ci: skip Linux PR build on forks (no secrets access)

* Account for script-type sizes in changeless matching

* Keep coin selection order stable within a transaction build
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Explains rationale or failure mode✓ Mentions testing or verification✓ Links an issue, advisory, or supporting reference✓ Names security-relevant behavior explicitly
The short version

What changed, and why it matters

This commit improves Bitcoin/Litecoin privacy in Cake Wallet by making coin selection less predictable. Previously, the wallet chose coins in a fixed order (based on address creation and age), which outside observers could use to fingerprint the wallet or trace its funds. The patch shuffles candidate coins using a secure random source, tries to build changeless transactions when possible, and also randomizes coin order in RBF fee-bump and PayJoin flows. It also adds a CI guard to skip Linux builds on fork pull requests because secrets are unavailable there.

Recommended action

No immediate action required; this is a privacy-hardening patch. Reviewers should verify that Random.secure() is available on all target platforms, that the BnB maxTries cap cannot be bypassed, and that the changeless leftover arithmetic holds for all supported script types and fee rates. Consider whether MWEB exclusion from changeless selection is correctly enforced.

Security signals we found

01

Predictable coin-selection ordering removed from main send path

02

Secure RNG (Random.secure()) used for selection shuffle and secondary paths

03

Changeless branch-and-bound reduces change-output fingerprinting

04

RBF and PayJoin input candidate ordering also randomized

05

Per-script-type vByte sizes accounted for in changeless matching

06

CI workflow guard added for fork PRs lacking secrets access

Risk score

Why this scored 38/100

Our methodology →
Potential impact 8/30
Exploitability 5/25
Stealth signal 6/15
Affected reach 7/15
Confidence 8/10
Evidence quality 4/5
Human-validated context

Community notes

Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.

No validated notes yet.

The AI analysis stands alone for now. Submit a note if you can add evidence or important context.