Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add PsbtInputExt::update_with_descriptor #339

Merged
merged 5 commits into from
Apr 30, 2022

Conversation

LLFourn
Copy link
Contributor

@LLFourn LLFourn commented Apr 7, 2022

Fixes #335

This populates all relevant fields in a PSBT input according to a descriptor. Includes previously not yet implemented logic for populating bip32_derivation and tap_key_origins. Also optionally checks the witness_utxo and non_witness_utxo.

I renamed PsbtExt::update_desc to PsbtExt::update_inp_with_descriptor which calls update_with_descriptor
internally but retrieves the vout for the input for the non_witness_utxo check automatically. I removed the derivation range functionality because it didn't feel useful (and if it is then it probably belongs in a separate function). Let me know if you want me to add it back where it was or to provide an additional utility function to find which index a script pubkey was derived from.

@LLFourn LLFourn force-pushed the psbt_update_input branch 6 times, most recently from 3cb78d8 to d9c5c99 Compare April 7, 2022 06:18
@sanket1729
Copy link
Member

I removed the derivation range functionality because it didn't feel useful (and if it is then it probably belongs in a separate function)

Sorry for the delayed review. I was busy with the conference in Miami. I was trying to emulate bitcoin core's utxoupdatepsbt RPC. I think having the range in the descriptor is certainly useful to scan the output because users don't have to derive the output then. It might be useful in the case where the creator(the entity creating the psbt) is separate from the updater (the entity updating the psbt with utxo information).

But I do think that you are right that this functionality belongs in a separate method. Perhaps it should be a method to Descriptor<DescriptorPublicKey> instead of Psbt.

Copy link
Member

@sanket1729 sanket1729 left a comment

Choose a reason for hiding this comment

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

Did an initial review. Do you need this for the 7.0.0 release of rust-miniscript? I think this might take some time to get iterate.

I would prefer to get a release out for 7.0.0 out as soon as rust-bitcoin 0.28.0 is out. If rust-bitcoin is delayed even further maybe we can get this in 7.0.0 too.

src/descriptor/mod.rs Outdated Show resolved Hide resolved
src/psbt/mod.rs Outdated Show resolved Hide resolved
src/psbt/mod.rs Outdated Show resolved Hide resolved
src/psbt/mod.rs Outdated Show resolved Hide resolved
src/psbt/mod.rs Outdated Show resolved Hide resolved
src/psbt/mod.rs Outdated
}
}
// only set the input once all the checks have passed
*self = inp;
Copy link
Member

Choose a reason for hiding this comment

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

Can you please make the above into a separate function that only operates on inp.

    fn update_with_descriptor(
        &mut self,
        ...
        )  -> ...
 {
 	let mut inp = self.clone();
 	update_with_descriptor_helper(&mut inp, ...);
 	*self = inp;
 }

This way we can be sure that we don't accidentally mutate self in the function body in the future.

We should also avoid the allocation here, but I can do that might be complicated code-wise. I will perhaps address that in future as a low-priority issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I removed the allocation with a helper function that both the checked and unchecked version use in: 8c8f82b

The idea is that the helper optionally checks the SPK is correct before it mutates anything.

src/psbt/mod.rs Outdated Show resolved Hide resolved
src/psbt/mod.rs Outdated Show resolved Hide resolved
@LLFourn
Copy link
Contributor Author

LLFourn commented Apr 11, 2022

I removed the derivation range functionality because it didn't feel useful (and if it is then it probably belongs in a separate function)

Sorry for the delayed review. I was busy with the conference in Miami. I was trying to emulate bitcoin core's utxoupdatepsbt RPC. I think having the range in the descriptor is certainly useful to scan the output because users don't have to derive the output then. It might be useful in the case where the creator(the entity creating the psbt) is separate from the updater (the entity updating the psbt with utxo information).

On the contrary, that was very fast considering. Thanks.

But I do think that you are right that this functionality belongs in a separate method. Perhaps it should be a method to Descriptor<DescriptorPublicKey> instead of Psbt.

