AI-generated analysisPublished automatically and not human-verified. Validated context appears in community notes below.
← Watch feed
Moderate 60 Cryptographic libraries

wallet2: exclude outputs from an unconfirmed send in reserve proofs

Public commit record

What the developer wrote

Authored by Cole Munz

81/100 · Strong
wallet2: exclude outputs from an unconfirmed send in reserve proofs

get_reserve_proof picks outputs with is_spent(td, true). That only
counts an output spent once its spending tx has confirmed. commit_tx
marks an output spent as soon as the tx goes to the daemon, well before
it confirms. spent_height stays 0 until then. So an output already used
as input to a pending send still passes as unspent here.

A wallet can prove reserve over outputs it already committed to a
pending send. check_reserve_proof reports them unspent, but they are
gone as soon as that send confirms. Reported with a stagenet repro in
#6595.

Switched to is_spent(td, false), which the wallet's default balance
already uses for this reason. Both the zero-balance guard and the
account_minreserve check used to go through balance_all()/balance(),
which fold in change and self-transfer amounts from our own
unconfirmed txs (m_unconfirmed_txs, m_unconfirmed_payments). Those
amounts have no matching row in m_transfers yet, so once an output is
tied up in a pending send, is_spent(td, false) drops it from
selected_transfers while the pending change keeps the balance call
non-zero, and the guard passes over an empty or thinner selection than
it should. Now both guards sum selected_transfers directly, computed
once, so they match exactly what the proof is built over.
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Links an issue, advisory, or supporting reference
The short version

What changed, and why it matters

This fix corrects a Monero wallet bug where a user could generate a cryptographic 'reserve proof' claiming they still owned money that they had already committed to spending. Before the patch, the wallet only excluded outputs from the proof once the spending transaction was confirmed on the blockchain. Because the wallet marks outputs as spent earlier—when the transaction is first submitted to the network—a user could honestly but incorrectly prove reserve over outputs that would disappear as soon as the pending send confirmed. The patch makes the wallet treat pending-spent outputs as unavailable and checks the exact outputs selected for the proof rather than a broader balance figure that could include pending change.

Recommended action

Reviewers should verify that is_spent(td, false) correctly reflects the wallet's intended behavior for unconfirmed outgoing spends and that no other proof-generation paths still rely on balance_all(true)/balance(..., true) in a similar way. Consider adding regression tests covering reserve proofs with unconfirmed spends and account_minreserve edge cases.

Security signals we found

01

Incorrect spent-state check allows reserve proof over outputs committed to a pending transaction

02

Balance-based guard mismatched against actual selected outputs due to unconfirmed change/self-transfer amounts

03

Fix aligns reserve proof semantics with wallet's default balance behavior

04

Reported with a stagenet reproduction in issue #6595

Risk score

Why this scored 60/100

Our methodology →
Potential impact 18/30
Exploitability 12/25
Stealth signal 10/15
Affected reach 8/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.