Skip to content
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

Make Wallet require a change descriptor #1390

Merged

Conversation

ValuedMammal
Copy link
Contributor

@ValuedMammal ValuedMammal commented Mar 27, 2024

All Wallet constructors are modified to require a change descriptor, where previously it was optional. Additionally we enforce uniqueness of the change descriptor to avoid ambiguity when deriving scripts and ensure the wallet will always have two distinct keystores.

Notable changes

  • Add error DescriptorError::ExternalAndInternalAreTheSame
  • Remove error CreateTxError::ChangePolicyDescriptor
  • No longer rely on map_keychain

fixes #1383

Notes to the reviewers

Changelog notice

Changed:

Constructing a Wallet now requires two distinct descriptors.

Checklists

All Submissions:

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

@ValuedMammal
Copy link
Contributor Author

ValuedMammal commented Mar 27, 2024

Do we want to keep the change descriptor optional for example_cli? I'm guessing it won't hurt anything since we don't actually use the wallet.

Copy link
Contributor

@storopoli storopoli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments

crates/bdk/examples/compiler.rs Outdated Show resolved Hide resolved
crates/bdk/examples/compiler.rs Outdated Show resolved Hide resolved
crates/bdk/src/descriptor/error.rs Outdated Show resolved Hide resolved
crates/bdk/src/descriptor/template.rs Outdated Show resolved Hide resolved
crates/bdk/src/descriptor/template.rs Outdated Show resolved Hide resolved
crates/bdk/src/descriptor/template.rs Outdated Show resolved Hide resolved
crates/bdk/src/descriptor/template.rs Outdated Show resolved Hide resolved
crates/bdk/src/wallet/export.rs Outdated Show resolved Hide resolved
@ValuedMammal ValuedMammal force-pushed the refactor/wallet-descriptor branch 2 times, most recently from edd290d to 238c292 Compare March 27, 2024 23:30
@ValuedMammal ValuedMammal marked this pull request as ready for review March 27, 2024 23:30
@casey-bowman
Copy link
Contributor

casey-bowman commented Mar 28, 2024

If two descriptors are the same except that each has a different extended public key that is replaced by the corresponding extended private key, do these qualify as distinct?

Also, to clarify, the receive descriptors for any two wallets must also be distinct with this change, too, correct?
UPDATE - Oh, I see the issue #1383

Copy link
Member

