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
Taproot: BIP32 extended keys using Scep256k1 keys instead of bitcoin ECDSA #590
Conversation
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 d155115
Rebased on master, addressed comments, now ready for review |
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 this. The code looks great. I have a discussion point about API naming which might use more feedback from other contributors.
src/util/bip32.rs
Outdated
|
||
/// Constructs BIP340 keypair for Schnorr signatures and Taproot use matching the internal | ||
/// secret key representation. | ||
pub fn to_schnorr<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> schnorr::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.
I have a slight preference in favor of to_schnorr_keypair
.
I also like to_ecdsa_privkey
, to_ecdsa_pubkey
, and to_schnorr_pubkey
over the current ones. But this one is more debatable since the user has the context of what return type to expect.
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.
Changed to to_keypair
since there is no other keypair (and @apoelstra does not like "Schnorr" surname :D because of him patenting signature algo)
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 would prefer to_schnorr_keypair
. It's sad that this has patent history, but IMO the convenience and usability triumphs this.
Would like to get input from other contributors as well, but IMO to_keypair
is just confusing. It could main ecdsa_keypair
as well as schnorr_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.
Do we have a keypair type for non-BIP-340 keys?
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 really do not like inconsistency in function names for extended keys that we have today in the API: derive_priv
, derive_pub
, new_master
, from_private
- and now to_ecdsa_privkey
.... :(
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.
Did a temporal version with priv/pub
instead of privkey
for more consisted naming for now; will wait for others to 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.
Reviewing the discussion, after some thinking I think my vote is for to_ec_pubkey
, to_xonly_keypair
for rationale mentioned here:
#588 (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.
Looks pretty good. I just now saw @sanket1729's comment 😆 so take my comments with a grain of salt. As explained in the first comment my aversion to the more descriptive names comes from the idea of unifying the interface of x-keys via a common trait. For that the different names would be very annoying (but no blocker here, won't cut a release too soon anyway). I'll try to build it in a separate PR, the basic idea is to have:
pub trait XKey: Sized {
type EcdsaKey;
type SchnorrKey;
fn derive<P: IntoIterator<Item=ChildNumber>>(&self, path: P) -> Self;
fn to_ecdsa(&self) -> Self::EcdsaKey;
fn to_schnorr(&self) -> Self::SchnorrKey;
}
src/util/bip32.rs
Outdated
chain_code: ChainCode::from(&hmac_result[32..]), | ||
}) | ||
} | ||
|
||
/// Constructs ECDSA compressed private key matching internal secret key representation. | ||
pub fn to_ecdsa_priv(&self) -> ecdsa::PrivateKey { |
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 wonder if the _priv
part is really useful. You know you are operating on an xpriv and the return type is a private key. Kinda related: I'd like to explore the possibility of putting the derive and conversion functions into a trait (not here though) to allow generic code over x-keys.
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 #755, I think this should be just to_priv
. As mentioned and discussed in several places, keys are not related to signature algorithms.
src/util/bip32.rs
Outdated
|
||
/// Constructs BIP340 keypair for Schnorr signatures and Taproot use matching the internal | ||
/// secret key representation. | ||
pub fn to_schnorr_keypair<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> schnorr::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.
Same here _keypair
seems redundant.
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 missed this. This should also be to_keypair
src/util/bip32.rs
Outdated
chain_code: sk.chain_code | ||
} | ||
} | ||
|
||
/// Constructs ECDSA compressed public key matching internal public key representation. | ||
pub fn to_ecdsa_pub(&self) -> ecdsa::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.
redundant _pub
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.
Same, this should be to_pub
src/util/bip32.rs
Outdated
|
||
/// Constructs BIP340 x-only public key for BIP-340 signatures and Taproot use matching | ||
/// the internal public key representation. | ||
pub fn to_schnorr_pub(&self) -> schnorr::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.
redundant _pub
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.
And this should be to_x_only_pub
What is the status of this? Does it need a rebase and/or the PR description updated to have different commit IDs? |
@apoelstra rebased, description update, ready for (re)reviews |
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 we should resolve our discussion #588 (comment) before proceeding with this.
@sanket1729 that discussion much more relates to previous PR #589 which is already merged (and a bitcoin-rust version with it is already released). If we need to change that approach, that should be a matter of a new PR. This PR is very specifically removes unnecessary non-Secp256k1 pubkeys from BIP32/PSBT related data structures and does not directly related to the key type naming discussion within rust-bitcoin |
@dr-orlovsky, there is a discussion to possibly undo things in #589 (in a non-breaking way). If there is a consensus for that, this PR would be updated, namely, the function names that use |
Why we can't do that alltogether after merging this PR, independently from it? |
I don't have a strong opinion but appears that there is consensus for removing signature names from functions names using This PR is not dependant on it and can get in without the other one, but there is no reason to have poor API names here just so we can change them later. |
@sanket1729 can you pls explain what exactly you propose to update in 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.
ACK 7c565ec . Once we rename ecdsa
and schnorr
keys to ec
and xonly
, we should update the API names in this PR too.
/// Constructs BIP340 keypair for Schnorr signatures and Taproot use matching the internal | ||
/// secret key representation. | ||
pub fn to_schnorr_keypair<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> schnorr::KeyPair { | ||
schnorr::KeyPair::from_seckey_slice(secp, &self.private_key[..]).expect("BIP32 internal private key representation is broken") |
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.
Can you add a unit test which tries to parse an xpriv with a secret key of all zeros and one where the secret key is all ff
s? I'm worried that this panic can be triggered.
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.
Well, you can't construct xpriv with such secret keys - it errors during the construction on incorrect secret key. However I added general test case for this method.
utACK 7c565ec |
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 1ff5730
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.
Left two comments. I don't think we should wait for a point release of secp for a purpose of a test (which I think is not testing anything concrete). We can add the test later after a point release of secp.
src/util/bip32.rs
Outdated
|
||
/// Constructs BIP340 keypair for Schnorr signatures and Taproot use matching the internal | ||
/// secret key representation. | ||
pub fn to_schnorr_keypair<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> schnorr::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.
I missed this. This should also be to_keypair
src/util/bip32.rs
Outdated
let secp = Secp256k1::new(); | ||
let xpriv = ExtendedPrivKey::from_str("xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi").unwrap(); | ||
let pair = KeyPair::from_seckey_slice(&secp, &xpriv.private_key[..]).unwrap(); | ||
assert_eq!(xpriv.to_schnorr_keypair(&secp), pair); |
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 don't see any value in this test? What is this test testing? It is a one-line function code that is repeated in the test.
If you feel there is value in this, we can check that the x-only public keys from the keypairs are the same for now. After another point release, we can re-add this test. I don't think we are losing any features for the next release because of this.
@sanket1729 addressed issues and removed test. @Kixunil pls re-ACK |
Would've reacked but it's uncompilable... |
@Kixunil sorry, my bad: introduced last minute "improvement" with additional commit and didn't properly checked it. Should be fixed now |
Still some issue. |
@Kixunil I do not know what the ... is happening with CI, but it perfectly builds locally while it fails with exactly the same config on GitHub:
|
@dr-orlovsky , the tests are not building. Not the binary. |
Looks like we are not deriving #[derive(Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct ExtendedPrivKey { |
@@ -44,7 +45,8 @@ impl_array_newtype!(Fingerprint, u8, 4); | |||
impl_bytes_newtype!(Fingerprint, 4); | |||
|
|||
/// Extended private key | |||
#[derive(Copy, Clone, PartialEq, Eq, Debug)] | |||
#[derive(Copy, Clone, PartialEq, Eq)] | |||
#[cfg_attr(feature = "std", derive(Debug))] |
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.
Why are you doing this? I think this is causing the issue? Why cannot use derive debug in no-std? And how is that related to 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.
We need this since SecretKey
in the upstream does custom Debug
which works only on std
. Without this change it foes not compile on no_std
I think we should leave all the secret display stuff out of this PR and this should build. |
According to #588, BIP32 does not support uncompressed keys and using type with compression flag is a mistake
@Kixunil @sanket1729 sorry it was my bad: probably was too tired yesterday and thought that I had no-std |
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 cf0c48c
It's a little eyebrow-raising that the nostd Debug
impl just silently omits the secret key, but whatever. I have no opinions about the renaming, though I think after we get all the key-changing PRs in (but before we do the Taproot release) we should go through everything and possibly do a rename cleanup.
@apoelstra there is not a lot of options for |
Right, I understand that we have no good options here. I think I might've stuck the literal string |
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.
@@ -638,6 +659,20 @@ impl ExtendedPubKey { | |||
} | |||
} | |||
|
|||
/// Constructs ECDSA compressed public key matching internal public key representation. | |||
pub fn to_pub(&self) -> ecdsa::PublicKey { | |||
ecdsa::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.
Note that there would be some conflicts with #757
…n ECDSA a6e8f58 PSBT BIP32 keys moved to Secp256k1 from bitcoin ECDSA (Dr Maxim Orlovsky) Pull request description: Fourth step in implementation of Schnorr key support after #588. This PR is a follow-up to non-API breaking #589 and API-breaking #590, which must be reviewed and merged first. ~~(The current PR includes all commits from #589 and #590, which should be reviewed there. The only commit specific to this PR is b8105e9)~~ UPDATE: All related PRs are merged now and this PR is ready for the review PR description: While PSBT BIP174 does not specify whether uncompressed keys are supported in BIP32-related fields, from BIP32 it follows that it is impossible to use uncompressed keys within the extended keys. This PR fixes this situation and is a companion to BIP174 PR clarifying key serialization: bitcoin/bips#1100 ACKs for top commit: apoelstra: ACK a6e8f58 sanket1729: ACK a6e8f58. Not sure which order to merge since there are many ready PRs which that would break each other. Tree-SHA512: 198ba646bbce1949b255a54a97957d952acdad8b7f9580be123116c0f44d773e6d90e0cac0d5993ec9a6b3328aa43aced0908522817861585877c50008fec835
This is third step required to introduce Schnorr key support according to #588. This PR starts API-breaking changes and is follow-up to non-API breaking #589, which is already merged.
PR rationale: BIP32 does not support uncompressed keys and using type with compression flag was a mistake