Will do. In general couldn't these update PSBT functions go on Descriptor? I was guessing they were on an extension trait because you wanted to strictly keep anything to do with PSBTs in one module. Perhaps we could just add additional impls on Descriptor<DescriptorPublicKey> in psbt/mod.rs (or maybe psbt/descriptor_ext.rs)?

Did an initial review. Do you need this for the 7.0.0 release of rust-miniscript? I think this might take some time to get iterate.

I would prefer to get a release out for 7.0.0 out as soon as rust-bitcoin 0.28.0 is out. If rust-bitcoin is delayed even further maybe we can get this in 7.0.0 too.

I think we will only be blocked on this by sometime in mid-may so I could wait for a follow up release around then. I'll try keep this PR as ready as possible as I can though in case we can sneak it in to 7.0.0 :)

Working on it!

Copy link
Member

@sanket1729 sanket1729 left a comment

Choose a reason for hiding this comment

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

Addressed some comments

src/descriptor/mod.rs Outdated Show resolved Hide resolved
src/psbt/mod.rs Outdated Show resolved Hide resolved
src/psbt/mod.rs Outdated Show resolved Hide resolved
@LLFourn LLFourn force-pushed the psbt_update_input branch 2 times, most recently from 9009f1f to 88e49fe Compare April 11, 2022 05:19
@LLFourn LLFourn marked this pull request as draft April 11, 2022 11:34
@LLFourn
Copy link
Contributor Author

LLFourn commented Apr 11, 2022

Made it a draft while it's not ready for merge. I've changed the API so there's a checked and unchecked version of the update input method. The checked version is on the PSBT (where the full data is there so we can do the non_witness_utxo txid check) and the unchecked version is on the input. It think it's better :)

What remains:

  1. Fix pkh keys not being included in tapscripts (test that fails has been done)
  2. Create new separate test for the checked version of the function.
  3. separate the checked version into helper as mentioned here: Add PsbtInputExt::update_with_descriptor #339 (comment)

As a heads up I don't think I'm going to be able to work on this again until Friday.

@sanket1729
Copy link
Member

I like the latest commit. Will do another round of review tomorrow.

@LLFourn LLFourn force-pushed the psbt_update_input branch from 8c8f82b to acf6ddb Compare April 15, 2022 01:32
@LLFourn
Copy link
Contributor Author

LLFourn commented Apr 15, 2022

This is almost ready it just needs some tests on the checked version of the function. I've just moved logic to a helper function and removed the clone of the PSBT. I also stop requiring that non_witness_utxo is present for segwitv0 descriptors since this is easy to enforce outside. Here's what's changed so far for CHANGELOG:

  1. Rename update_desc to update_input_with_descriptor which additionally populates bip32_derivation and tap_key_origins and more thoroughly checks the validity of witness_utxo and non_witness_utxo.
  2. Add PsbtInputExt::update_with_descriptor_unchecked which operates on an psbt::Input the above but without the checks.
  3. Add is_segwit to DescriptorType

@LLFourn LLFourn marked this pull request as ready for review April 15, 2022 01:44
@afilini
Copy link
Contributor

afilini commented Apr 15, 2022

I was quickly skimming through the code and noticed:

The descriptor must not have any wildcards in it otherwise an error will be returned however it can (and should) have extended keys in it.

I wonder if I should prioritize my work upstreaming the equivalent of the DerivedDescriptorKey type that we have in BDK, which would maybe help the users make less mistakes by enforcing rules with the type system.

Sidenote: I love all these new traits and various bits of utilities being exposed in miniscript. We currently have our own in BDK, but I'll be very happy to delete some code and start using yours!

@@ -259,6 +260,19 @@ pub enum DescriptorType {
Tr,
}

