Skip to content

Commit af410f6

Browse files
committed
Merge #59: CT descriptor: remove generic from blinding key
f4be6af CT descriptor: test cases with wildcards (Leonardo Comandini) eafc28d CT descriptor: handle bare/view keys with wildcards (Leonardo Comandini) 8ffda8c CT descriptor: implement at_derivation_index (Leonardo Comandini) da8d16c CT descriptor: remove generic from blinding key (Leonardo Comandini) Pull request description: The generic was only used by the `Bare` variant, which was inconsistent with its corresponding "secret" variant, `View`. The generic could be used to handle the conversion from `ConfidentialDescriptor<DescriptorPublicKey>` to `Descriptor<DefiniteDescriptorKey>`, specifically making sure that the descriptor blinding key does not have wildcards. For the `Bare` variant this could be done, but this cannot happen for the `View` variant, since we don't have the definite version of `DescriptorSecretKey`. The proposed solution consists in: * Remove generic from descriptor blinding key (this commit) * Add `ConfidentialDescriptor::at_derivation_index` that "removes" wildcards from both the "bitcoin" descriptor and the descriptor blinding key. This makes deriving an address from a CT descriptor with wildcards less cumbersome. * Change `Key::to_public_key(&self, secp, spk)` to return a `Result` and error if `Key` has some wildcards. This will fix the case `ct(xprv/*,desc)` which is currently broken. * Add test cases for blinding keys with wildcards * Add test vectors with blinding keys with wildcards to ELIP-150 ACKs for top commit: apoelstra: ACK f4be6af Tree-SHA512: f991785026190bcb445f7458a66ee06207dbe24bb6c82eb1c34856778b5bb7056fbfdf3a1ce1f863e789467d9dc378f05a09f9015554ed353d3c3aacaa3a3b94
2 parents 571e467 + f4be6af commit af410f6

File tree

1 file changed

+124
-17
lines changed

1 file changed

+124
-17
lines changed

src/confidential/mod.rs

Lines changed: 124 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,26 +22,30 @@ pub mod slip77;
2222

2323
use std::fmt;
2424

25+
use bitcoin::bip32;
2526
use elements::secp256k1_zkp;
2627

2728
use crate::descriptor::checksum::{desc_checksum, verify_checksum};
28-
use crate::descriptor::DescriptorSecretKey;
29+
use crate::descriptor::{
30+
ConversionError, DefiniteDescriptorKey, DescriptorSecretKey, DescriptorPublicKey,
31+
DescriptorXKey, Wildcard
32+
};
2933
use crate::expression::FromTree;
3034
use crate::extensions::{CovExtArgs, CovenantExt, Extension, ParseableExt};
3135
use crate::{expression, Error, MiniscriptKey, ToPublicKey};
3236

