rust-bitcoin / rust-bitcoin/rust-miniscript
`master_fingerprint()` docs promise `0x00000000` for no-origin keys, but PSBT helpers export synthetic fingerprints instead
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 426
- Forks
- 200
- Avg merge
- 7d 17h
- Merged PRs (30d)
- 8
Description
Summary
The public docs for master_fingerprint() say no-origin descriptor keys should use:
0x00000000 if none
However, for raw descriptor public keys with no origin information, the current implementation computes a nonzero synthetic fingerprint from the key material. The public PSBT descriptor-update helpers then serialize that synthetic (fingerprint, empty_path) pair into PSBT origin metadata maps:
bip32_derivationtap_key_origins
The first minimal repro below shows the non-Taproot bip32_derivation path. A Taproot companion variant further below shows the same synthetic-origin behavior in tap_key_origins.
Verified locally on commit:
6131095c8ee99ba9a91ee004b1d387dd8418766a
Doc / implementation contradiction
The contradiction appears to be literal.
The public docs for master_fingerprint() say the fingerprint should be:
0x00000000 if none
That wording appears on both the DescriptorPublicKey and DefiniteDescriptorKey wrappers.
But for raw single keys without origin information, the current implementation instead derives a nonzero fingerprint from the pubkey bytes and then exports that value with an empty derivation path.
Minimal repro
use std::str::FromStr;
use miniscript::bitcoin::psbt;
use miniscript::descriptor::DefiniteDescriptorKey;
use miniscript::{Descriptor, PsbtInputExt};
fn main() {
let desc = Descriptor::<DefiniteDescriptorKey>::from_str(
"wpkh(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)",
)
.unwrap();
let mut input = psbt::Input::default();
input.update_with_descriptor_unchecked(&desc).unwrap();
println!("{:?}", input.bip32_derivation);
}
Taproot companion variant
Also locally confirmed:
use std::str::FromStr;
use miniscript::bitcoin::psbt;
use miniscript::descriptor::DefiniteDescriptorKey;
use miniscript::{Descriptor, PsbtOutputExt};
fn main() {
let desc = Descriptor::<DefiniteDescriptorKey>::from_str(
"tr(79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)",
)
.unwrap();
let mut output = psbt::Output::default();
output.update_with_descriptor_unchecked(&desc).unwrap();
println!("{:?}", output.tap_key_origins);
}
Observed behavior
Locally:
- the no-origin case does not use the documented
00000000 if nonebehavior bip32_derivationis populated even though the descriptor key had no origin- the exported fingerprint is synthetic and nonzero rather than the documented
00000000 if none
Concrete local output from the existing repro family:
wpkh_bip32_len=1
wpkh_fp=751e76e8
wpkh_path= (empty path)
I also confirmed the same family on Taproot helpers:
tr_tap_key_origins_len=1
tr_fp=f678d9b7
tr_path= (empty path)
So both non-Taproot and Taproot PSBT helper paths export nonzero synthetic fingerprints with an empty derivation path for raw no-origin descriptor keys.
Why this seems to happen
The current raw-key path appears to:
- treat a raw
DescriptorPublicKey::Singleas a definite key - derive a synthetic
master_fingerprint()from the raw pubkey bytes - pair it with an empty derivation path
- emit that pair into PSBT key-origin metadata maps
Why this matters
Applications using the public PSBT update helpers can emit invented origin/provenance metadata for raw descriptor keys whose descriptor supplied no origin at all.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing DescriptorPublicKey and DefiniteDescriptorKey::master_fingerprint, then follow update_with_descriptor_unchecked for both PsbtInputExt and PsbtOutputExt. Run the supplied non-Taproot and Taproot reproductions first; done means raw keys without origin information no longer export synthetic fingerprint and empty-path pairs in bip32_derivation or tap_key_origins, while the documented no-origin behavior is preserved.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- cryptography
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100