Expose inner DescriptorPublicKey functionality#570
Expose inner DescriptorPublicKey functionality#570quad wants to merge 6 commits intorust-bitcoin:masterfrom
DescriptorPublicKey functionality#570Conversation
a1dbaf5 to
dcfcc25
Compare
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| maybe_fmt_master_id(f, &self.origin)?; | ||
| self.xkey.fmt(f)?; | ||
| fmt_derivation_path(f, &self.derivation_path)?; | ||
| match self.wildcard { | ||
| Wildcard::None => {} | ||
| Wildcard::Unhardened => write!(f, "/*")?, | ||
| Wildcard::Hardened => write!(f, "/*h")?, | ||
| } | ||
| Ok(()) | ||
| } |
There was a problem hiding this comment.
Good candidate to extract to a fmt_xkey(origin, [paths]) function.
| .ok_or(ConversionError::HardenedChild)?, | ||
| ), | ||
| }; | ||
| let definite = DescriptorPublicKey::XPub(DescriptorXKey { |
There was a problem hiding this comment.
This is an awkward "upward" instantiation of the DescriptorPublicKey type.
An idea: DefiniteDescriptorKey::new could take a DescriptorInnerKey.
There was a problem hiding this comment.
Maybe DescriptorInnerKey should have an Into<DescriptorPublicKey> bound?
There was a problem hiding this comment.
Erm, that'll require DescriptorInnerKeyDescriptorKey to be made object-safe. That's beyond my ken.
There was a problem hiding this comment.
I think it might be as simple as sticking a : Sized bound on the trait? What error do you get?
a6f0354 to
14799e2
Compare
src/descriptor/key.rs
Outdated
There was a problem hiding this comment.
In e4d9af4:
Do you think we should add fmt::Display and std::str::FromStr bounds on this? How about Eq, Hash, etc.? (My vote is yes to all four, and maybe fmt::Debug too.)
cc @sanket1729 who may have opinions here.
There was a problem hiding this comment.
I'm a big fan of that, since it was the lack of Display / FromStr that motivated this PR.
There was a problem hiding this comment.
I added Clone + fmt::Debug + fmt::Display + Eq + FromStr + std::hash::Hash + Ord to match the existing derivations.
|
|
||
| pub trait DescriptorInnerKey { | ||
| /// The fingerprint of the master key associated with this key, `0x00000000` if none. | ||
| fn master_fingerprint(&self) -> bip32::Fingerprint; |
There was a problem hiding this comment.
In e4d9af4:
As a separate issue, I think we should return an option here and then upstream we should have a bip32::Fingerprint::from_opt function that would do the 0x00000000 thing. Though I'm unsure. Maybe this function is only ever used in contexts where the result needs to be serialized (PSBT, encoding xpubs) in which case users always want the existng behavior.
src/descriptor/key.rs
Outdated
There was a problem hiding this comment.
In e4d9af4:
This can go in a separate PR, but like to have a type-level relationship between DescriptorPublicKey and DescriptorSecretKey. This would help me downstream in elements-miniscript where I want to define a "view key" for a confidential transaction descriptor, which has a private key in one slot and a public key in every other slot. I think it would also simplify a lot of usage of xprv descriptors, which currently require you maintain an xpub descriptor alongside a hashmap, though I haven't worked out the details.
One way we could do this is to add two associated types, like
type SecretKey: DescriptorInnerKey;
type PublicKey: DescriptorInnerKey;
where almost always one of these would be Self.
I would also like first-class support for unspendable keys in taproot. I am imagining an UnspendableKey whose SecretKey type would be !, or something.
Anyway these are just half-baked ideas that don't belong in this PR. But I want to bring them up in case they could guide this API.
|
Overall 14799e2 looks good. It's a straightforward enough diff and I am confirming that all commits pass all tests (looking good so far).
I don't think it would be breaking, but I also don't know that we need to worry about breaking changes here. Especially if we can also figure out the "what is the right way to relate private and public keys" and "what is the right way to handle unspendable keys" API questions. But yes, it did occur to me when reading the code that it might make sense to implement this on
Maybe just
Good to know. Let's revisit that if we decide to do a breaking change.
So, with But having said that, for keys we have continuously moved toward more trait usage, adding more traits and associated types etc. |
397d435 to
ef617d5
Compare
ef617d5 to
b18ba59
Compare
👍
I added a commit that does that. It's "breaking" insofar as consumers now need to import a trait. (e.g. see the change in |
|
Hello, thanks for this PR. Am I correct in summarizing that if we have a If this the case, we provide the same APIs for We can then offer some granular types with some guidelines as Tihs obviously implies that you cannot single keys in your descriptor. Curious what @apoelstra thinks about this. Comment about the trait approachI am not sure about adding a trait, 1) It requires users to import it and 2) we are not using it any bounds here. Are you planning it downstream? If so, can you share you want it to be used as a bound? As Andrew mentioned in the #386, we found ourselves into a similar issue where we group things for I do think the trait has value only if we go all the way so that cc @tcharding worked on changing the previous long names that we had |
Maybe? Honestly, the type hierarchy is too complex for me to follow. That said, there's a lot of useful functionality on
I strongly agree with this sentiment. |
Our codebase has |
If all functionality for |
| type DescriptorSinglePublicKey = SinglePub; | ||
| type DescriptorExtendedPublicKey = DescriptorXKey<bip32::ExtendedPubKey>; | ||
| type DescriptorMultiExtendedPublicKey = DescriptorMultiXKey<bip32::ExtendedPubKey>; |
There was a problem hiding this comment.
IMO we should not start putting Extended back into the identifiers of keys. The only other place that appears is in bitcoin::bip32 which is not a module to copy :)
In general I prefer shorter names if there is no ambiguity or loss of clarity. Thanks. |
Hi friends, it's me again! 👋
In our use of miniscript, we find ourselves writing constantly writing this idiom:
I'd love to directly use the
DescriptorXKey<bip32::ExtendedPubKey>type; but, its functionality is anemic when compared to all the useful functions onDescriptorPublicKey.The closer I looked at
DescriptorPublicKey, the more I realised it was mostlymatchblocks itself that delegated to different implementations based on the internal enum type. And, so, here's a PR that leans into that and reifies a trait (DescriptorInnerKey) that encapsulates all that behaviour and three types that implement it:DescriptorSinglePublicKey(a type alias forSinglePub🤒)DescriptorExtendedPublicKeyDescriptorMultiExtendedPublicKeyThis all said, I feel like this PR is still lacking. A few questions:
DescriptorPublicKeycould implement the trait too; but, I suspect that be a breaking change? Moreover …DescriptorInnerKeyis a weird name, especially when contrasted against the already existingInnerXKeytrait. I'm open to a better name!DescriptorPublicKey::into_single_keyshas been awkwardly left out of the extraction because it returns a vector ofDescriptorPublicKeys. If I was going to do a breaking refactor, I'd probably move that method on toDescriptorMultiExtendedPublicKeyitself. 🤷DescriptorPublicKeyas astructjust disappeared … and instead there was only aDescriptorPublicKeytrait?