Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Please unbreak single-key wallets (and potentially remove internal/external distinction entirely) #1511

Closed
stevenroose opened this issue Jul 11, 2024 · 19 comments · Fixed by #1533
Labels
bug Something isn't working
Milestone

Comments

@stevenroose
Copy link
Contributor

stevenroose commented Jul 11, 2024

In alpha.9 or something, I could pass None for the internal descriptor. Now it is no longer an Option, so I passed in the same descriptor, but we get an error that they can't be the same.

We have a wallet that is a single key, simple tr(<pubkey>) descriptor where internal and external addresses are identical. This used to work perfectly before.

We are hitting this message: write!(f, "External and internal descriptors are the same")

@stevenroose stevenroose added the bug Something isn't working label Jul 11, 2024
@storopoli
Copy link
Contributor

This was discussed in #1383

@stevenroose
Copy link
Contributor Author

Hmm, from reading the convo, it's not very clear that the explicit decision was made to no longer support single-key wallets. What is the difficulty in doing so? I don't fully understand what this change makes internal development easier.

I would like to strongly request to bring back support for single-key wallets. Also, the internal/external distinction is kinda stupid anyway, I don't see a good reason to keep this structure altogether. I think if BDK wants to take a progressive stance, it should do away with the dual descriptors entirely and just have a single descriptor. (AFAIK the difference originated in wallets not being able to remember which addresses were already given out so they would accidentally use an address they gave to someone for change. But since BDK, and more modern wallets for that matter, never allow re-handing out an address, having both is no longer needed. When you need a change address, you can just pull a new address from the key pool.)

@stevenroose stevenroose changed the title Recent change made internal descriptor mandatory Please unbreak single-key wallets (and potentially remove internal/external distinction entirely) Jul 11, 2024
@stevenroose
Copy link
Contributor Author

EDIT: It seems that another motivation for the distinction is to enhance privacy for users that use Electrum-like servers. They could avoid informing the server about their internal addresses. Ever since we have compact block filters, Electrum-like setups are less and less needed and we should definitely think about making progress away from this model and not doubling down on it, IMO.

@notmandatory
Copy link
Member

Unfortunately many/most of our users are still using electrum and/or esplora servers so I think this privacy concern still exists. I can see us removing this restriction in the future once CBF is more common, but for now the decision was to be more opinionated with the Wallet apis and to support the more common uses.

If you want to use BDK for less common scenarios you can still construct a custom wallet using the bdk_chain crate, which we have examples for.

@stevenroose
Copy link
Contributor Author

You are deciding to provide a worse experience for users that have better wallet practices? All wallets that use bitcoind, cbf, their own personal electrum server or anything else that is not a public blockchain index, are now suffering that they need to deal with two different descriptors, potentially write these down, etc etc, all because a lot of other users do use Electrum?

Also, entirely dropping support for single-key wallets is also a very big feature cut IMO that should be discussed independently of the int/ext discussion.

@notmandatory
Copy link
Member

Maybe I don't understand your use case for TR single key wallets, would these always produce the same SPK? are they needed for LN taproot channels or some other type of TR user?

@notmandatory notmandatory added this to the 1.0.0-alpha milestone Jul 11, 2024
@notmandatory
Copy link
Member

notmandatory commented Jul 11, 2024

I put this on the discussion tab for our alpha milestone to get feed back from others on the team on if we really need to revert #1390.

@ValuedMammal
Copy link
Contributor

It shouldn't be too difficult to build in support for single descriptor wallets. Long term we might consider doing away with KeychainKind enum and supporting wallets with one or more keychains.

@notmandatory
Copy link
Member

@stevenroose have you taken a look at the example_bitcoind_rpc_polling cli wallet ? it's an example that is built on just the bdk_chain crate and doesn't use the bdk_wallet at all. This would be the best way to use BDK for a custom on-chain app with a single keychain or any other custom behavior. The example isn't fully fleshed out but we'll be doing that once we get the first beta release out.

@LLFourn
Copy link
Contributor

LLFourn commented Jul 12, 2024

I think trying to build a single descriptor wallet is a fine idea. But that's not Wallet. Wallet was trying to internally map the internal keychain to the external keychain if the internal didn't exist. I don't think that's the "modern" approach you are after @stevenroose. The correct way to approach this is to create a SingleDescriptorWallet that only takes one descriptor. The architect for this will have to figure out how mark which derivation indices are being used for change, which have been revealed for receiving funds, how and whether to persist these designations and how to figure out confirmed/unconfirmed balance etc etc.

@stevenroose
Copy link
Contributor Author

stevenroose commented Jul 12, 2024

will have to figure out how mark which derivation indices are being used for change, which have been revealed for receiving funds

