Skip to content

Return error from at_derivation_index when descriptor has no wildcard#911

Open
febyeji wants to merge 1 commit intorust-bitcoin:masterfrom
febyeji:at-derivation-index-no-wildcard
Open

Return error from at_derivation_index when descriptor has no wildcard#911
febyeji wants to merge 1 commit intorust-bitcoin:masterfrom
febyeji:at-derivation-index-no-wildcard

Conversation

@febyeji
Copy link
Copy Markdown

@febyeji febyeji commented Mar 27, 2026

Closes #829.

  • Previously, calling at_derivation_index() on a descriptor without wildcards would silently ignore the index and return the descriptor unchanged. This was error-prone because callers would expect different indices to produce different addresses.
  • Callers that need to convert a non-wildcard descriptor to a definite one should use DefiniteDescriptorKey::new() on individual keys, or check has_wildcard() before calling at_derivation_index().
  • Add edge case tests for at_derivation_index wildcard check

Copy link
Copy Markdown
Contributor

@trevarj trevarj left a comment

Choose a reason for hiding this comment

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

But what should the workflow be if you just want to convert a DescriptorPublicKey descriptor to a DefiniteDescriptorKey one, not touching wildcards if they don't exist?

I'm ok with this as long as we address what Andrew said in the linked issue. I also would like a way to convert from Descriptor<DescriptorPublicKey> into Descriptor<DefiniteDescriptorKey> since I am currently using at_derivation_index as an agnostic translation.

Maybe it can be solved with just adding a helper function into_definite() or something.

@febyeji
Copy link
Copy Markdown
Author

febyeji commented Mar 28, 2026

Thanks for the review! Extracted a public into_definite() method which resolves the duplication and also addresses Andrew's question in the issue. find_derivation_index_for_spk now just calls self.into_definite()?.derived_descriptor(secp).

@febyeji febyeji force-pushed the at-derivation-index-no-wildcard branch from 6cad2d4 to cc5bded Compare March 31, 2026 05:33
@apoelstra
Copy link
Copy Markdown
Member

  • Previously, calling at_derivation_index() on a descriptor without wildcards would silently ignore the index and return the descriptor unchanged. This was error-prone because callers would expect different indices to produce different addresses.

I agree this is a bad and footgunny API.

  • Callers that need to convert a non-wildcard descriptor to a definite one should use DefiniteDescriptorKey::new() on individual keys, or check has_wildcard() before calling at_derivation_index().

But nither of these are reasonable API choices. Converting every key requires the user to run translate_pk over the whole descriptor (and maybe implement their own Translator type), which is not very discoverable. And checking has_wildcard on a DescriptorPublicKey doesn't help a user who needs a `DefiniteDescriptorKey'.

@apoelstra
Copy link
Copy Markdown
Member

apoelstra commented Apr 2, 2026

How about we also add a TryFrom<Descriptor<DescriptorPublicKey>> for Descriptor<DefiniteDescriptorKey>, which attempts the "dumb conversion" and only works on wildcard-less descriptors?

And in the docs for at_derivation_index we should point users at this method.

Secondly, because existing users are calling at_derivation_index(0) or something to do this conversion (I know I am), and this will break their code, I think we need a better transition strategy. I would suggest deprecating the existing method without changing its behavior, and adding a new at_index method with the new behavior. Then we could even backport this.

After a couple versions we can rename the method back because I do prefer the old name :/ but I think that the "silent breakage" of returning an error for previously-legitimate use is too much of a problem.

@trevarj
Copy link
Copy Markdown
Contributor

trevarj commented Apr 2, 2026

How about we also add a TryFrom<Descriptor<DescriptorPublicKey>> for Descriptor<DefiniteDescriptorKey>, which attempts the "dumb conversion" and only works on wildcard-less descriptors?

I like this. Then the into_definite() can call that (or just get rid of it).

I would suggest deprecating the existing method without changing its behavior, and adding a new at_index method with the new behavior. Then we could even backport this.

After a couple versions we can rename the method back because I do prefer the old name :/

Possibly even derive_at_index()

@apoelstra
Copy link
Copy Markdown
Member

Possibly even derive_at_index()

Oh, nice, I like this name even better than at_derivation_index.

@febyeji
Copy link
Copy Markdown
Author

febyeji commented Apr 2, 2026

Possibly even derive_at_index()

Nice, I like this name, too.

@febyeji febyeji force-pushed the at-derivation-index-no-wildcard branch from cc5bded to 74075e9 Compare April 2, 2026 18:51
@apoelstra
Copy link
Copy Markdown
Member

In 74075e9:

This enum Definitor is cute but it should really be split into two separate Translator instances because the two variants of the enum do completely different things. One should be local to the at_derivation_index and one should be local to the into_definite method.

Then derive_at_index can call at_derivation_index rather than directly doing the translation. (And at_derivation_index will not change its code at all.)

@trevarj
Copy link
Copy Markdown
Contributor

trevarj commented Apr 3, 2026

This enum Definitor is cute but it should really be split into two separate Translator instances

That was my fault, sorry.

@apoelstra
Copy link
Copy Markdown
Member

Oh, lol, I missed that. Sorry to jerk you around @febyeji. But I do think it's better design to have them split up.

…and TryFrom

- Deprecate at_derivation_index() preserving old behavior (non-wildcard silently works)
- Add derive_at_index() that strictly requires wildcards, delegates to at_derivation_index()
- Add TryFrom<Descriptor<DescriptorPublicKey>> for Descriptor<DefiniteDescriptorKey>
- Split Definitor enum into separate local Translator structs (ToDefinite, AtIndex)
- Update internal callers, tests, and examples to use new API
@febyeji febyeji force-pushed the at-derivation-index-no-wildcard branch from 74075e9 to 1062144 Compare April 3, 2026 14:43
@febyeji
Copy link
Copy Markdown
Author

febyeji commented Apr 3, 2026

No worries, both suggestions made sense in context! The split looks cleaner so I just applied it.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

at_derivation_index should fail when the descriptor has no wildcard

3 participants