-
Notifications
You must be signed in to change notification settings - Fork 63
Add child key pair generation api #154
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 child key pair generation api #154
Conversation
|
@notmandatory @artfuldev please review this draft. |
src/bdk.udl
Outdated
| [Throws=BdkError] | ||
| ExtendedKeyInfo restore_extended_key(Network network, string mnemonic, string? password); | ||
| [Throws=BdkError] | ||
| ChildKeyPair derive_child_key_pair(string str_xprv, string str_path); |
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 not need to expose ExtendedPrivKey and DerivationPath? @notmandatory
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.
@notmandatory if this is the only way to create a child key pair, can it be a constructor?
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 modified the bdk-ff commit that the bdk-kotlin uses, and made same changes. After that, I added test for the deriveChildKeyPair() function
@Test
fun validChildKeys() {
val str_xprv = "tprv8ZgxMBicQKsPdMTRB73kkTfC665bK3JL33eAFZ3MUiGZj8jniKQroG2yuamoQcRb24GG8dKRmVgaisjcZrDc9L3fyMf2NW19oajw8iWSwMV"
val str_path = "m/84'/1'/0'/0"
val childKeyPair = deriveChildKeyPair(str_xprv, str_path)
assertEquals(
"[59a3697d/84'/1'/0'/0]tprv8hZWYaWfKprzVnSVaaxwYun2yVp9TgpvcosjrGYwgL9hKMU1Jsrp3xKsNCpxSNBSEgUPp7c8A1MYLQcMcq7Ab9eafzNZUHQaBtGBvfCQiMg/*",
childKeyPair.xprv
)
assertEquals(
"[59a3697d/84'/1'/0'/0]tpubDEFYgzYuUCYfPFUHUEdXxKS9YXL5d21qC7UX8nbF6bx69qimwGgQESwjYN52awKwDd5PWGKXWAw27EBUk6QzSK3eEwsK8odY8FxVn4poRDp/*",
childKeyPair.xpub
)
}
And It worked
(I used the CLI to derive this result first, and used the same key for testing)
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.
Not discounting that it works, just checking if the types are okay or if we need something more elaborate. and if child key pair could have a constructor instead of this function deriveChildKeyPair. also wondering if ChildKeyPair is what we want to expose - it could be any key pair, not necessarily only a "child" key pair. just thinking out loud.
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's fine to keep the types and design for this simple to start and not add additional types for ExtendedPrivKey and DerivationPath. They can always be added later if we find a need, I think most apps will 1. generate_extended_key and get the user to write down the mnemonic, 2) from the ExtendedKeyInfo.xprv or possibly from importing a xprv from somewhere else, derive a descriptor key, and 3) create a descriptor for the wallet, currently needs to be done putting strings together but in future we can make it simpler with some standard/common templates. That all said, I have two naming change suggestions:
- rename
ChildKeyPairtoDescriptorKeyInfowhich is more in line with whatbdkcalls it - rename arguments
str_xprvandstr_pathto simplyxprvandpathsince type is already enforced thus redundant in the variable name.
As for creating the ChildKeyPair or DescriptorKeyInfo with a constructor, if we do that we need to change it from dictionary (pass by value) to an interface (pass by reference), so I'd prefer to keep it a dictionary and not use a constructor.
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.
One other suggestion, in the case someone has a watch-only wallet then they won't have the xprv for the DescriptorKeyInfo and will only have the xpub. So I think you should make xprv optional, ie. string? xprv and xprv: Option<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.
Noted. Will force push the changes soon.
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.
Got it, thanks @notmandatory
artfuldev
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.
Just have some questions, mostly for @notmandatory
src/lib.rs
Outdated
| if let Secret(desc_seckey, _, _) = derived_xprv_desc_key { | ||
| let desc_pubkey = desc_seckey | ||
| .as_public(&secp) | ||
| .map_err(|e| Error::Generic(e.to_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.
The bdk-cli code here uses .unwrap() - @notmandatory can you add some context?
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.
The
bdk-clicode here uses.unwrap()- @notmandatory can you add some context?
I think maybe you're looking at an older version of bdk-cli 🙂 this is how the cli code works also without an .unwrap().
src/bdk.udl
Outdated
| [Throws=BdkError] | ||
| ExtendedKeyInfo restore_extended_key(Network network, string mnemonic, string? password); | ||
| [Throws=BdkError] | ||
| ChildKeyPair derive_child_key_pair(string str_xprv, string str_path); |
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.
@notmandatory if this is the only way to create a child key pair, can it be a constructor?
75d759e to
3291839
Compare
|
@artfuldev @notmandatory any suggestions here? |
src/lib.rs
Outdated
| network: Network, | ||
| word_count: WordCount, | ||
| password: Option<String>, | ||
| ) -> Self { |
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 we retain Result<T, E> semantics here? Did we remove it intentionally? I'd prefer not having .unwrap() and throwing exceptions in Kotlin etc.
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.
Got it, will add back the Result<T, E> and force push the changes.
3291839 to
2929f15
Compare
8069624 to
b2b3a8c
Compare
src/bdk.udl
Outdated
| }; | ||
|
|
||
| interface ExtendedPubKey { | ||
| string to_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.
Looks like this won't compile in Kotlin since toString() is an open, member function of Any that needs to be overriden and uniffi doesn't seem to have a way to declare an overriding API, as far as I can tell. A simple workaround here would be to rename to_string() to something else (maybe ExtendedPubKey#value or #raw read-only property?

)
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.
Filed an issue with uniffi to see if it's feasible to allow adding override modifier in Kotlin: mozilla/uniffi-rs#1286.
|
Overall looks good, I commented on a few places you can remove the |
b2b3a8c to
e538b72
Compare
e538b72 to
2a2ffde
Compare
|
I had a little chat with @thunderbiscuit and @dhruv-1001 and it looks like the logic in this PR probably does belong in |
|
@quad I've created a draft |
590ab37 to
2ab9e09
Compare
notmandatory
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.
Overall looks good! I'd just like the match statements cleaned up to avoid returning None and then unwrap(), which I don't think is as clear.
2ab9e09 to
e77e48c
Compare
e77e48c to
5bc3811
Compare
notmandatory
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.
ACK 5bc3811
Good work! this one was a challenging API to simplify for FFI and I think this way strikes the right balance.
5bc3811 to
5944756
Compare
|
Note to anyone following this issue, as discussed in bitcoindevkit/bdk#670 we decided not to put this logic in the |
9866649 Added Mnemonic Interface (dhruvbaliyan) Pull request description: ### Description This PR adds `interface Mnemonic` which will make the API to generate new DescriptorSecretKey have type-safe arguments. <!-- Describe the purpose of this PR, what's being adding and/or fixed --> ### Notes to the reviewers This PR doesn't have any issue linked to it, as it was discusses on a call during the implementation of `DescriptorSecretKey` (PR #154). It was discussed to make `Mnemonic` an interface and use that instead of string `Mnemonic` so that the API to generate `DescriptorSecretKey` doesn't have any potential failure (like in case it's provided with incorrect Mnemonic words). APIs added ``` // generates and returns Mnemonic with random entropy Mnemonic::new(word_count: WordCount) -> Self { ... } // converts string Mnemonic to Mnemonic type with error (in case of incorrect string Mnemonic) Mnemonic::from_str(mnemonic: String) -> Result<Self, BdkError> { ... } // generates and returns Mnemonic with given entropy Mnemonic::from_entropy(entropy: Vec<u8>) -> Result<Self, BdkError> {...} // view mnemonic as string Mnemonic::as_string(&self) -> String { ... } ``` Along with some changes to `DescriptorSecretKey::new()` to fit these new APIs ### Changelog notice ``` - Added Struct Mnemonic with following methods [#219] - new(word_count: WordCount) generates and returns Mnemonic with random entropy - from_str(mnemonic: String) converts string Mnemonic to Mnemonic type with error - from_entropy(entropy: Vec<u8>) generates and returns Mnemonic with given entropy - as_string() view Mnemonic as string - API removed [#219] - generate_mnemonic(word_count: WordCount) [#219](#219) ``` ### Checklists #### All Submissions: * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing #### New Features: * [x] I've added docs for the new feature #### Bugfixes: * [x] This pull request breaks the existing API * Top level function `generate_mnemonic(...)` was removed ACKs for top commit: thunderbiscuit: ACK 9866649. notmandatory: ACK 9866649 Tree-SHA512: 45f9158beb6fe7bfe2a901c3f17126db855fe0b4b479ecb2a16381e06a415eed295fe6be3c840bd1d1fc8cffaf58bd97dc27bdc1e82699367a827d700e8fd09b
Completes #87