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
Add convenience methods for keys #430
Add convenience methods for keys #430
Conversation
It looks like we borrow Regarding your questions,
|
The 33rd byte is 0x02. |
I don't think we need it. I was under the impression that |
I'm glad I'm not the only one who gets confused by this :) I thought that too @sanket1729 but if that was the case we would never need parity when using |
Yes, the tweaked output key is XOnlyPublicKey. But we need a parity bit for checking the taptweak equation.
The output of I hope this answers the question. |
Legend, thanks. |
When it appears as the external key (and in pretty-much every other context), yes. But specifically when it is used for internal keys, no (as you note -- you need the parity of the internal key to check the taptweak equation). Having said this, I wouldn't mind implementing |
the parity bit of output key. The internal key is always x-only with even Y.
You mean |
Ah you're right, I was confused.
Yes, sorry, that's what I meant. |
That's right, we've had this discussion before - whether to borrow a 32 byte array or not :) I've favored consistency but updated the commit message. |
For reference: rust-bitcoin/rust-bitcoin#708. It seems that there was some consensus that we should take 32-byte arrays by value as they already implement However, that is not related to this PR, and we can address that in another breaking release. |
0be9a1f
to
3e63c6d
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.
Some non-critical naming suggestions
/// | ||
/// This is equivalent to using [`KeyPair::from_secret_key`]. | ||
#[inline] | ||
pub fn keypair<C: Signing>(&self, secp: &Secp256k1<C>) -> KeyPair { |
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.
If we use KeyPair
, according to rust naming guidelines this should be key_pair
, like in PublicKey/public_key
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.
Lol, similar to sighash. I have no preference on this one but we should be consistent, yes.
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.
Double lol, I noticed this while writing this PR too and hoped to no one else would notice because KeyPair
/keypair
has to be addressed in rust-secp256k1
as well, its an invasive change.
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 the problem is that capitalization adds less visual noise than underscoring. Thus, the same API guidelines to add capital letters and underscores in practice do not work well for human eyes.
Personally I do not care if we will use KeyPair
and keypair
at the same time - we already doing that all around the library.
src/key.rs
Outdated
@@ -418,6 +443,19 @@ impl PublicKey { | |||
} | |||
} | |||
|
|||
/// Creates a [`PublicKey`] using the key material from `xonly` combined with `parity`. | |||
pub fn from_x_only_public_key_and_parity(xonly: XOnlyPublicKey, parity: Parity) -> Result<PublicKey, Error> { |
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.
Rust API guidelines recommend to name parameters taking many args to use with_*
pattern. I propose here with_xpubkey_parity
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 from
is clearer because we're literally transforming the inputs, the parameters are not modifiers on a default PublicKey
.
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 went looking and couldn't find the API docs for with vs for, if you disagree with @apoelstra could you please post a link @dr-orlovsky?
Re the ridiculously long name; I was trying to be uniform with the other functions that use x_only_public_key
in the function name. However x_only_public_key
functions also return the parity now so possibly we could shorten them all to xonly
, users would know that 'xonly' means XOnlyPublicKey
and Parity
?
As an example
/// Returns the [`XOnlyPublicKey`] (and it's [`Parity`]) for this [`SecretKey`].
///
/// This is equivalent to `XOnlyPublicKey::from_keypair(self.keypair(secp))`.
#[inline]
pub fn xonly<C: Signing>(&self, secp: &Secp256k1<C>) -> Result<(XOnlyPublicKey, Parity), Error> {
let kp = self.keypair(secp);
XOnlyPublicKey::from_keypair(&kp)
}
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.
@tcharding https://github.com/rust-lang/rfcs/blob/master/text/0430-finalizing-naming-conventions.md#general-naming-conventions
I had read in some previous docs on rust naming in APIs that with_*
stands for when we have more than a one parameter, while from
are the conversions from a specific single type.
I will agree with any naming: all my naming comments are just nits; in fact with modern IDE I do not really care about function names since I always check the function content when do not remember what it does.
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.
Thanks for the link, I ended up using from_xonly
- this may violate the naming convention since xonly
is not referring to a single type but to the key and parity?
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.
from_xonly
is good by me. Have a mild preference for from_xonly_parity
but it's fine, the fact that you need the parity is clear from the function signature (and will be clear from the compiler error if you use it wrong).
src/key.rs
Outdated
|
||
/// Returns the [`XOnlyPublicKey`] (and it's [`Parity`]) for this [`PublicKey`]. | ||
#[inline] | ||
pub fn x_only_public_key<C: Signing>(&self, secp: &Secp256k1<C>) -> Result<(XOnlyPublicKey, Parity), Error> { |
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.
split_xpubkey_parity
?
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 that name is confusing and hard to remember.
src/key.rs
Outdated
#[inline] | ||
pub fn public_key(&self) -> XOnlyPublicKey { | ||
pub fn x_only_public_key(&self) -> Result<(XOnlyPublicKey, Parity), Error> { |
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.
Ibid
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.
Partial review
3e63c6d
to
927839b
Compare
Changes in force push:
|
I am not a big fan of this. Just feels incomplete, maybe I like the completeness of It is good to be consistent with the naming convention to convert to a type |
Thanks for you input @sanket1729, I totally agree with you when the return type was just the |
This is why I am a big fan using |
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.
Looks good. Some final comments. Maybe @apoelstra has opinions about xonly
public key parities.
src/key.rs
Outdated
@@ -418,6 +443,20 @@ impl PublicKey { | |||
} | |||
} | |||
|
|||
/// Creates a [`PublicKey`] using the key material from `pk` combined with the `parity`. | |||
pub fn from_xonly(pk: XOnlyPublicKey, parity: Parity) -> Result<PublicKey, Error> { |
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 am sorry to bring this up again. But I think this should add 0x02
by default. XOnlyPublicKey
is a full public key with 0x02
y co-ordinate.
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 am unable to make forward progress on this PR, @sanket1729 and @apoelstra currently have opposing views, how do we want to move forward, with or without parity parameter?
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 concede, let's go with @sanket1729's suggestion.
src/key.rs
Outdated
}; | ||
buf[1..].clone_from_slice(&pk.serialize()); | ||
|
||
PublicKey::from_slice(&buf) |
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.
You can safely unwrap/expect
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.
Cool, thanks. Error return removed as suggested.
src/key.rs
Outdated
/// This is equivalent to using [`PublicKey::from_xonly_and_parity(self, parity)`]. | ||
#[inline] | ||
pub fn public_key(&self, parity: Parity) -> Result<PublicKey, Error> { | ||
PublicKey::from_xonly(*self, parity) |
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.
After the abox fix, this should not return Result
. And I think we should have not a parity parameter here.
`SecretKey` implements `Copy` and it is fine to take owneship of it; we have multiple methods called `from_secret_key` and they all borrow the secret key parameter. Favour consistency over perfection. Borrow secret key parameter as is done in other `from_secret_key` methods.
We have two places in the code where we pass a mutable parity integer to ffi code. At one callsite we tell the compiler explicitly what type it is (`::secp256k1_sys::types::c_int`) and at the other call site we let the compiler figure out the type. Is one way better than the other? I don't know. But letting the compiler figure it out seems to make the code easier to read.
We have a bunch of `from_<key>` methods for converting between key types. To improve the API and make it more ergonomic to use we can add methods that do the same but can be called on the initial key instead of on the resulting key's type. E.g. once applied the following are equivalent: - `let pk = PublicKey::from_keypair(kp)` - `let pk = kp.public_key()` Do this for `SecretKey`, `PublicKey`, `KeyPair`, and `XOnlyKeyPair`.
927839b
to
f08276a
Compare
Changes in force push:
The parity parameter is still present but if it should be or not is still unresolved. |
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.
ACK f08276a. Thanks for going through all the iterations.
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.
ACK f08276a
Yes, thanks for iterating!
We have a bunch of
from_<key>
methods for converting between key types. To make the API more ergonomic to use we can add methods that do the same but called on a variable e.g., once applied the following are equivalent:let pk = PublicKey::from_keypair(kp)
let pk = kp.public_key()
Do this for
SecretKey
,PublicKey
,KeyPair
, andXOnlyKeyPair
.Fixes: #428
Note to reviewers
XOnlyPublicKey
->PublicKey
logic is made up by me, I could not work out how to getlibsecp256k1
to do this.