3337
/// A description of a blinding key
3438
#[derive(Clone, PartialEq, Eq, Debug)]
35-
pub enum Key<Pk: MiniscriptKey> {
39+
pub enum Key {
3640
/// Blinding key is computed using SLIP77 with the given master key
3741
Slip77(slip77::MasterBlindingKey),
3842
/// Blinding key is given directly
39-
Bare(Pk),
43+
Bare(DescriptorPublicKey),
4044
/// Blinding key is given directly, as a secret key
4145
View(DescriptorSecretKey),
4246
}
4347

44-
impl<Pk: MiniscriptKey> fmt::Display for Key<Pk> {
48+
impl fmt::Display for Key {
4549
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4650
match self {
4751
Key::Slip77(data) => write!(f, "slip77({})", data),
@@ -51,16 +55,40 @@ impl<Pk: MiniscriptKey> fmt::Display for Key<Pk> {
5155
}
5256
}
5357

54-
impl<Pk: MiniscriptKey + ToPublicKey> Key<Pk> {
58+
impl Key {
5559
fn to_public_key<C: secp256k1_zkp::Signing + secp256k1_zkp::Verification>(
5660
&self,
5761
secp: &secp256k1_zkp::Secp256k1<C>,
5862
spk: &elements::Script,
59-
) -> secp256k1_zkp::PublicKey {
63+
) -> Result<secp256k1_zkp::PublicKey, Error> {
6064
match *self {
61-
Key::Slip77(ref mbk) => mbk.blinding_key(secp, spk),
62-
Key::Bare(ref pk) => bare::tweak_key(secp, spk, pk),
63-
Key::View(ref sk) => bare::tweak_key(secp, spk, &sk.to_public(secp).expect("view keys cannot be multipath keys").at_derivation_index(0).expect("FIXME deal with derivation paths properly")),
65+
Key::Slip77(ref mbk) => Ok(mbk.blinding_key(secp, spk)),
66+
Key::Bare(ref pk) => {
67+
if pk.is_multipath() {
68+
Err(Error::Unexpected("multipath blinding key".into()))
69+
} else if pk.has_wildcard() {
70+
Err(Error::Unexpected("wildcard blinding key".into()))
71+
} else {
72+
// Convert into a DefiniteDescriptorKey, note that we are deriving the xpub
73+
// since there is not wildcard.
74+
// Consider adding DescriptorPublicKey::to_definite_descriptor
75+
let pk = pk.clone().at_derivation_index(0).expect("single or xpub without wildcards");
76+
Ok(bare::tweak_key(secp, spk, &pk))
77+
}
78+
},
79+
Key::View(ref sk) => {
80+
if sk.is_multipath() {
81+
Err(Error::Unexpected("multipath blinding key".into()))
82+
} else {
83+
let pk = sk.to_public(secp).expect("single or xprv");
84+
if pk.has_wildcard() {
85+
Err(Error::Unexpected("wildcard blinding key".into()))
86+
} else {
87+
let pk = pk.at_derivation_index(0).expect("single or xprv without wildcards");
88+
Ok(bare::tweak_key(secp, spk, &pk))
89+
}
90+
}
91+
},
6492
}
6593
}
6694
}
@@ -69,7 +97,7 @@ impl<Pk: MiniscriptKey + ToPublicKey> Key<Pk> {
6997
#[derive(Clone, PartialEq, Eq, Debug)]
7098
pub struct Descriptor<Pk: MiniscriptKey, T: Extension = CovenantExt<CovExtArgs>> {
7199
/// The blinding key
72-
pub key: Key<Pk>,
100+
pub key: Key,
73101
/// The script descriptor
74102
pub descriptor: crate::Descriptor<Pk, T>,
75103
}
@@ -82,6 +110,51 @@ impl<Pk: MiniscriptKey, T: Extension> Descriptor<Pk, T> {
82110
}
83111
}
84112

113+
impl<T: Extension + ParseableExt> Descriptor<DescriptorPublicKey, T> {
114+
/// Replaces all wildcards (i.e. `/*`) in the descriptor and the descriptor blinding key
115+
/// with a particular derivation index, turning it into a *definite* descriptor.
116+
///
117+
/// # Errors
118+
/// - If index ≥ 2^31
119+
pub fn at_derivation_index(&self, index: u32) -> Result<Descriptor<DefiniteDescriptorKey, T>, ConversionError> {
120+
let definite_key = match self.key.clone() {
121+
Key::Slip77(k) => Key::Slip77(k),
122+
Key::Bare(k) => Key::Bare(k.at_derivation_index(index)?.into_descriptor_public_key()),
123+
Key::View(k) => Key::View(match k {
124+
// Consider implementing DescriptorSecretKey::at_derivation_index
125+
DescriptorSecretKey::Single(_) => k,
126+
DescriptorSecretKey::XPrv(xprv) => {
127+
let derivation_path = match xprv.wildcard {
128+
Wildcard::None => xprv.derivation_path,
129+
Wildcard::Unhardened => xprv.derivation_path.into_child(
130+
bip32::ChildNumber::from_normal_idx(index)
131+
.ok()
132+
.ok_or(ConversionError::HardenedChild)?,
133+
),
134+
Wildcard::Hardened => xprv.derivation_path.into_child(
135+
bip32::ChildNumber::from_hardened_idx(index)
136+
.ok()
137+
.ok_or(ConversionError::HardenedChild)?,
138+
),
139+
};
140+
DescriptorSecretKey::XPrv(DescriptorXKey {
141+
origin: xprv.origin,
142+
xkey: xprv.xkey,
143+
derivation_path,
144+
wildcard: Wildcard::None,
145+
})
146+
},
147+
DescriptorSecretKey::MultiXPrv(_) => return Err(ConversionError::MultiKey),
148+
}),
149+
};
150+
let definite_descriptor = self.descriptor.at_derivation_index(index)?;
151+
Ok(Descriptor{
152+
key: definite_key,
153+
descriptor: definite_descriptor,
154+
})
155+
}
156+
}
157+
85158
impl<Pk: MiniscriptKey + ToPublicKey, T: Extension + ParseableExt> Descriptor<Pk, T> {
86159
/// Obtains the unblinded address for this descriptor.
87160
pub fn unconfidential_address(
@@ -99,7 +172,7 @@ impl<Pk: MiniscriptKey + ToPublicKey, T: Extension + ParseableExt> Descriptor<Pk
99172
) -> Result<elements::Address, Error> {
100173
let spk = self.descriptor.script_pubkey();
101174
self.descriptor
102-
.blinded_address(self.key.to_public_key(secp, &spk), params)
175+
.blinded_address(self.key.to_public_key(secp, &spk)?, params)
103176
}
104177
}
105178

@@ -137,7 +210,7 @@ impl_from_str!(
137210
("slip77", _) => return Err(Error::BadDescriptor(
138211
"slip77() must have exactly one argument".to_owned()
139212
)),
140-
_ => expression::terminal(keyexpr, Pk::from_str).map(Key::Bare)
213+
_ => expression::terminal(keyexpr, DescriptorPublicKey::from_str).map(Key::Bare)
141214
.or_else(|_| expression::terminal(keyexpr, DescriptorSecretKey::from_str).map(Key::View))?,
142215
},
143216
descriptor: crate::Descriptor::from_tree(&top.args[1])?,
@@ -161,20 +234,20 @@ mod tests {
161234
// taken from libwally src/test/test_confidential_addr.py
162235
let mut addr = Address::from_str("Q7qcjTLsYGoMA7TjUp97R6E6AM5VKqBik6").unwrap();
163236
let key = Key::Bare(
164-
bitcoin::PublicKey::from_str(
237+
DescriptorPublicKey::from_str(
165238
"02dce16018bbbb8e36de7b394df5b5166e9adb7498be7d881a85a09aeecf76b623",
166239
)
167240
.unwrap(),
168241
);
169-
addr.blinding_pubkey = Some(key.to_public_key(&secp, &addr.script_pubkey()));
242+
addr.blinding_pubkey = Some(key.to_public_key(&secp, &addr.script_pubkey()).unwrap());
170243
assert_eq!(
171244
addr.to_string(),
172245
"VTpt7krqRQPJwqe3XQXPg2cVdEKYVFbuprTr7es7pNRMe8mndnq2iYWddxJWYowhLAwoDF8QrZ1v2EXv"
173246
);
174247
}
175248

176249
struct ConfidentialTest {
177-
key: Key<DefiniteDescriptorKey>,
250+
key: Key,
178251
descriptor: crate::Descriptor<DefiniteDescriptorKey, NoExt>,
179252
descriptor_str: String,
180253
conf_addr: &'static str,
@@ -231,7 +304,7 @@ mod tests {
231304
let secp = secp256k1_zkp::Secp256k1::new();
232305

233306
// CT key used for bare keys
234-
let ct_key = DefiniteDescriptorKey::from_str(
307+
let ct_key = DescriptorPublicKey::from_str(
235308
"xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL",
236309
)
237310
.unwrap();
@@ -370,7 +443,7 @@ mod tests {
370443
let view_key = DescriptorSecretKey::from_str(
371444
"xprv9s21ZrQH143K28NgQ7bHCF61hy9VzwquBZvpzTwXLsbmQLRJ6iV9k2hUBRt5qzmBaSpeMj5LdcsHaXJvM7iFEivPryRcL8irN7Na9p65UUb",
372445
).unwrap();
373-
let ct_key = view_key.to_public(&secp).unwrap().at_derivation_index(0).unwrap(); // FIXME figure out derivation
446+
let ct_key = view_key.to_public(&secp).unwrap();
374447
let spk_key = DefiniteDescriptorKey::from_str(
375448
"xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH",
376449
)
@@ -396,4 +469,38 @@ mod tests {
396469
};
397470
test.check(&secp);
398471
}
472+
473+
#[test]
474+
fn descriptor_wildcard() {
475+
let secp = secp256k1_zkp::Secp256k1::new();
476+
let params = &elements::AddressParams::ELEMENTS;
477+
478+
let xprv = "xprv9s21ZrQH143K28NgQ7bHCF61hy9VzwquBZvpzTwXLsbmQLRJ6iV9k2hUBRt5qzmBaSpeMj5LdcsHaXJvM7iFEivPryRcL8irN7Na9p65UUb";
479+
let xpub = "xpub661MyMwAqRbcEcT9W98HZP2kFzyzQQZkYnrRnrM8uD8kH8kSeFoQHq1x2iihLgC6PXGy5LrjCL66uSNhJ8pwjfx2rMUTLWuRMns2EG9xnjs";
480+
let desc_view_str = format!("ct({}/*,elwpkh({}/*))#wk8ltq6h", xprv, xpub);
481+
let desc_bare_str = format!("ct({}/*,elwpkh({}/*))#zzac2dpf", xpub, xpub);
482+
let index = 1;
483+
let conf_addr = "el1qqf6690fpw2y00hv5a84zsydjgztg2089d5xnll4k4cstzn63uvgudd907qpvlvvwd5ym9gx7j0v46elf23kfxunucm6ejjyk0";
484+
let unconf_addr = "ert1qkjhlqqk0kx8x6zdj5r0f8k2avl54gmyn7qjk2k";
485+
486+
let desc_view = Descriptor::<DescriptorPublicKey>::from_str(&desc_view_str).unwrap();
487+
let desc_bare = Descriptor::<DescriptorPublicKey>::from_str(&desc_bare_str).unwrap();
488+
let definite_desc_view = desc_view.at_derivation_index(index).unwrap();
489+
let definite_desc_bare = desc_bare.at_derivation_index(index).unwrap();
490+
assert_eq!(definite_desc_view.address(&secp, params).unwrap().to_string(), conf_addr.to_string());
491+
assert_eq!(definite_desc_bare.address(&secp, params).unwrap().to_string(), conf_addr.to_string());
492+
assert_eq!(definite_desc_view.unconfidential_address(params).unwrap().to_string(), unconf_addr.to_string());
493+
assert_eq!(definite_desc_bare.unconfidential_address(params).unwrap().to_string(), unconf_addr.to_string());
494+
495+
// It's not possible to get an address if the blinding key has a wildcard,
496+
// because the descriptor blinding key is not *definite*,
497+
// but we can't enforce this with the Descriptor generic.
498+
let desc_view_str = format!("ct({}/*,elwpkh({}))#ls6mx2ac", xprv, xpub);
499+
let desc_view = Descriptor::<DefiniteDescriptorKey>::from_str(&desc_view_str).unwrap();
500+
assert_eq!(desc_view.address(&secp, params).unwrap_err(), Error::Unexpected("wildcard blinding key".into()));
501+
502+
let desc_bare_str = format!("ct({}/*,elwpkh({}))#czkz0hwn", xpub, xpub);
503+
let desc_bare = Descriptor::<DefiniteDescriptorKey>::from_str(&desc_bare_str).unwrap();
504+
assert_eq!(desc_bare.address(&secp, params).unwrap_err(), Error::Unexpected("wildcard blinding key".into()));
505+
}
399506
}

0 commit comments

Comments
 (0)