@evanlinjin evanlinjin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm concerned with the way we are checking whether the 2 keychains are "distinct". I have a few questions.

  1. How can we guarantee that into_wallet_descriptor returns the same public-descriptor string for all non-distinct descriptors?
  2. How can we ensure that a non-wildcard descriptor and a wildcard descriptor has no overlaps (the non-wildcard descriptor's spk can be derived from the wildcard descriptor).

For 2. also refer to my comment here: #1383 (comment)

crates/bdk/src/wallet/mod.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@LLFourn LLFourn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 04c8750

I'm going to be away so not going to get another chance to look at this. It LGTM. Feel free to keep shedding. Thanks for taking on this rather tedious PR.

crates/bdk/src/descriptor/error.rs Outdated Show resolved Hide resolved
@ValuedMammal ValuedMammal force-pushed the refactor/wallet-descriptor branch 2 times, most recently from e3ba751 to cba43de Compare April 5, 2024 13:28
@ValuedMammal
Copy link
Contributor Author

Small changes

  • Fixed key_* names in descriptor template docs
  • Changed the new descriptor error to just ExternalAndInternalAreTheSame

@ValuedMammal
Copy link
Contributor Author

1. How can we guarantee that `into_wallet_descriptor` returns the same public-descriptor string for all non-distinct descriptors?

2. How can we ensure that a non-wildcard descriptor and a wildcard descriptor has no overlaps (the non-wildcard descriptor's spk can be derived from the wildcard descriptor).

To point 1) I don't think you're objecting to checking equality for two values of Descriptor<DescriptorPublicKey>, which is only true for identical descriptors, i.e. ones that derive 100% of the same scripts. Maybe this was addressed in the discussion around point 2?

Note that the public descriptor will be common whether the wallet is given a public or private descriptor. So for example this should pass:

#[test]
fn same_descriptor_public_private() {
    // public + private of same descriptor should fail to create wallet
    let desc = "wpkh(tprv8ZgxMBicQKsPdcAqYBpzAFwU5yxBUo88ggoBqu1qPcHUfSbKK1sKMLmC7EAk438btHQrSdu3jGGQa6PA71nvH5nkDexhLteJqkM4dQmWF9g/84'/1'/0'/0/*)";
    let change = "wpkh([3c31d632/84'/1'/0']tpubDCYwFkks2cg78N7eoYbBatsFEGje8vW8arSKW4rLwD1AU1s9KJMDRHE32JkvYERuiFjArrsH7qpWSpJATed5ShZbG9KsskA5Rmi6NSYgYN2/0/*)";

    let err = Wallet::new_no_persist(desc, change, Network::Testnet);
    assert!(matches!(
        err,
        Err(DescriptorError::ExternalAndInternalAreTheSame),
    ));
}

@ValuedMammal
Copy link
Contributor Author

Rebased on 358e842

Copy link
Contributor

@storopoli storopoli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK cddefbb

@ValuedMammal
Copy link
Contributor Author

Rebased on 7607b49 and added two commits, but this may need more discussion and testing to decide if it's a reasonable solution

Copy link
Contributor

@storopoli storopoli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally agree with the infallible.

ACK aec4dda

Note that you might want to update the original PR message details.

@notmandatory
Copy link
Member

notmandatory commented Jun 1, 2024

Note that the public descriptor will be common whether the wallet is given a public or private descriptor. So for example this should pass:

#[test]
fn same_descriptor_public_private() {
    // public + private of same descriptor should fail to create wallet
    let desc = "wpkh(tprv8ZgxMBicQKsPdcAqYBpzAFwU5yxBUo88ggoBqu1qPcHUfSbKK1sKMLmC7EAk438btHQrSdu3jGGQa6PA71nvH5nkDexhLteJqkM4dQmWF9g/84'/1'/0'/0/*)";
    let change = "wpkh([3c31d632/84'/1'/0']tpubDCYwFkks2cg78N7eoYbBatsFEGje8vW8arSKW4rLwD1AU1s9KJMDRHE32JkvYERuiFjArrsH7qpWSpJATed5ShZbG9KsskA5Rmi6NSYgYN2/0/*)";

    let err = Wallet::new_no_persist(desc, change, Network::Testnet);
    assert!(matches!(
        err,
        Err(DescriptorError::ExternalAndInternalAreTheSame),
    ));
}

This test does pass and I think is an adequate level of error checking. If we run into cases where users are adding duplicates descriptors that aren't caught we can add more sophisticated checking later and it won't break the API.

It's good documentation to add this test to tests/wallet.rs and I think you need a rebase and then this one looks ready to merge.

Copy link
Member

@notmandatory notmandatory left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK aec4dda

Copy link
Contributor

@storopoli storopoli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK aec4dda

@ValuedMammal
Copy link
Contributor Author

@storopoli I took your advice about more precise wording from this comment #1390 (comment) and I even put the variable inside the format string in 0ffcd9a :)

Copy link
Contributor

@storopoli storopoli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 0ffcd9a

All `Wallet` constructors are modified to require a change
descriptor, where previously it was optional. Additionally
we enforce uniqueness of the change descriptor to avoid
ambiguity when deriving scripts and ensure the wallet will
always have two distinct keystores.

Notable changes

* Add error DescriptorError::ExternalAndInternalAreTheSame
* Remove error CreateTxError::ChangePolicyDescriptor
* No longer rely on `map_keychain`
Copy link
Member

@notmandatory notmandatory left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re-ACK 8bc3d35

@notmandatory notmandatory merged commit c5a3b62 into bitcoindevkit:master Jun 6, 2024
12 checks passed
@ValuedMammal ValuedMammal deleted the refactor/wallet-descriptor branch June 10, 2024 21:38
@notmandatory notmandatory mentioned this pull request Jun 14, 2024
31 tasks
ValuedMammal added a commit to ValuedMammal/bdk that referenced this pull request Aug 1, 2024
The change descriptor is made optional, making this an effective
reversion of bitcoindevkit#1390 and enables creating wallets with a single
descriptor.
ValuedMammal added a commit to ValuedMammal/bdk that referenced this pull request Aug 1, 2024
The change descriptor is made optional, making this an effective
reversion of bitcoindevkit#1390 and enables creating wallets with a single
descriptor.
ValuedMammal added a commit to ValuedMammal/bdk that referenced this pull request Aug 1, 2024
The change descriptor is made optional, making this an effective
reversion of bitcoindevkit#1390 and enables creating wallets with a single
descriptor.
evanlinjin added a commit that referenced this pull request Aug 14, 2024
13e7008 doc(wallet): clarify docs for `Wallet::load` (valued mammal)
3951110 fix(wallet)!: Change method `LoadParams::descriptors` (valued mammal)
b802714 example(wallet): simplify miniscript compiler example (valued mammal)
2ca8b6f test(wallet): Add tests for single descriptor wallet (valued mammal)
31f1c2d fix(wallet): Change FromSql type to `Option<_>` (valued mammal)
75155b7 feat(wallet): Add method `Wallet::create_single` (valued mammal)

Pull request description:

  The change descriptor is made optional, making this an effective reversion of #1390 and enables creating wallets with a single descriptor.

  fixes #1511

  ### Notes to the reviewers

  PR 1390 also removed an error case [`ChangePolicyDescriptor`](https://github.com/bitcoindevkit/bdk/blob/8eef350bd08057acc39b6fc50b1217db5e29b968/crates/wallet/src/wallet/mod.rs#L1529-L1533) and this can possibly be added back. In the case the wallet only has a single descriptor we allow any utxos to fund a tx that aren't specifically marked unspendable regardless of the change spend policy.

  ### Changelog notice

  * Added method `Wallet::create_single` that expects a single `D: IntoWalletDescriptor` as input and enables building a wallet with no internal keychain.

  ### 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] This pull request breaks the existing API
  * [x] I've added tests for the new feature
  * [x] I've added docs for the new feature

ACKs for top commit:
  evanlinjin:
    ACK 13e7008

Tree-SHA512: 3e6fe5d9165d62332ac1863ec769c4bc5c7cd3c7f3fbdb8f505906bcdc681fa73b3fef2571adb0e52e9a23d4257f66a6145838b90ec68596b5f4c64054a047fa
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api A breaking API change module-wallet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Wallet must have unique Internal and External descriptors
6 participants