Skip to content

Reorg never rewinds own-wallet spends: spent_height is never written #74

Description

@codewordneptune

Summary

On a reorg, roll_back undoes spend markers with:

UPDATE wallet_state_utxos SET spent_height = NULL, spent_txid = NULL, spent_in_block = NULL WHERE spent_height > ?

but wallet_state_utxos.spent_height is never assigned a non NULL value anywhere in the codebase, so this statement can never match a row. A spend recorded in a block that is later rolled back is never undone.

Watch only UTXOs are not affected. watch_only_utxos.spent_height is written in watch_only.rs:762, so the equivalent rollback on the next line of roll_back works. The asymmetry suggests an oversight rather than a deliberate choice.

Where

Spends on own UTXOs are recorded as two separate columns, neither of which is spent_height:

  • spent_in_block, wallet_state_table.rs:427
  • spent_txid, wallet_state_table.rs:439

The only statements naming wallet_state_utxos.spent_height are the schema definition and the rollback UPDATE itself.

Impact

balance.rs:101 treats a UTXO as unspent only when spent_in_block.is_none(). After a reorg drops the block that spent it, the marker stays set, so the UTXO is permanently excluded from the balance and from input selection, even though it is spendable on chain. Recovering it presumably needs a rescan or a reimport.

This is more than a display defect, since it affects which funds the wallet believes it can spend.

Proposed fix

Write spent_height at the same point as spent_in_block. The value is already in hand there: UtxoBlockInfo carries block_height, which balance.rs:33 already reads. With the column populated, the existing rollback works as written and no change to roll_back is needed.

Open question

Worth deciding separately what should happen to a pending record whose confirming block is rolled back. roll_back calls try_clean_pending_by_utxo, which deletes pending rows for UTXOs confirmed above the rollback height, but that keys on outputs. A send whose inputs were spent in the rolled back block may not be covered by it.

References

wallet_state_table.rs:427, 439, 560-600, watch_only.rs:762, balance.rs:101.

Split out from #44.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions