multi: refresh asset VTXOs through the round (client) - #1128
Conversation
Refreshing an asset-bearing VTXO forfeits the leaf and asks the same units back as a fixed-amount asset VTXO request, so the round reissues them through its asset transition. The wallet descriptor and the RPC preview learn the asset identity; the blanket refresh rejection is lifted for explicitly named targets while the catch-all selection still skips them.
The proof-of-funds input for a forfeited VTXO derived the default timeout path, whose witness program is the bare policy output. An asset leaf pays to the policy branched with its asset root, so the proof needs the composed control block, the same way boarding disclosures already build theirs.
The round store's ClientVTXO loader dropped the Taproot Asset columns, so a forfeited asset leaf looked like plain sats to the join-auth builder and its proof spent the wrong witness program.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4efe00666f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if vtxo.TaprootAssetRef != "" { | ||
| request.AssetRef = vtxo.TaprootAssetRef | ||
| request.AssetAmount = vtxo.TaprootAssetAmount | ||
| request.FixedAmount = true |
There was a problem hiding this comment.
Add a change leg for asset-only refresh batches
When the selected batch contains only asset VTXOs, every replacement is marked FixedAmount and none can become the fee-bearing change output. A single asset refresh therefore has input sats exactly equal to its fixed output and is rejected whenever the quoted operator fee is positive; two or more asset replacements are rejected even with a zero fee because designateChangeMarker skips every fixed output, leaving the multi-output intent with no IsChange marker. Add an ordinary Bitcoin change/funding leg or reject these unsupported selections before reserving them.
AGENTS.md reference: wallet/AGENTS.md:L40-L42
Useful? React with 👍 / 👎.
| // TaprootAssetRef is the canonical reference of the Taproot Asset | ||
| // this VTXO carries, empty for a Bitcoin-only VTXO. A refresh must | ||
| // reissue the same asset through the round's asset transition. | ||
| TaprootAssetRef string |
There was a problem hiding this comment.
Preserve the asset-root discriminator in wallet descriptors
For upgraded wallets containing the explicitly supported historical root-only asset rows, TaprootAssetRef is empty even though TaprootAssetRoot is non-nil. This descriptor therefore misclassifies such a VTXO as Bitcoin-only, and handleRefreshVTXOs emits a replacement request without AssetRef, AssetAmount, or FixedAmount; the refresh consequently cannot reissue the asset and may submit a destructive transition to an operator lacking conservation checks. Carry the root into this adapter and reject or recover metadata for root-only assets rather than using an empty reference as the Bitcoin discriminator.
Useful? React with 👍 / 👎.
1aa2174
into
darioAnongba/taproot-assets-oor-runtime
Client half of the asset leaf refresh, stacked on #1108. Operator counterpart: lumos#773 (see the PR list on the integration epic); design: https://taproot-assets-ark.lightning.wiki/.
A refresh forfeits an asset leaf and asks the same units back as a fixed-amount asset VTXO request, so the next round reissues them through its asset transition. Three changes make that work end to end:
handleRefreshVTXOsstamps them onto the new VTXO request withFixedAmountset: the seal quote can never shrink an asset carrier, and the request never acts as the change slot. The RPC preview accepts explicitly named asset targets; the catch-all--allselection still skips them, so a bulk Bitcoin refresh cannot pull an asset leaf along silently.ClientVTXOloader dropped the Taproot Asset columns, which made a forfeited asset leaf look like plain sats to the join-auth builder.Forfeit signing needed no changes: the VTXO actor already resolves its spend path through
EffectiveStandardSpendInfo, which extends the control block with the asset root.Verified by
TestRoundAssetRefreshin lumos: board, refresh with inventory-funded reissue, and an out-of-round spend from the refreshed leaf.