-
Notifications
You must be signed in to change notification settings - Fork 167
Expose inner DescriptorPublicKey functionality
#570
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
base: master
Are you sure you want to change the base?
Expose inner DescriptorPublicKey functionality
#570
Conversation
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe DescriptorInnerKey should have an Into<DescriptorPublicKey> bound?
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.
🧠 💡
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.
Erm, that'll require DescriptorInnerKeyDescriptorKey to be made object-safe. That's beyond my ken.
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 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
| } | ||
| } | ||
|
|
||
| pub trait DescriptorInnerKey { |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
That's sure where I'm using it. 😅
src/descriptor/key.rs
Outdated
| } | ||
| } | ||
|
|
||
| pub trait DescriptorInnerKey { |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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?