Skip to content

Conversation

@vkprogrammer-001
Copy link
Contributor

Description

This PR implements support for importing and exporting wallets in the Caravan wallet format as requested in issue #5.

Changes

  • Added CaravanExport struct to handle Caravan wallet format
  • Implemented methods to export a wallet to Caravan JSON format
  • Added functionality to import Caravan wallet configs and convert to BDK descriptors
  • Added documentation with usage examples
  • Added comprehensive test coverage for all supported multisig types (P2SH, P2WSH, P2SH-P2WSH)

Implementation Details

  • All code is contained in the wallet/export.rs module alongside the existing FullyNoded export functionality
  • Support for different address types required by Caravan (P2SH, P2WSH, P2SH-P2WSH)
  • Handles the mapping between BDK descriptor format and Caravan's JSON configuration format

Testing

The implementation includes tests for:

  • Exporting BDK wallets to Caravan format
  • Importing Caravan configurations to BDK wallets
  • All supported address types (P2SH, P2WSH, P2SH-P2WSH)
  • Network detection (mainnet/testnet)

Checklists

All Submissions:

  • I've signed all my commits
  • I followed the contribution guidelines
  • I ran cargo fmt and cargo clippy before committing

Closes #5

@coveralls
Copy link

coveralls commented Apr 10, 2025

Pull Request Test Coverage Report for Build 18456006387

Warning: 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

  • 353 of 386 (91.45%) changed or added relevant lines in 2 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage decreased (-0.1%) to 85.111%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/wallet/export.rs 352 385 91.43%
Totals Coverage Status
Change from base Build 18099939060: -0.1%
Covered Lines: 7260
Relevant Lines: 8530

💛 - Coveralls

@notmandatory notmandatory moved this to In Progress in BDK Wallet Apr 21, 2025
@ValuedMammal ValuedMammal moved this from In Progress to Needs Review in BDK Wallet Aug 5, 2025
@ValuedMammal ValuedMammal added this to the Wallet 3.0.0 milestone Aug 5, 2025
Copy link
Collaborator

@ValuedMammal ValuedMammal left a 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.

@ValuedMammal
Copy link
Collaborator

@vkprogrammer-001 Any interest in keeping this PR up to date? If not we can probably find someone to pick it up.

@thunderbiscuit
Copy link
Member

I had been thinking about this in the context of the multi-keychain wallet PRs and the new KeyRing type. This sort of stuff is a really good fit for the keyring, because it could simply have a new constructor on the type which passes these backup formats (similar to how we think of doing it for bitcoin core) as an argument, and bam you're done. This can be extended to other export formats too on popular wallets.

@ValuedMammal ValuedMammal moved this from Needs Review to In Progress in BDK Wallet Sep 11, 2025
@vkprogrammer-001 vkprogrammer-001 force-pushed the feature/caravan-wallet-export branch from 0b17dbd to e95df66 Compare September 21, 2025 13:21
@vkprogrammer-001
Copy link
Contributor Author

vkprogrammer-001 commented Sep 21, 2025

@vkprogrammer-001 Any interest in keeping this PR up to date? If not we can probably find someone to pick it up.

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.

Copy link
Collaborator

@ValuedMammal ValuedMammal left a 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

Comment on lines +278 to +280
/// ExtendedPublicKey structure for Caravan wallet format
#[derive(Debug, Clone)]
pub struct CaravanExtendedPublicKey {
Copy link
Collaborator

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?

Copy link
Contributor Author

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.

Copy link
Collaborator

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.

Copy link
Contributor Author

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.

Copy link
Collaborator

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.

Copy link
Collaborator

@ValuedMammal ValuedMammal left a 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.

Comment on lines 612 to 616
// Combine with origin path if present
match &xpub_key.origin {
Some((_, origin_path)) => origin_path.extend(&base),
None => base,
}
Copy link
Collaborator

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

Copy link
Collaborator

@ValuedMammal ValuedMammal Sep 23, 2025

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.

Copy link
Contributor Author

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.

Copy link
Collaborator

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

Copy link
Contributor Author

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.

@ValuedMammal
Copy link
Collaborator

When this is close to being ready would you mind please dropping the merge commit 9f86668 and just rebase the changes onto current master.

@vkprogrammer-001 vkprogrammer-001 force-pushed the feature/caravan-wallet-export branch 3 times, most recently from 2b654a1 to 7a90425 Compare September 27, 2025 18:31
@vkprogrammer-001
Copy link
Contributor Author

When this is close to being ready would you mind please dropping the merge commit 9f86668 and just rebase the changes onto current master.

@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

@ValuedMammal ValuedMammal moved this from In Progress to Needs Review in BDK Wallet Sep 27, 2025
@ValuedMammal ValuedMammal self-requested a review September 27, 2025 19:24
@oleonardolima
Copy link
Contributor

Sorry, but it looks like it needs yet another rebase.

@vkprogrammer-001
Copy link
Contributor Author

Sorry, but it looks like it needs yet another rebase.

I’ve rebased it again. Everything looks good now. PTAL

@thunderbiscuit
Copy link
Member

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).

@vkprogrammer-001 vkprogrammer-001 force-pushed the feature/caravan-wallet-export branch from 3de7c59 to 7a90425 Compare October 4, 2025 09:04
…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.
@vkprogrammer-001 vkprogrammer-001 force-pushed the feature/caravan-wallet-export branch from 7a90425 to 0b67cd1 Compare October 4, 2025 09:31
@vkprogrammer-001
Copy link
Contributor Author

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).

@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

@ValuedMammal
Copy link
Collaborator

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.
@vkprogrammer-001
Copy link
Contributor Author

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

@ValuedMammal
Copy link
Collaborator

ACK c624e85

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new feature New feature or request

Projects

Status: Needs Review

Development

Successfully merging this pull request may close these issues.

Extend wallet::export module to also import/export the Caravan wallet format

5 participants