Summary
The public docs for master_fingerprint() say no-origin descriptor keys should use:
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_derivation
tap_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:
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 none behavior
bip32_derivation is 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::Single as 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.
Summary
The public docs for
master_fingerprint()say no-origin descriptor keys should use: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_originsThe first minimal repro below shows the non-Taproot
bip32_derivationpath. A Taproot companion variant further below shows the same synthetic-origin behavior intap_key_origins.Verified locally on commit:
Doc / implementation contradiction
The contradiction appears to be literal.
The public docs for
master_fingerprint()say the fingerprint should be:That wording appears on both the
DescriptorPublicKeyandDefiniteDescriptorKeywrappers.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
Taproot companion variant
Also locally confirmed:
Observed behavior
Locally:
00000000 if nonebehaviorbip32_derivationis populated even though the descriptor key had no origin00000000 if noneConcrete local output from the existing repro family:
I also confirmed the same family on Taproot helpers:
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:
DescriptorPublicKey::Singleas a definite keymaster_fingerprint()from the raw pubkey bytesWhy 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.