Resolve #3343: report whether missing UTXOs are absent from the node or excluded by selection

Resolve #3343 (closed)

This MR implements the changes described in issue #3343 (closed).

Summary

Investigation (job 16355041548, MIGRATE:1975)

  • Keygen created the migrating vault at 23:14, when DOGE was at height about 1945. All of its UTXOs arrived above that height.
  • dogecoind stopped at 23:25 (bifrost logged no such host after height 2418). The container restarted, and bifrost could not reach dogecoind again until 23:49. Bifrost then logged re-org detected at height 2418, and solvency reported wallet_amount=0 against asgard_amount=2448975937391.
  • The restarted node rolled back its unflushed blocks, and the entrypoint mined a fresh 1000-block initial batch on a new branch. The vault funding existed only on the orphaned branch.
  • Conclusion: the coins were absent from the node's view. Selection rules did not exclude them, and this was not the wallet-tracking gap from #3084 / #2948.

Root-cause fix

The container restart is fixed by the #3345 (closed) dogecoind entrypoint change in !5130 (merged):

  • dogecoind keeps running through slow RPCs.
  • SIGTERM is forwarded so state is flushed on stop.
  • After a restart, only the initial blocks that are still missing are mined.

This MR does not duplicate that change.

Diagnostics (this MR)

The old shortfall error counted only the selected UTXOs, so 0 available from 0 UTXOs could not show whether the node listed nothing or selection dropped everything. getUtxoToSpend now reports one of two cases:

  • Absent: node lists no UTXOs for <addr> (coins absent from the node view: unfunded, not imported, or removed by a reorg)
  • Excluded: the listed UTXO count and value, plus how many UTXOs were excluded for each reason: invalid script, unconfirmed or dust not from asgard, mempool chain limit, failed spent-cache check, or already spent.

Selection behaviour is unchanged.

Tests

  • Regression tests in bifrost/pkg/chainclients/utxo/utxo_selection_test.go:
    • an empty vault gives the absent message
    • listed coins that are excluded give the per-reason counts
    • a funded vault with a confirmed UTXO covering the migration amount is selected successfully
  • The new tests pass under both the mocknet and mainnet build tags.
  • make test passes.
  • golangci-lint reports nothing new.

🤖 Generated with Claude Code

Merge request reports

Loading
Loading