Skip to content

wollet: Update::prune keeps rangeproofs of outputs paying scripts introduced by the update - #179

Open
ledegenbtc wants to merge 3 commits into
Blockstream:masterfrom
ledegenbtc:prune-keep-new-script-rangeproofs
Open

ledegenbtc wants to merge 3 commits into
Blockstream:masterfrom
ledegenbtc:prune-keep-new-script-rangeproofs

Conversation

@ledegenbtc

Copy link
Copy Markdown

Update::prune() decides which outputs belong to the wallet by looking at wallet.cache.paths, but that map is only filled when an update is applied (apply_update_inner). With the documented usage, prune the update and then apply it (see the "Update Pruning" section of the book and tests/prune.rs), the scripts introduced by the update being pruned are not in the map yet, so their outputs are treated as foreign and lose the whole witness, rangeproof included.

This is invisible when the wallet already synced before receiving funds, because the gap-limit scripts are already in the cache: that is what test_prune does through TestWollet::new. It shows up when a funded script is first seen by the update being pruned:

  • a wallet restored from its descriptor: the first full_scan carries the whole history and cache.paths is empty, so prune() behaves exactly like prune_witnesses(), contrary to its documentation;
  • a wallet whose derivation index jumps ahead, e.g. many addresses handed out and funded before they are scanned.

The affected outputs then have no in_utxo_rangeproof when spent, and unblind_utxos_with() / reunblind() cannot see them.

Fix: Update::prune() also treats the scripts in scripts_with_blinding_pubkey as wallet scripts. DownloadTxResult::prune takes a predicate instead of a map, so the (possibly large) paths map is not cloned at every sync.

Tests:

  • unit: test_update_prune_keeps_rangeproofs_of_new_scripts prunes the update_many_txs vector against a wallet that never synced and checks that the outputs paying the update's own scripts keep their rangeproofs, and that applying the pruned update yields the same utxos and balance. Fails on master.
  • integration: test_prune gains a wallet restored from the descriptor after the transactions exist: prune, apply, build a PSET and check that in_utxo_rangeproof is present. Fails on master.

Relates to #158 (found while pruning updates in production; numbers in that thread).

Update::prune() decides which outputs belong to the wallet by looking at
wallet.cache.paths, but that map is only filled when an update is
applied. With the documented usage (prune, then apply) the scripts
introduced by the update being pruned are not there yet, so the outputs
paying to them lose the whole witness, rangeproof included: at the first
sync of a wallet restored from its descriptor, prune() removes as much as
prune_witnesses(), and a wallet whose derivation index jumps ahead loses
the rangeproofs of the outputs found on the new scripts.

Also treat the scripts carried by the update (scripts_with_blinding_pubkey)
as wallet scripts. DownloadTxResult::prune takes a predicate instead of a
map so the paths map is not cloned at every sync.

The new unit test prunes the update_many_txs vector against a wallet that
never synced and fails on master.
Add to test_prune a wallet built from the descriptor after the
transactions exist: its first update is pruned before being applied.
Building a PSET must still find the input rangeproofs, which fails
without the previous commit.
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