-
Notifications
You must be signed in to change notification settings - Fork 46
feature: Add Caravan wallet format import/export support #205
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?
feature: Add Caravan wallet format import/export support #205
Conversation
Pull Request Test Coverage Report for Build 18456006387Warning: This coverage report may be inaccurate.This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.
Details
💛 - Coveralls |
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 this will be a good addition to have. After doing a first pass my main concern is the amount of string manipulation that's required, and would prefer to use stronger types where possible, for example Fingerprint, DerivationPath, and Xpub.
|
@vkprogrammer-001 Any interest in keeping this PR up to date? If not we can probably find someone to pick it up. |
|
I had been thinking about this in the context of the multi-keychain wallet PRs and the new |
0b17dbd to
e95df66
Compare
Yes @ValuedMammal , I am interested in completing this PR. I have addressed all the comments and made the necessary changes. PTAL and let me know if there are any further changes needed. |
ValuedMammal
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.
Thank you @vkprogrammer-001
| /// ExtendedPublicKey structure for Caravan wallet format | ||
| #[derive(Debug, Clone)] | ||
| pub struct CaravanExtendedPublicKey { |
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 can still derive Deserialize for this right?
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.
Yes, definitely! Deserialize can still be derived for CaravanExtendedPublicKey. The custom implementation I added ensures proper handling of the "m/" prefix in the JSON format while internally using correct DerivationPath types.
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.
Right but I think rust-bitcoin can already parse a DerivationPath from a string with or without the m/ prefix.
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 are right about this. Rust-bitcoin can parse a DerivationPath from a string with or without the m/ prefix. I made the changes.
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 that case I think it would be better to derive Deserialize for CaravanExtendedPublicKey rather than manually implementing it. And probably also include a #[serde(rename = "bip32Path")] attribute.
ValuedMammal
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.
I added a few comments.
wallet/src/wallet/export.rs
Outdated
| // Combine with origin path if present | ||
| match &xpub_key.origin { | ||
| Some((_, origin_path)) => origin_path.extend(&base), | ||
| None => base, | ||
| } |
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 had another question about the derivation path. I think we only care about the origin path here. If there are any steps after the xpub, then I would think they'd be appended to the end of the xpub rather than the origin. but I'm not even sure this is supported by caravan.
Are you testing this on a descriptor with more than one step after the xpub (excluding wildcard)? because so far it looks like base_vec is always empty.
To illustrate what I mean you can refer to a similar implementation I did while reviewing this. master...ValuedMammal:bdk_wallet:feat/caravan_export
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.
@vkprogrammer-001 I might have had that wrong. I think it makes sense to combine the paths. Still a test would probably help.
If it's relevant maybe we add another field derivation_path to CaravanExtendedPublicKey for storing the xpub.derivation_path and reserve bip32_path for the origin.
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 follow-up! I agree, combining the origin and derivation paths matches Caravan’s expectations. I’ve added a test to confirm the path handling works correctly, including cases with multiple derivation steps. For now, I’m keeping a single bip32_path field since Caravan expects the full combined path, but I’ve documented the option to split into origin and derivation paths if needed in the future.
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 issue I'm seeing is that this won't effectively "round-trip" back to same descriptors, e.g. descriptor_str -> Wallet -> CaravanExport -> to_descriptors. But I don't think it's a deal breaker for this PR as long as we communicate that the import/export is a 1-way operation, i.e.
Wallet -> CaravanExport
Or
JSON -> CaravanExport -> to_descriptors
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're absolutely right about the round-trip limitation. I understand that the derivation path handling means descriptor_str -> Wallet -> CaravanExport -> to_descriptors won't produce identical descriptors due to how we combine paths for Caravan compatibility.
I agree this is acceptable for the intended use cases - the functionality is designed for one-way operations (either exporting to Caravan or importing from Caravan), not for round-trip preservation. I'll add documentation to clearly communicate this limitation so users understand these are one-way operations.
|
When this is close to being ready would you mind please dropping the merge commit 9f86668 and just rebase the changes onto current master. |
2b654a1 to
7a90425
Compare
@ValuedMammal I checked my branch history and the merge commit 9f86668 is not present, so there’s nothing to drop. My branch is now rebased onto the latest master and ready for review. PTAL |
|
Sorry, but it looks like it needs yet another rebase. |
I’ve rebased it again. Everything looks good now. PTAL |
|
This PR has 13 commits, and I think only the first 3 are actually meant to be here. A complete cleanup of the PR would require rebasing (not merging master back into this PR branch) and dropping all unnecessary commits. You should end up with 3 commits that extend e2cf34d (currently latest on master). |
3de7c59 to
7a90425
Compare
…an export This commit addresses @ValuedMammal feedback by replacing extensive string manipulation with proper Bitcoin types throughout the Caravan wallet export functionality, making the code more type-safe and less error-prone. Key Changes: - Type-safe BIP32 handling: Updated CaravanExtendedPublicKey to use bitcoin::bip32::{DerivationPath, Fingerprint, Xpub} instead of strings. Added custom serde implementation that properly handles "m/" prefix for JSON compatibility while using proper types internally. - Enhanced descriptor parsing: Changed extract_xpubs_from_multi() to accept SortedMultiVec<DescriptorPublicKey, Ctx> instead of SortedMultiVec<String, Ctx>, eliminating string parsing errors and making the code more robust. - Enum-based address types: Replaced string-based address type handling with CaravanAddressType enum (P2SH, P2WSH, P2SHWrappedP2WSH) for better type safety and validation. - Proper descriptor construction: Updated to_descriptors() to use Descriptor::new_sh_sortedmulti(), new_wsh_sortedmulti(), and new_sh_wsh_sortedmulti() construction methods instead of string building, ensuring standard format with checksums. - Network type conversion: Replaced string-based network handling with CaravanNetwork enum and From<bitcoin::Network> implementation using NetworkKind pattern. - Comprehensive validation: Enhanced test suite to verify that exported descriptors can create functional BDK wallets with proper address generation, ensuring practical usability.
- Simplified DerivationPath parsing by relying on rust-bitcoin's native support for m/ prefix. - Used wallet.public_descriptor directly to avoid unnecessary descriptor re-parsing. - Clarified and documented logic for combining origin and derivation paths for Caravan compatibility. - Added tests to verify correct handling of complex derivation paths and serialization/deserialization of CaravanExtendedPublicKey. - Ensured all changes are backward compatible and maintain expected Caravan export behavior.
7a90425 to
0b67cd1
Compare
@thunderbiscuit Thanks for the feedback! I've cleaned up the PR by rebasing and keeping only the 3 relevant commits extending from the latest master (e2cf34d). The branch now has a clean history with just the Caravan wallet export functionality. Ready for review. PTAL |
|
ACK 0b67cd1 I left some remaining comments #205 (comment) #205 (comment). |
Add documentation explaining that Caravan export/import operations are designed as one-way operations and that full round-trip conversions (Descriptor → Wallet → CaravanExport → to_descriptors) will not produce identical descriptors due to derivation path processing for Caravan compatibility.
|
Hey @ValuedMammal, I've added documentation about the round-trip limitation we discussed. The note explains that these are intended as one-way operations for Caravan compatibility. PTAL |
|
ACK c624e85 |
Description
This PR implements support for importing and exporting wallets in the Caravan wallet format as requested in issue #5.
Changes
CaravanExportstruct to handle Caravan wallet formatImplementation Details
Testing
The implementation includes tests for:
Checklists
All Submissions:
cargo fmtandcargo clippybefore committingCloses #5