Skip to content

fix: error instead of panic for hardened blinding keys - #103

Merged
apoelstra merged 1 commit into
ElementsProject:masterfrom
delta1:issues/102
Aug 6, 2026
Merged

fix: error instead of panic for hardened blinding keys#103
apoelstra merged 1 commit into
ElementsProject:masterfrom
delta1:issues/102

Conversation

@delta1

@delta1 delta1 commented Aug 5, 2026

Copy link
Copy Markdown
Member

Return an error instead of panic when a blinding key uses hardened derivation

The code that turned the descriptor's blinding key into an actual public
key assumed the step could never fail, so asking such a descriptor for
its address crashed instead of returning an error.

The new tests cover both ways of running into this: writing a hardened
step directly into the blinding key, and using a hardened wildcard that
gets filled in with a specific index later.

Closes #102

Key::to_public_key passed the DefiniteDescriptorKey to bare::tweak_key,
which reaches it through ToPublicKey::to_public_key. That impl unwraps the
ConversionError from derive_public_key, so a bare blinding key with a
hardened derivation step panicked rather than returning an error.

Derive the key explicitly and propagate the failure through the existing
Error::Conversion variant, then pass the resulting bitcoin::PublicKey to
tweak_key, whose ToPublicKey impl is infallible. tweak_key's public
signature is unchanged.

Add tests for both routes to the panic: an explicit hardened step in the
blinding key path, and a hardened wildcard resolved by at_derivation_index.

Closes ElementsProject#102
@delta1

delta1 commented Aug 5, 2026

Copy link
Copy Markdown
Member Author

Descriptor::at_derivation_index still accepts a hardened-wildcard blinding key and produces a definite descriptor that can only ever error at address() time. Do we want to error earlier?

at_derivation_index has no secp context, so it can't call derive_public_key. Instead scanning the resulting derivation path for hardened steps:

Key::Bare(k) => {
    let k = k.at_derivation_index(index)?.into_descriptor_public_key();
    if let DescriptorPublicKey::XPub(ref xpub) = k {
        if xpub.derivation_path.into_iter().any(|c| c.is_hardened()) {
            return Err(ConversionError::HardenedChild);
        }
    }
    Key::Bare(k)
}

@apoelstra

Copy link
Copy Markdown
Member

Possibly related to rust-bitcoin/rust-miniscript#830 which required a followup rust-bitcoin/rust-miniscript#913

But I can't really tell because this code is so old. For now I'll just accept this patch. Upstream we are hoping to pull all the descriptor key crap out into its own crate that we can share between rust-miniscript and elements-miniscript.

@apoelstra apoelstra left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ACK 4b5c341; successfully ran local tests

@apoelstra
apoelstra merged commit e6eca8b into ElementsProject:master Aug 6, 2026
9 of 18 checks passed
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.

hardened blinding keys panic

2 participants