My argument is that this distinction only matters for wallets using a public chain index. So maybe a wallet that cares about keeping track of internal and external addresses separately should be called ElectrumWallet or PublicIndexWallet and wallets that don't care about this just Wallet? Also, the privacy gain from separating them before sending to a public index service is marginal: you send them all your incoming funds and doing the further graph analysis is often trivial. So even for those, I am not sure the complexity of managing two sets of addresses is merited.

IMO refactoring to make KeychainKind a second class citizen that is only enabled for users explicitly requesting so, would simplify the API for regular users. Having them a first class citizen also feels like it encourages bad behavior of using public index services.

I've had many conversations with Evan about ideas of making compact block filters more ergonomic to use and how BDK is perfectly positioned to use those (with something like Neutrum, public servers of blocks and block filters, wallets can also do full sync without needed p2p or chain data but also without sending their addresses to a third party). I believe this is the way forward instead of doubling down on the Electrum model.

@notmandatory
Copy link
Member

notmandatory commented Jul 12, 2024

These are all sound technical arguments for supporting a Wallet with only a single descriptor, but the overriding non-technical arguments for keeping our current API for the 1.0 are:

  1. The primary goal for the 1.0 release was to support async for Wallet persistence and blockchain syncing, this has been accomplished.
  2. Most of our existing users use simple descriptors with extended keys and change descriptors.
  3. Most of our users use the electrum or esplora clients by the simple fact that we don't have a rust CBF client ready for production use. We may have some RPC client users but they are much less common right now.
  4. Getting to a BDK 1.0 stable API has taken more than a year longer than expected and we have users anxiously waiting for it. We hope to wrap this up in a week and don't have time to incorporate any major new changes.

The good news is 1.0 is our first, but won't be our last stable API release. If we can incorporate single descriptors in a non-breaking way in a 1.1 release (eg. some new Wallet constructor methods) we can get it in there. Or worst case we target this for a 2.0 milestone, where we've already discussed supporting "multi descriptor" (ie. 1..n descriptor) wallets.

@notmandatory
Copy link
Member

I did a little experimenting and we might be able to support single descriptor wallets in with the existing Wallet, but just not for our 1.0 beta milestone. I did a quick experiment with #1513 and it looks like we can do it as a non-breaking change (new constructors) so could get it in a 1.1 release.

@cryptoquick
Copy link

It would be really helpful if we could get this in for 1.0. For compatibility purposes, could we just make it so if the same descriptor is passed as both arguments, it's just treated as a single descriptor for now?

@ValuedMammal
Copy link
Contributor

ValuedMammal commented Aug 1, 2024

I can work on this. In order to not break the current (beta) API, we may just add a way to construct CreateParams with one descriptor instead of two. Some of the immediate consequences are

  • Attempting to reveal script pubkeys/addresses for an empty keychain would cause a panic
  • Methods involving the ChangeSpendPolicy type would be less significant

@storopoli
Copy link
Contributor

  • Attempting to reveal script pubkeys/addresses for an empty keychain would cause a panic

Yes, we should abort at any potential loss of funds. Agreed!

@matthiasdebernardini
Copy link

I've asked on X/Twitter why have two descriptors instead of one. I got some great answers from the community. From my perspective, it comes down to privacy/backup restoration UX.

Optech writeup

Bitcoin Core related to the PR

For our use case, we have two descriptors, but our wallet can guarantee metadata persistence (we can add labels to the addresses and mark them as change and always track which addresses are given out) which would take care of privacy/backup concerns. Therefore we could have one instead of two. This change would simplify the code for creating the descriptors for our users. Despite this, its such a small value add and would make our descriptors incompatible with wallets expecting a second descriptor that staying with two is the best way forward.

I'm adding these links here to add some color to the discussion.

@Rob1Ham
Copy link

Rob1Ham commented Aug 9, 2024

I'm in support of single descriptor wallets as a use case. In addition to use cases brought up so far here, descriptors allow for multiple paths within them. The standard change/receive functionality is used with <0;1> syntax for descriptors for most hardware wallets at this point.

If this was enabled for BDK, I could see many multi descriptor wallets falling under the single descriptor path, and using this syntax to clean up code, and bring about uniform interoperability between HWW descriptors and BDK descriptors.

As of now we treat BDK external/internal descriptors as first class citizens, and do conversions to <0;1> for HWWs at the last mile, but it adds additional maintenance in our codebase to do this.

@github-project-automation github-project-automation bot moved this from Discussion to Done in BDK Aug 14, 2024
@notmandatory
Copy link
Member

If this was enabled for BDK, I could see many multi descriptor wallets falling under the single descriptor path, and using this syntax to clean up code, and bring about uniform interoperability between HWW descriptors and BDK descriptors.

I agree, and now that we've added back support for a single descriptor when creating a wallet we should be able to do #1021 as a non-breaking change in a 1.x version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
Archived in project
8 participants