Skip to content

fix(mint): delete orphan blank outputs on melt change early-return#1016

Open
gorrdy wants to merge 3 commits into
cashubtc:mainfrom
gorrdy:fix/melt-orphan-blank-outputs
Open

fix(mint): delete orphan blank outputs on melt change early-return#1016
gorrdy wants to merge 3 commits into
cashubtc:mainfrom
gorrdy:fix/melt-orphan-blank-outputs

Conversation

@gorrdy

@gorrdy gorrdy commented May 25, 2026

Copy link
Copy Markdown
Contributor

Summary

  • _generate_change_promises deletes the wallet's blank NUT-08 outputs
    from the promises table after signing them, but the early-return
    branch (overpaid_fee <= 0 or no outputs supplied) skipped that
    cleanup. Those rows were left behind with c_ IS NULL and collided with
    later operations that re-derive the same B_ — most visibly NUT-13
    seed restore, which would surface as OutputsArePendingError.
  • Apply the same delete_blinded_messages_melt_id cleanup in the
    early-return branch.
  • Soften the overpaid_fee < 0 log from error to debug with a
    clearer message. In practice this is reachable whenever the LN backend
    reports a fee greater than the wallet-provided reserve (e.g. an LNbits
    backend skimming a service fee on top of the routing fee), so the
    original "this should not happen" wording is misleading and the
    error-level severity is noisy.
  • Add a parametrized regression test covering both overpaid_fee == 0
    and overpaid_fee < 0 cases.

Test plan

  • poetry run pytest tests/mint/test_mint_melt.py -k early_return -v passes both parametrize cases (overpaid_fee_zero,
    overpaid_fee_negative).
  • Same tests fail on main (without the ledger fix) — orphan rows
    assertion trips.
  • Full melt suite still green: poetry run pytest tests/mint/test_mint_melt.py.

@codecov

codecov Bot commented May 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.07%. Comparing base (2376e47) to head (d82ebf3).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1016      +/-   ##
==========================================
+ Coverage   75.04%   75.07%   +0.03%     
==========================================
  Files         111      111              
  Lines       12244    12246       +2     
==========================================
+ Hits         9188     9194       +6     
+ Misses       3056     3052       -4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@a1denvalu3

Copy link
Copy Markdown
Collaborator

looks like the CI is failing

@KvngMikey

Copy link
Copy Markdown
Contributor

@gorrdy , you've got this test failing:

`FAILED tests/mint/test_mint.py::test_generate_change_promises_zero_fee_deletes_all_blanks - assert 0 == 4

  • where 0 = len([])`

Comment thread cashu/mint/ledger.py Outdated
# Clean up the blank outputs the wallet sent for fee return; otherwise
# they remain in `promises` with c_ IS NULL and collide with later
# operations that re-derive the same B_ (e.g. NUT-13 seed restore).
if melt_id:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

if melt_id and outputs is not None: # only clean up if something was stored

gorrdy added 3 commits June 1, 2026 08:35
`_generate_change_promises` already deletes the wallet's blank NUT-08
outputs from `promises` after signing them. But when the function takes
the early-return branch (overpaid_fee <= 0 or no outputs supplied) those
rows were left behind with `c_ IS NULL` — and later operations that
re-derive the same `B_` (notably NUT-13 seed restore) collide with them
and surface as `OutputsArePendingError`.

Also soften the negative-overpaid_fee log to debug with a message that
explains the case: in practice the backend can report a fee greater
than the wallet-provided reserve (e.g. an LNbits backend skimming a
service fee on top of the routing fee), so an error-level log here is
noisy and misleading.

Adds two regression tests covering both the `overpaid_fee == 0` and
`overpaid_fee < 0` branches.
The two `overpaid_fee == 0` / `overpaid_fee < 0` cases differ only in
one integer offset, so collapse them into a single parametrized test
and drop the `_run_melt_with_patched_fee` helper / `INVOICE_64_SAT`
module constant — the invoice is inlined to match the style of the
other tests in this file.
…lanks with fix

The test was added in 9fed0f0 (PR cashubtc#795) with a name asserting deletion
("..._deletes_all_blanks") but assertions baked in the then-current bug
where the overpaid_fee <= 0 early-return skipped cleanup. This PR
completes the fix; flip the assertions to match the fixed behavior the
test name already claimed.

Also apply KvngMikey's review suggestion: guard the cleanup with
`outputs is not None` so we don't issue a no-op DELETE when the wallet
sent no blanks to begin with.
@gorrdy gorrdy force-pushed the fix/melt-orphan-blank-outputs branch from 226048f to d82ebf3 Compare June 1, 2026 08:35
@gorrdy

gorrdy commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

Should be ok now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

3 participants