Skip to content

fix: recover witness UTXOs orphaned by a lost consignment#90

Open
berserker-systems wants to merge 1 commit into
RGB-Tools:masterfrom
deedsats:s/receive_bug
Open

fix: recover witness UTXOs orphaned by a lost consignment#90
berserker-systems wants to merge 1 commit into
RGB-Tools:masterfrom
deedsats:s/receive_bug

Conversation

@berserker-systems

Copy link
Copy Markdown
Contributor

A witness invoice can be paid on-chain without its consignment ever being delivered: the proxy can drop a successfully posted consignment (e.g. it restarts and loses its file store), and the sender may then pay the same invoice again with a new TX whose consignment does arrive. This only happens in donation mode, where the sender broadcasts right after posting the consignment; otherwise it waits for the recipient's ACK (which requires the recipient to already hold the consignment) before broadcasting, so a lost consignment cannot leave a confirmed orphan. The first payment's UTXO is quarantined with pending_witness set, but no transfer ever references its TXID, so the flag is never cleared: the UTXO stays unspendable forever and its allocation, received as part of the second consignment's history, lives only in the RGB runtime and never makes it into the DB (understated balance, sends failing with InsufficientAllocationSlots).

On refresh, look for pending witness TXOs with no transfer referencing their TXID. When the runtime already holds allocations for one there is nothing left to wait for: save them to the DB as a settled transfer and clear the flag, so both the sats and the assets become usable again. TXOs the runtime knows nothing about are left as-is, since spending them would burn whatever a still-pending consignment may deliver.

A witness invoice can be paid on-chain without its consignment ever
being delivered: the proxy can drop a successfully posted consignment
(e.g. it restarts and loses its file store), and the sender may then pay
the same invoice again with a new TX whose consignment does arrive. This
only happens in donation mode, where the sender broadcasts right after
posting the consignment; otherwise it waits for the recipient's ACK
(which requires the recipient to already hold the consignment) before
broadcasting, so a lost consignment cannot leave a confirmed orphan. The
first payment's UTXO is quarantined with pending_witness set, but no
transfer ever references its TXID, so the flag is never cleared: the
UTXO stays unspendable forever and its allocation, received as part of
the second consignment's history, lives only in the RGB runtime and
never makes it into the DB (understated balance, sends failing with
InsufficientAllocationSlots).

On refresh, look for pending witness TXOs with no transfer referencing
their TXID. When the runtime already holds allocations for one there is
nothing left to wait for: save them to the DB as a settled transfer and
clear the flag, so both the sats and the assets become usable again.
TXOs the runtime knows nothing about are left as-is, since spending them
would burn whatever a still-pending consignment may deliver.
@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.70330% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.75%. Comparing base (1e6353d) to head (625fa39).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
src/wallet/online.rs 96.70% 3 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master      #90   +/-   ##
=======================================
  Coverage   95.74%   95.75%           
=======================================
  Files          23       23           
  Lines       12835    12924   +89     
=======================================
+ Hits        12289    12375   +86     
- Misses        546      549    +3     
Flag Coverage Δ
rust 95.75% <96.70%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@zoedberg

Copy link
Copy Markdown
Member

Thank you for this PR and for the clear write-up of the orphan witness UTXO scenario. The problem is real and the test covers the failure path well.

We're about to release a large set of changes, including a refactor of how the proxy integration works. We'd prefer not to merge this now, since the surrounding code is likely to change significantly. Could you please rebase this PR once those changes are public? We can then reassess whether this reconciliation logic still works.

On that point: the transport proxy is chosen by the receiver, and in practice it should ideally be infrastructure the receiver controls. Relying on third-party proxies is possible, but should be done carefully: users should understand and trust each proxy's data retention and availability guarantees. With a receiver-owned proxy, the failure mode this PR addresses (a consignment posted successfully but later unavailable) becomes much less likely, and we may find that explicit orphan reconciliation is unnecessary altogether.

That said, donation mode remains inherently risky regardless of proxy setup. It broadcasts before the recipient has accepted the consignment, which creates several ways things can go wrong, for example the recipient may consider the consignment invalid and refuse it. Wallets using donation mode should treat it as an explicit, operator-controlled option, not a default path, and be aware of these risks.

Thanks again for the contribution. We'll revisit this once our upcoming changes are out.

@berserker-systems

Copy link
Copy Markdown
Contributor Author

@zoedberg Thank you. Please ping or e-mail me, when we can continue here.
Quick note: this also happens, when only btc gets send to an rgb-invoice address.

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