-
Notifications
You must be signed in to change notification settings - Fork 55
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
@@ -3,6 +3,8 @@ namespace bdk { | |||
ExtendedKeyInfo generate_extended_key(Network network, WordCount word_count, string? password); | |||
[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
ChildKeyPair
toDescriptorKeyInfo
which is more in line with whatbdk
calls it - rename arguments
str_xprv
andstr_path
to simplyxprv
andpath
since 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
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-cli
code 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
@@ -3,6 +3,8 @@ namespace bdk { | |||
ExtendedKeyInfo generate_extended_key(Network network, WordCount word_count, string? password); | |||
[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 throw
ing 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
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
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