Skip to content

Commit ca82331

Browse files
committed
descriptor: allow "raw" pubkeys to have origin
Signed-off-by: Antoine Poinsot <[email protected]>
1 parent 71c550e commit ca82331

File tree

1 file changed

+100
-48
lines changed

1 file changed

+100
-48
lines changed

src/descriptor/mod.rs

Lines changed: 100 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,16 @@ pub enum Descriptor<Pk: MiniscriptKey> {
7777

7878
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
7979
pub enum DescriptorPublicKey {
80-
PukKey(bitcoin::PublicKey),
80+
ShortPub(DescriptorShortPub),
8181
XPub(DescriptorXPub),
8282
}
8383

84+
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
85+
pub struct DescriptorShortPub {
86+
origin: Option<(bip32::Fingerprint, bip32::DerivationPath)>,
87+
key: bitcoin::PublicKey,
88+
}
89+
8490
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
8591
pub struct DescriptorXPub {
8692
origin: Option<(bip32::Fingerprint, bip32::DerivationPath)>,
@@ -101,16 +107,13 @@ impl fmt::Display for DescriptorKeyParseError {
101107
impl fmt::Display for DescriptorPublicKey {
102108
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
103109
match *self {
104-
DescriptorPublicKey::PukKey(ref pk) => pk.fmt(f),
110+
DescriptorPublicKey::ShortPub(ref pk) => {
111+
maybe_fmt_master_id(f, &pk.origin)?;
112+
pk.key.fmt(f)?;
113+
Ok(())
114+
}
105115
DescriptorPublicKey::XPub(ref xpub) => {
106-
if let Some((ref master_id, ref master_deriv)) = xpub.origin {
107-
fmt::Formatter::write_str(f, "[")?;
108-
for byte in master_id.into_bytes().iter() {
109-
write!(f, "{:02x}", byte)?;
110-
}
111-
fmt_derivation_path(f, master_deriv)?;
112-
fmt::Formatter::write_str(f, "]")?;
113-
}
116+
maybe_fmt_master_id(f, &xpub.origin)?;
114117
xpub.xpub.fmt(f)?;
115118
fmt_derivation_path(f, &xpub.derivation_path)?;
116119
if xpub.is_wildcard {
@@ -122,6 +125,23 @@ impl fmt::Display for DescriptorPublicKey {
122125
}
123126
}
124127

128+
/// Writes the fingerprint of the origin, if there is one.
129+
fn maybe_fmt_master_id(
130+
f: &mut fmt::Formatter,
131+
origin: &Option<(bip32::Fingerprint, bip32::DerivationPath)>,
132+
) -> fmt::Result {
133+
if let Some((ref master_id, ref master_deriv)) = *origin {
134+
fmt::Formatter::write_str(f, "[")?;
135+
for byte in master_id.into_bytes().iter() {
136+
write!(f, "{:02x}", byte)?;
137+
}
138+
fmt_derivation_path(f, master_deriv)?;
139+
fmt::Formatter::write_str(f, "]")?;
140+
}
141+
142+
Ok(())
143+
}
144+
125145
/// Writes a derivation path to the formatter, no leading 'm'
126146
fn fmt_derivation_path(f: &mut fmt::Formatter, path: &bip32::DerivationPath) -> fmt::Result {
127147
for child in path {
@@ -134,11 +154,16 @@ impl FromStr for DescriptorPublicKey {
134154
type Err = DescriptorKeyParseError;
135155

136156
fn from_str(s: &str) -> Result<Self, Self::Err> {
157+
// A "raw" public key without any origin is the least we accept.
137158
if s.len() < 66 {
138-
Err(DescriptorKeyParseError(
159+
return Err(DescriptorKeyParseError(
139160
"Key too short (<66 char), doesn't match any format",
140-
))
141-
} else if s.chars().next().unwrap() == '[' {
161+
));
162+
}
163+
164+
// They may specify an origin
165+
let mut origin = None;
166+
if s.chars().next().unwrap() == '[' {
142167
let mut parts = s[1..].split(']');
143168
let mut raw_origin = parts
144169
.next()
@@ -165,30 +190,33 @@ impl FromStr for DescriptorPublicKey {
165190
.map_err(|_| {
166191
DescriptorKeyParseError("Error while parsing master derivation path")
167192
})?;
193+
origin = Some((parent_fingerprint, origin_path));
194+
}
168195

169-
let key_deriv = parts
170-
.next()
171-
.ok_or(DescriptorKeyParseError("No key after origin."))?;
196+
let key_part = if origin == None {
197+
Ok(s)
198+
} else {
199+
s.split(']')
200+
.collect::<Vec<&str>>()
201+
.pop()
202+
.ok_or(DescriptorKeyParseError("No key after origin."))
203+
}?;
172204

173-
let (xpub, derivation_path, is_wildcard) = Self::parse_xpub_deriv(key_deriv)?;
205+
if key_part.starts_with("xpub") {
206+
let (xpub, derivation_path, is_wildcard) = Self::parse_xpub_deriv(key_part)?;
174207

175208
Ok(DescriptorPublicKey::XPub(DescriptorXPub {
176-
origin: Some((parent_fingerprint, origin_path)),
209+
origin,
177210
xpub,
178211
derivation_path,
179212
is_wildcard,
180213
}))
181-
} else if s.starts_with("02") || s.starts_with("03") || s.starts_with("04") {
182-
let pk = bitcoin::PublicKey::from_str(s)
183-
.map_err(|_| DescriptorKeyParseError("Error while parsing simple public key"))?;
184-
Ok(DescriptorPublicKey::PukKey(pk))
185214
} else {
186-
let (xpub, derivation_path, is_wildcard) = Self::parse_xpub_deriv(s)?;
187-
Ok(DescriptorPublicKey::XPub(DescriptorXPub {
188-
origin: None,
189-
xpub,
190-
derivation_path,
191-
is_wildcard,
215+
let key = bitcoin::PublicKey::from_str(key_part)
216+
.map_err(|_| DescriptorKeyParseError("Error while parsing simple public key"))?;
217+
Ok(DescriptorPublicKey::ShortPub(DescriptorShortPub {
218+
key,
219+
origin,
192220
}))
193221
}
194222
}
@@ -238,10 +266,10 @@ impl DescriptorPublicKey {
238266
///
239267
/// Panics if derivation path contains a hardened child number
240268
pub fn derive(&self, path: &[bip32::ChildNumber]) -> DescriptorPublicKey {
241-
assert!(path.into_iter().all(|c| c.is_normal()));
269+
debug_assert!(path.into_iter().all(|c| c.is_normal()));
242270

243271
match *self {
244-
DescriptorPublicKey::PukKey(ref pk) => DescriptorPublicKey::PukKey(*pk),
272+
DescriptorPublicKey::ShortPub(ref pk) => DescriptorPublicKey::ShortPub(pk.clone()),
245273
DescriptorPublicKey::XPub(ref xpub) => {
246274
if xpub.is_wildcard {
247275
DescriptorPublicKey::XPub(DescriptorXPub {
@@ -267,7 +295,7 @@ impl MiniscriptKey for DescriptorPublicKey {
267295

268296
fn to_pubkeyhash(&self) -> Self::Hash {
269297
match *self {
270-
DescriptorPublicKey::PukKey(ref pk) => pk.to_pubkeyhash(),
298+
DescriptorPublicKey::ShortPub(ref spub) => spub.key.to_pubkeyhash(),
271299
DescriptorPublicKey::XPub(ref xpub) => {
272300
let ctx = secp256k1::Secp256k1::verification_only();
273301
xpub.xpub
@@ -283,7 +311,7 @@ impl MiniscriptKey for DescriptorPublicKey {
283311
impl ToPublicKey for DescriptorPublicKey {
284312
fn to_public_key(&self) -> bitcoin::PublicKey {
285313
match *self {
286-
DescriptorPublicKey::PukKey(ref pk) => *pk,
314+
DescriptorPublicKey::ShortPub(ref spub) => spub.key.to_public_key(),
287315
DescriptorPublicKey::XPub(ref xpub) => {
288316
let ctx = secp256k1::Secp256k1::verification_only();
289317
xpub.xpub
@@ -804,7 +832,7 @@ mod tests {
804832
use bitcoin::hashes::{hash160, sha256};
805833
use bitcoin::util::bip32;
806834
use bitcoin::{self, secp256k1, PublicKey};
807-
use descriptor::{DescriptorPublicKey, DescriptorXPub};
835+
use descriptor::{DescriptorPublicKey, DescriptorShortPub, DescriptorXPub};
808836
use miniscript::satisfy::BitcoinSig;
809837
use policy;
810838
use std::collections::HashMap;
@@ -1368,37 +1396,52 @@ mod tests {
13681396

13691397
// Raw (compressed) pubkey
13701398
let key = "03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8";
1371-
let expected = DescriptorPublicKey::PukKey(
1372-
bitcoin::PublicKey::from_str(
1399+
let expected = DescriptorPublicKey::ShortPub(DescriptorShortPub {
1400+
key: bitcoin::PublicKey::from_str(
13731401
"03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8",
13741402
)
13751403
.unwrap(),
1376-
);
1404+
origin: None,
1405+
});
13771406
assert_eq!(expected, key.parse().unwrap());
13781407
assert_eq!(format!("{}", expected), key);
13791408

13801409
// Raw (uncompressed) pubkey
13811410
let key = "04f5eeb2b10c944c6b9fbcfff94c35bdeecd93df977882babc7f3a2cf7f5c81d3b09a68db7f0e04f21de5d4230e75e6dbe7ad16eefe0d4325a62067dc6f369446a";
1382-
let expected = DescriptorPublicKey::PukKey(
1383-
bitcoin::PublicKey::from_str(
1411+
let expected = DescriptorPublicKey::ShortPub(DescriptorShortPub {
1412+
key: bitcoin::PublicKey::from_str(
13841413
"04f5eeb2b10c944c6b9fbcfff94c35bdeecd93df977882babc7f3a2cf7f5c81d3b09a68db7f0e04f21de5d4230e75e6dbe7ad16eefe0d4325a62067dc6f369446a",
13851414
)
13861415
.unwrap(),
1387-
);
1416+
origin: None,
1417+
});
13881418
assert_eq!(expected, key.parse().unwrap());
13891419
assert_eq!(format!("{}", expected), key);
1420+
1421+
// Raw pubkey with origin
1422+
let desc =
1423+
"[78412e3a/0'/42/0']0231c7d3fc85c148717848033ce276ae2b464a4e2c367ed33886cc428b8af48ff8";
1424+
let expected = DescriptorPublicKey::ShortPub(DescriptorShortPub {
1425+
key: bitcoin::PublicKey::from_str(
1426+
"0231c7d3fc85c148717848033ce276ae2b464a4e2c367ed33886cc428b8af48ff8",
1427+
)
1428+
.unwrap(),
1429+
origin: Some((
1430+
bip32::Fingerprint::from(&[0x78, 0x41, 0x2e, 0x3a][..]),
1431+
(&[
1432+
bip32::ChildNumber::from_hardened_idx(0).unwrap(),
1433+
bip32::ChildNumber::from_normal_idx(42).unwrap(),
1434+
bip32::ChildNumber::from_hardened_idx(0).unwrap(),
1435+
][..])
1436+
.into(),
1437+
)),
1438+
});
1439+
assert_eq!(expected, desc.parse().expect("Parsing desc"));
1440+
assert_eq!(format!("{}", expected), desc);
13901441
}
13911442

13921443
#[test]
13931444
fn parse_descriptor_key_errors() {
1394-
// origin is only supported for xpubs
1395-
let desc =
1396-
"[78412e3a/0'/0'/0']0231c7d3fc85c148717848033ce276ae2b464a4e2c367ed33886cc428b8af48ff8";
1397-
assert_eq!(
1398-
DescriptorPublicKey::from_str(desc),
1399-
Err(DescriptorKeyParseError("Error while parsing xpub."))
1400-
);
1401-
14021445
// We refuse creating descriptors which claim to be able to derive hardened childs
14031446
let desc = "[78412e3a/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/42'/*";
14041447
assert_eq!(
@@ -1426,12 +1469,21 @@ mod tests {
14261469
))
14271470
);
14281471

1429-
// And ones with invalid xpubs
1472+
// And ones with invalid xpubs..
14301473
let desc = "[78412e3a]xpub1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaLcgJvLJuZZvRcEL/1/*";
14311474
assert_eq!(
14321475
DescriptorPublicKey::from_str(desc),
14331476
Err(DescriptorKeyParseError("Error while parsing xpub."))
14341477
);
1478+
1479+
// ..or invalid raw keys
1480+
let desc = "[78412e3a]0208a117f3897c3a13c9384b8695eed98dc31bc2500feb19a1af424cd47a5d83/1/*";
1481+
assert_eq!(
1482+
DescriptorPublicKey::from_str(desc),
1483+
Err(DescriptorKeyParseError(
1484+
"Error while parsing simple public key"
1485+
))
1486+
);
14351487
}
14361488

14371489
#[test]

0 commit comments

Comments
 (0)