bdk icon indicating copy to clipboard operation
bdk copied to clipboard

AddressIndex improvements: LastUnused, FirstUnused, and get_batch_unused_addresses()

Open nickfarrow opened this issue 3 years ago • 13 comments

Description

  • Change AddressIndex::LastUnused to look back further than current_index
  • Add AddressIndex::FirstUnused
  • Add get_batch_unused_addresses

Notes to the reviewers

Builds upon https://github.com/bitcoindevkit/bdk/pull/522

Currently BDK supports address indexing via LastUnused, which will return the address with current_index if it is unused, otherwise it will return a New address.

With this current logic, if you get two new addresses A1 and A2 and use A2, then LastUnused will give you a New address rather than the unused A1.

In order to more consistently utilize unused addresses i've added a new function get_unused_key_indexes(keychain) which returns a vector of indexes for the unused addresses in that keychain. Making use of this function, LastUnused now returns the most recent address that has not yet been used, and New if all addresses have been used.

In some cases it may be desirable to utilize unused addresses that reside earlier in the keychain. i.e. AddressIndex::FirstUnused in this PR.

FirstUnused has the same caveat as LastUnused: that if the wallet has not yet detected an address has been used, it could return a used address.

Additionally a new public function get_batch_unused_addresses allows for retrieval of N unused addresses at once. Prioritizing unused addresses first, then populating the remaining with New addresses (like FirstUnused).

For example: if a wallet builds a transaction involving many New internal addresses but that transaction is never broadcast, then all of these addresses can now easily be used in a later transaction via get_batch_unused_addresses.

Checklists

All Submissions:

  • [X] I've signed all my commits
  • [X] I followed the contribution guidelines
  • [X] I ran cargo fmt and cargo clippy before committing

New Features:

  • [X] I've added tests for the new feature
  • [X] I've added docs for the new feature
  • [X] I've updated CHANGELOG.md

nickfarrow avatar Feb 16 '22 23:02 nickfarrow

approach ACK. Needs rebase.

LLFourn avatar Mar 15 '22 02:03 LLFourn

Code changes look great but you'll need to do another rebase and can you add a signing key to Github and sign your commits when you do the rebase also?

notmandatory avatar Apr 06 '22 01:04 notmandatory

force push updated CI

nickfarrow avatar Apr 10 '22 07:04 nickfarrow

Hi, please rebase to pickup changes in #596. Thanks!

notmandatory avatar May 04 '22 17:05 notmandatory

@nickfarrow also try to rebase on top of master instead of fetching and merging specific commit next time.. :) That makes the commit history much cleaner and also applies your changes on top of current master.. just do git rebase master from the PR branch..

rajarshimaitra avatar May 10 '22 12:05 rajarshimaitra

I had to push this PR to the next release so the team can focus on #593, and after that one you'll probably need to rebase again. But then I promise we'll work on getting this one in. :-)

notmandatory avatar May 11 '22 04:05 notmandatory

Yep I think this one needs some bigger discussion first around whether it is worthwhile to mark used addresses directly in the database as @rajarshimaitra suggested.

If this were the case, this could be simplified to a function get_unused_addresses that (quickly) gets an iterator over unused addresses in the database up to the current addressindex. With FirstUnused and LastUnused addresses at either ends.

This PR's get_batch_unused_addresses (currently fetches n) would be superseded by get_unused_addresses where the user gets all the unused addresses and handles them how they desire.

Not sure what changed with old commits, possible I added signoff lines to the previous commits by mistake which may have updated them sorry. Will take care with next.

nickfarrow avatar May 12 '22 03:05 nickfarrow

Upon further thoughts on this I have this rough idea of how it can be done:

  • Add a new column in the script_pubkey table named used. Which will have binary value 0/1, defaulting to 0.
  • At each sync when we add utxos to the Utxo table, mark the pubkey as used too in the script_pubkey table.
  • Make a generic get_batch_unused() that will return a list of unused addresses, checking the used flag in the database.
  • Make FirstUnused as the front pop of the list, and LastUnused as the back pop of the list..

Pro: Much more scalable than transaction list scanning for wallets with large list of transactions.

Cons: This is going to change the DB structure and the BatchDatabase API.

I am willing to work on fleshing an impl out if this has Approach Acks..

rajarshimaitra avatar Jun 18 '22 14:06 rajarshimaitra

Keep in mind we also have the key/value db, we don't have tables and columns there. We can add a flag to mark a script as used (similarly to how I suggested adding a flag for scripts that we've already setup rather than relying on just the derivation index), but getting the list of unused addresses will still require scanning

afilini avatar Jun 28 '22 07:06 afilini

ACK on @LLFourn that this can go in as it is without much further changes.. I will check the behavior once again.. If this lands through bdk_core eventually then we might not wanna do the DataBase way for now, and just keep things simple..

rajarshimaitra avatar Jul 08 '22 07:07 rajarshimaitra

Is this one OK to add this to the 0.22.0 milestone? looks like it's about ready and I don't want it to be overlooked.

notmandatory avatar Aug 09 '22 17:08 notmandatory

IMO this PR is suboptimal because of #701. I think it should be fixed first to make the code in this PR make sense. @nickfarrow?

LLFourn avatar Aug 10 '22 01:08 LLFourn

Yep agree it may as well wait for improvements to fetch_index, that function is relied upon a few times here

nickfarrow avatar Aug 10 '22 12:08 nickfarrow

Hey, we are in the process of releasing BDK 1.0, which will under the hood work quite differently from the current BDK. For this reason, I'm closing all the PRs that don't really apply anymore. If you think this is a mistake, feel free to rebase on master and re-open!

danielabrozzoni avatar Mar 16 '23 17:03 danielabrozzoni