Skip to content

key-wallet: committed-range rescans (#866) can redeliver funding after its observed-spent entry was pruned #896

Description

@xdustinface

Context

PR #851 fixes #649 with a wallet-level observed_spent_outpoints map: every block-observed spend is recorded, and update_utxos never inserts a UTXO whose outpoint is in the map. Entries are pruned at the finality boundary min(chainlock_height, synced_height).

The pruning rule is sound on current dev because every redelivery path is a full replay: a full rescan redelivers the spending block too (BIP158 filters match it via the prevout script), so the map repopulates before the funding is re-processed, in either order.

The gap

synced_height acts as a coverage certificate relative to the script set at scan time. #866 introduces rescans of already-committed filter ranges for newly derived scripts (gap-limit growth, late-added accounts). Such a rescan can redeliver a funding transaction for a script that was unknown when the range was first scanned, while the spend of that coin sits below the finality boundary and its observed-spent entry has already been pruned. The funding insert then resurrects a spent coin, reopening #649 for exactly the coins #866 recovers.

PR #851 originally tried to close the account-add slice of this with a synced_height rewind to birth plus a commit-time contiguity guard in dash-spv. That was dropped: the guard compared batch starts against absolute wallet birth instead of the scan anchor and bricked every checkpoint sync whose wallet birth lies below the anchor (infinite restart loop, 6421 restarts observed in field testing), and the rewind never covered #866's main redelivery source, gap-limit script growth.

What #866 (or a follow-up to it) must guarantee

For any committed-range rescan, one of:

  • redeliver spends before or together with fundings for the rescanned range (full-replay semantics, the map repopulates itself), or
  • pre-check redelivered fundings against recorded/finalized transactions AND the observed-spent map before inserting, or
  • lower the effective prune boundary for the duration of the rescan (a scoped, anchor-aware version of the rewind idea).

Related direction for the map itself

The map currently records every input of every transaction in every matched block, since ownership of an unseen prevout is unknowable at spend time. For P2PKH inputs the spender's public key is in the scriptSig, so the prevout script can be derived and recording could be narrowed to spends of own coins. That would shrink the map to roughly one entry per real wallet spend and reduce the surface #866 has to reason about.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions