Skip to content

Commit abe56b1

Browse files
Refuse hardened xpubs before miniscript at_derivation_index
rust-miniscript panics on an xpub hardened step. Detect it first and return Core's Expand failure. An xprv still expands because parse applies those steps with the secret. Co-authored-by: metaphorics <metaphorics@users.noreply.github.com>
1 parent 46531a3 commit abe56b1

2 files changed

Lines changed: 19 additions & 0 deletions

File tree

crates/rpc/src/handlers/mining.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2282,5 +2282,8 @@ mod tests {
22822282
.unwrap_or_else(|| panic!("hardened xpub must fail Expand"));
22832283
assert_eq!(error.code(), RpcError::CORE_NOT_FOUND);
22842284
assert_eq!(error.to_string(), GENERATEBLOCK_NEEDS_PRIVATE_KEYS);
2285+
let tprv = "tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS";
2286+
generateblock(&ctx, &json!([format!("wpkh({tprv}/0h/0)"), []]))
2287+
.unwrap_or_else(|err| panic!("hardened tprv must Expand: {err}"));
22852288
}
22862289
}

crates/rpc/src/handlers/util.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,7 @@ fn script_from_descriptor(
10501050
}
10511051
ensure_keys_match_network(&descriptor, network)?;
10521052
ensure_secret_keys_match_network(keys, network)?;
1053+
reject_hardened_xpub(&descriptor)?;
10531054
let derived = descriptor
10541055
.at_derivation_index(0)
10551056
.map_err(|_| DescriptorError::PrivateKeys)?;
@@ -1068,6 +1069,7 @@ fn combo_payout_script(key: &str, network: bitcoin::Network) -> Result<Vec<u8>,
10681069
.paths
10691070
.first()
10701071
.ok_or_else(|| DescriptorError::Parse("Invalid combo descriptor".into()))?;
1072+
reject_hardened_xpub(path)?;
10711073
let derived = path
10721074
.at_derivation_index(0)
10731075
.map_err(|_| DescriptorError::PrivateKeys)?;
@@ -1084,6 +1086,20 @@ fn ranged_descriptor_rejected() -> DescriptorError {
10841086
DescriptorError::Range(GENERATEBLOCK_RANGED)
10851087
}
10861088

1089+
/// rust-miniscript panics in `at_derivation_index` on an xpub hardened step.
1090+
/// Core `Expand` returns false, which `getScriptFromDescriptor` maps to
1091+
/// `Cannot derive script without private keys`. An xprv is converted to an
1092+
/// xpub with those steps already applied during parse, so it never hits this.
1093+
fn reject_hardened_xpub(
1094+
descriptor: &MiniscriptDescriptor<DescriptorPublicKey>,
1095+
) -> Result<(), DescriptorError> {
1096+
if descriptor.for_any_key(DescriptorPublicKey::has_hardened_step) {
1097+
Err(DescriptorError::PrivateKeys)
1098+
} else {
1099+
Ok(())
1100+
}
1101+
}
1102+
10871103
fn descriptor_text_with_optional_checksum(text: &str) -> Result<String, DescriptorError> {
10881104
if text.contains('#') {
10891105
return Ok(text.to_owned());

0 commit comments

Comments
 (0)