-
Notifications
You must be signed in to change notification settings - Fork 38.8k
descriptors: Use xpub at last hardened step if possible #18163
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
Conversation
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
Sjors
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concept ACK
src/script/descriptor.cpp
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Travis doesn't like this: https://travis-ci.org/bitcoin/bitcoin/jobs/651243106#L4064
Error: attempt to decrement a dereferenceable (start-of-sequence) iterator.
It sounds like a job for std::find_end with a binary predicate, since you're looking for a hardened path element by an unhardened one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was able to use std::find_if for this.
|
Is there a practical consideration for this change? It seems to make sense, just wondering if downstream applications are made easier by this or something. |
It would allow for descriptor wallets that import a descriptor with a private key and hardened steps to be able to derive scriptPubKeys without needing to be unlocked. |
|
Adding that to OP would be OP concept ACK! |
c106814 to
0e54ba7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0e54ba78ea7e7468ce5a185c4195b0dd58f3f82d looks good, expect the duplicate line.
That said, I feel a bit ambivalent about adding origin info to descriptors that don't already have it. For example pkh(xprv9...C2U/0'/0'/0) becomes pkh([bd16bee5/0'/0']xpub6AVD...pQKh/0). Perhaps normalisation should only happen for descriptors with origin info. Also, an xpub encodes its depth, so if in the example the original xpriv depth wasn't 0 it would be weird (or maybe not; depends on if we interpret the fingerprint as depth 0).
I personally haven't seen real world descriptors without origin info yet, so I'm sure how they should be handled.
Also, what would be the next step to have InferDescriptor benefit from this change? It would be nice to have getaddressinfo in return an xpub, instead of individual public key, for BIP44/49/84 style watch-only (legacy) wallets.
Then we wouldn't achieve the primary goal of this PR. People could import xprvs without origin info that have some hardened derivation steps and we wouldn't be able to derive the public keys from them.
The xpub at the end would still contain that depth information. Even without an origin info, an xprv could have a non-zero depth but that doesn't bother us.
I think that requires changes to |
0e54ba7 to
b674297
Compare
|
ACK b674297c7c491ea09cef171ed083533f23c9ffa9 I've never imported keys without origin info, but if you find it useful, I'm fine with supporting it. |
Utility function to derive at a path from an CExtKey instead of implementing this externally every time we want to derive at a path.
b674297 to
f827475
Compare
|
Had to rebase |
|
I discussed this with @achow101 IRL. One alternative I'd like to see explored is instead of rewriting the descriptor itself, having an "xpub cache" added to the descriptor interface (and later the wallet itself), similar to the existing pubkey cache, but shared across indexes. This would have all the availability benefits of a change like this, but also speed up operations on descriptors that include several non-hardened steps in xpubs. |
|
That would take case of this TODO, right? bitcoin/src/script/descriptor.cpp Lines 286 to 291 in ab9de43
Rewriting the descriptor has merit in any case; a descriptor like For BIP44/49/84 style descriptors ( |
09e2507 Cache parent xpub inside of BIP32PubkeyProvider (Andrew Chow) deb791c Only cache xpubs that have a hardened last step (Andrew Chow) f76733e Cache the immediate derivation parent xpub (Andrew Chow) 58f54b6 Add DescriptorCache* read_cache and DescriptorCache* write_cache to Expand and GetPubKey (Andrew Chow) 66c2cad Rename BIP32PubkeyProvider.m_extkey to m_root_extkey (Andrew Chow) df55d44 Track the index of the key expression in PubkeyProvider (Andrew Chow) 474ea3b Introduce DescriptorCache struct which caches xpubs (Andrew Chow) Pull request description: Improves the descriptor cache by changing it from a `std::vector<unsigned char>` to a newly introduced `DescriptorCache` class. Instead of serializing pubkeys and whatever else we would want to cache in a way that may not be backwards compatible, we instead create a `DescriptorCache` object and populate it. This object contains only an xpub cache. Since the only `PubkeyProvider` that used the cache is the `BIP32PubkeyProvider` we just have it store the xpubs instead of the pubkeys. This allows us to have both the parent xpub and the child xpubs in the same container. The map is keyed by `KeyOriginInfo`. Sine we are caching `CExtPubKey`s in `DescriptorCache`, `BIP32PubKeyProviders` can use the cached parent xpubs to derive the children if unhardened derivation is used in the last step. This also means that we can still derive the keys for a `BIP32PubkeyProvider` that has hardened derivation steps. When combined with descriptor wallets, this should allow us to be able to import a descriptor with an `xprv` and hardened steps and still be able to derive from it. In that sense, this is an alternative to #18163 To test that this works, the tests have been updated to do an additional `Expand` at the `i + 1` position. This expansion is not cached. We then do an `ExpandFromCache` at `i + 1` and use the cache that was produced by the expansion at `i`. This way, we won't have the child xpubs for `i + 1` but we will have the parent xpubs. So this checks whether the parent xpubs are being stored and can be used to derive the child keys. Descriptors that have a hardened last step are skipped for this part of the test because that will always require private keys. ACKs for top commit: instagibbs: code review re-re-ACK 09e2507 Sjors: re-ACK 09e2507 Tree-SHA512: 95c8d0092274cdf115ce39f6d49dec767679abf3758d5b9e418afc308deca9dc6f67167980195bcc036cd9c09890bbbb39ec1dacffbfacdc03efd72a7e23b276
…xpubs 09e2507 Cache parent xpub inside of BIP32PubkeyProvider (Andrew Chow) deb791c Only cache xpubs that have a hardened last step (Andrew Chow) f76733e Cache the immediate derivation parent xpub (Andrew Chow) 58f54b6 Add DescriptorCache* read_cache and DescriptorCache* write_cache to Expand and GetPubKey (Andrew Chow) 66c2cad Rename BIP32PubkeyProvider.m_extkey to m_root_extkey (Andrew Chow) df55d44 Track the index of the key expression in PubkeyProvider (Andrew Chow) 474ea3b Introduce DescriptorCache struct which caches xpubs (Andrew Chow) Pull request description: Improves the descriptor cache by changing it from a `std::vector<unsigned char>` to a newly introduced `DescriptorCache` class. Instead of serializing pubkeys and whatever else we would want to cache in a way that may not be backwards compatible, we instead create a `DescriptorCache` object and populate it. This object contains only an xpub cache. Since the only `PubkeyProvider` that used the cache is the `BIP32PubkeyProvider` we just have it store the xpubs instead of the pubkeys. This allows us to have both the parent xpub and the child xpubs in the same container. The map is keyed by `KeyOriginInfo`. Sine we are caching `CExtPubKey`s in `DescriptorCache`, `BIP32PubKeyProviders` can use the cached parent xpubs to derive the children if unhardened derivation is used in the last step. This also means that we can still derive the keys for a `BIP32PubkeyProvider` that has hardened derivation steps. When combined with descriptor wallets, this should allow us to be able to import a descriptor with an `xprv` and hardened steps and still be able to derive from it. In that sense, this is an alternative to bitcoin#18163 To test that this works, the tests have been updated to do an additional `Expand` at the `i + 1` position. This expansion is not cached. We then do an `ExpandFromCache` at `i + 1` and use the cache that was produced by the expansion at `i`. This way, we won't have the child xpubs for `i + 1` but we will have the parent xpubs. So this checks whether the parent xpubs are being stored and can be used to derive the child keys. Descriptors that have a hardened last step are skipped for this part of the test because that will always require private keys. ACKs for top commit: instagibbs: code review re-re-ACK bitcoin@09e2507 Sjors: re-ACK 09e2507 Tree-SHA512: 95c8d0092274cdf115ce39f6d49dec767679abf3758d5b9e418afc308deca9dc6f67167980195bcc036cd9c09890bbbb39ec1dacffbfacdc03efd72a7e23b276
|
something "like" this is probably nice to have for multisig setups. I'm looking into it for a |
…xpubs 09e2507 Cache parent xpub inside of BIP32PubkeyProvider (Andrew Chow) deb791c Only cache xpubs that have a hardened last step (Andrew Chow) f76733e Cache the immediate derivation parent xpub (Andrew Chow) 58f54b6 Add DescriptorCache* read_cache and DescriptorCache* write_cache to Expand and GetPubKey (Andrew Chow) 66c2cad Rename BIP32PubkeyProvider.m_extkey to m_root_extkey (Andrew Chow) df55d44 Track the index of the key expression in PubkeyProvider (Andrew Chow) 474ea3b Introduce DescriptorCache struct which caches xpubs (Andrew Chow) Pull request description: Improves the descriptor cache by changing it from a `std::vector<unsigned char>` to a newly introduced `DescriptorCache` class. Instead of serializing pubkeys and whatever else we would want to cache in a way that may not be backwards compatible, we instead create a `DescriptorCache` object and populate it. This object contains only an xpub cache. Since the only `PubkeyProvider` that used the cache is the `BIP32PubkeyProvider` we just have it store the xpubs instead of the pubkeys. This allows us to have both the parent xpub and the child xpubs in the same container. The map is keyed by `KeyOriginInfo`. Sine we are caching `CExtPubKey`s in `DescriptorCache`, `BIP32PubKeyProviders` can use the cached parent xpubs to derive the children if unhardened derivation is used in the last step. This also means that we can still derive the keys for a `BIP32PubkeyProvider` that has hardened derivation steps. When combined with descriptor wallets, this should allow us to be able to import a descriptor with an `xprv` and hardened steps and still be able to derive from it. In that sense, this is an alternative to bitcoin#18163 To test that this works, the tests have been updated to do an additional `Expand` at the `i + 1` position. This expansion is not cached. We then do an `ExpandFromCache` at `i + 1` and use the cache that was produced by the expansion at `i`. This way, we won't have the child xpubs for `i + 1` but we will have the parent xpubs. So this checks whether the parent xpubs are being stored and can be used to derive the child keys. Descriptors that have a hardened last step are skipped for this part of the test because that will always require private keys. ACKs for top commit: instagibbs: code review re-re-ACK bitcoin@09e2507 Sjors: re-ACK 09e2507 Tree-SHA512: 95c8d0092274cdf115ce39f6d49dec767679abf3758d5b9e418afc308deca9dc6f67167980195bcc036cd9c09890bbbb39ec1dacffbfacdc03efd72a7e23b276
When a descriptor contains a private key and has hardened derivation steps, derive the xpub at the last hardened step and convert the derivation into origin info. So the result is that a
BIP32PubkeyProvidercontaining axprvwith hardened derivation steps and ending with unhardened derivation becomes aOriginPubkeyProviderwith those hardened derivation steps as the origin info and a newBIP32PubkeyProvidercontaining thexpuband the unhardened derivation steps.For example:
wpkh(xprv..../0'/0'/*)becomeswpkh([d34db33f/0'/0']xpub.../*)If the descriptor has only hardened steps, it is not modified.
This change allows descriptor wallets to derive from such descriptors without needing to be unlocked.