impl DescriptorType {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would also add a is_taproot() method, even though it will be unused here in miniscript. I'm sure somebody will find that useful!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe segwit_version(&self) -> Option<u8> would be the most general?

@LLFourn
Copy link
Contributor Author

LLFourn commented Apr 15, 2022

hey @afilini. Thanks for taking a look.

I was quickly skimming through the code and noticed:

The descriptor must not have any wildcards in it otherwise an error will be returned however it can (and should) have extended keys in it.

I wonder if I should prioritize my work upstreaming the equivalent of the DerivedDescriptorKey type that we have in BDK, which would maybe help the users make less mistakes by enforcing rules with the type system.

Indeed it would be good to have some type safety here for keys that are wildcard free (but might require further derivation).

Sidenote: I love all these new traits and various bits of utilities being exposed in miniscript. We currently have our own in BDK, but I'll be very happy to delete some code and start using yours!

FWIW the context of this PR is that I was porting your work from BDK to my sketch of bdk_core and saw that this lib was offering some of the same functionality already but just needed to set the key origin stuff. I think it would great if it could all be maintained here.

Copy link
Member

@sanket1729 sanket1729 left a comment

Choose a reason for hiding this comment

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

concept ACK and code review ACK. Can you please squash this into cleaner commits?


if let Some(check_script) = check_script {
if check_script != derived.script_pubkey() {
return Ok((derived, false));
Copy link
Member

Choose a reason for hiding this comment

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

nit: I can see why you did this. Can we get away with returning Option<Descriptor> ?

If it's too much effort, you can add a comment explaining the return type of the function. (i.e the bool denotes the spk check)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I opted to not do Option<...> because it would mean an unwrap for one of the unchecked caller which seemed like the worse option. I added a comment explaining the awkwardness.

@LLFourn LLFourn force-pushed the psbt_update_input branch from acf6ddb to 4ddc565 Compare April 17, 2022 22:24
@LLFourn
Copy link
Contributor Author

LLFourn commented Apr 17, 2022

I've just added a test for the checked version of the function and cleaned up some comments/naming. Commits have been squashed. There's one on the end which adds segwit_version instead of is_segwit which hasn't been reviewed yet.

Updated CHANGELOG

- Rename `update_desc` to `update_input_with_descriptor` which additionally populates `bip32_derivation` and `tap_key_origins` and more thoroughly checks the validity of `witness_utxo` and `non_witness_utxo`.
- Add `PsbtInputExt::update_with_descriptor_unchecked` which operates on an `psbt::Input` and does the above but without the checks.
- Add `find_derivation_index_for_spk` which allows you to scan a derivation range to find the one that produced a script pubkey (this feature was extracted and removed from `update_desc`).
- Add `segwit_version` to `DescriptorType`

Ready to go from my perspective :)

@LLFourn LLFourn force-pushed the psbt_update_input branch from 4ddc565 to 51031b5 Compare April 21, 2022 08:55
@LLFourn
Copy link
Contributor Author

LLFourn commented Apr 21, 2022

Rebased on master.

@LLFourn
Copy link
Contributor Author

LLFourn commented Apr 21, 2022

CI failed but I don't think it's because of the PR.

@sanket1729
Copy link
Member

Another rebase should resolve the CI issue. We just merged #363

To replace the functionality lost by changing `update_desc`
@LLFourn LLFourn force-pushed the psbt_update_input branch from 51031b5 to 1bd31e6 Compare April 25, 2022 02:11
@LLFourn
Copy link
Contributor Author

LLFourn commented Apr 25, 2022

Rebased again :)

[EDIT] Also made some doc fixups.

LLFourn added 2 commits April 25, 2022 12:24
This populates all relevant fields in a PSBT input according to a
descriptor. Includes previously not yet implemented logic for populating
bip32_derivation and tap_key_origins.

I renamed "PsbtExt::update_desc" to
"PsbtExt::update_input_with_descriptor" which uses the same logic as
update_with_descriptor internally but additionally does some sanity
checks. I removed scanning logic for update_desc because you can do it
with find_derivation_index_for_spk.
@LLFourn LLFourn force-pushed the psbt_update_input branch from 1bd31e6 to e85b99c Compare April 25, 2022 02:24
sanket1729
sanket1729 previously approved these changes Apr 27, 2022
Copy link
Member

@sanket1729 sanket1729 left a comment

Choose a reason for hiding this comment

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

ACK e85b99c. Left a small comment, that need not be addressed here.

Will wait sometime for a review from @apoelstra

src/descriptor/mod.rs Outdated Show resolved Hide resolved
@sanket1729 sanket1729 mentioned this pull request Apr 27, 2022
Copy link
Member

@tcharding tcharding left a comment

Choose a reason for hiding this comment

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

My review adds very little of value, please do not implement suggestions unless you are rebasing anyways. I have next to zero knowledge of rust-miniscript.

src/descriptor/mod.rs Outdated Show resolved Hide resolved
src/psbt/mod.rs Show resolved Hide resolved
/// Note that his method doesn't check that the `witness_utxo` or `non_witness_utxo` is
/// consistent with the descriptor. To do that see [`update_input_with_descriptor`].
///
/// ## Return value
Copy link
Member

Choose a reason for hiding this comment

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

I didn't mention it at other places so as not to derail your PR with docs issues but since you add a section header here might as well mention it, by convention I believe this should be

Suggested change
/// ## Return value
/// # Returns

An example can be seen here: https://doc.rust-lang.org/rustdoc/documentation-tests.html

(Although while digging for a reference for you I found many non-uniform examples :(

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can't find this convention on that page?

Copy link
Member

@tcharding tcharding Apr 28, 2022

Choose a reason for hiding this comment

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

Soz for being confusing. The link shows the # Examples header not the # Returns header, I couldn't find a link with all the typical headers (Returns, Panics, Examplse ect.).

Copy link
Member

@tcharding tcharding Apr 28, 2022

Choose a reason for hiding this comment

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

No need to change the PR, I'm not at all fussed, I haven't even started shaking my docs clean up wand at rust-miniscript yet :)

src/psbt/mod.rs Show resolved Hide resolved
@LLFourn
Copy link
Contributor Author

LLFourn commented Apr 28, 2022

@sanket1729 @tcharding Thanks. I added two fixup commits to address the remarks. Seemed better to avoid doing more rebasing until @apoelstra has had a look. Unfortunately it looks like github still doesn't have a autosquash and merge feature so that'll have to be done manually once it's ready.

/// If it finds a match then it returns the index it was derived at and the concrete
/// descriptor at that index. If the descriptor is non-derivable then it will simply check the
/// script pubkey against the descriptor and return it if it matches (in this case the index
/// returned will be meaningless).
Copy link
Member

Choose a reason for hiding this comment

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

Cool, TIL we derive at an index not from an index. Cheers.

@tcharding
Copy link
Member

FTR my lack of ACK should not be seen to imply anything. I just don't know this code well enough to do so.

Copy link
Member

@sanket1729 sanket1729 left a comment

Choose a reason for hiding this comment

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

utACk f40dc83

Copy link
Member

@apoelstra apoelstra left a comment

Choose a reason for hiding this comment

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

ACK e85b99c

I didn't check that the PSBT logic is exactly what it should be, but ACK this API change (I think it's much cleaner than what we had before) as well as the implementation.

@sanket1729 sanket1729 merged commit 965a136 into rust-bitcoin:master Apr 30, 2022
@sanket1729
Copy link
Member

You ACKed a different tip, but I think we are good to merge this.

sanket1729 added a commit that referenced this pull request May 10, 2022
9cf065b Define a new type for derived `DescriptorPublicKey`s (Alekos Filini)

Pull request description:

  After a brief chat on IRC and in PR #339, here's a quick draft for a new type specific to derived `DescriptorPublicKey`s.

  This is the same design we use in BDK, where we wrap a normal `DescriptorPublicKey` and a secp context in a new struct. This has the drawback that the struct carries a lifetime and generic for the ctx, but I couldn't think of any other way to get this to work unless we drop the implementation of `ToPublicKey` for `DerivedDescriptorKey` which in my opinion can be very useful.

ACKs for top commit:
  apoelstra:
    ACK 9cf065b
  sanket1729:
    ACK 9cf065b

Tree-SHA512: 3dd486d52c589d104da9611c65229988825cc79115a1ff45d1a39aa63e49d2d2a76b40b21a278a58d0b8566b2b37682ffc5efbbb78779ef4e5dadeaf14bbcc6c
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

PsbtEx::update_desc include derivation paths for bip32 keys
5 participants