Skip to content

Commit 725f450

Browse files
committed
confidential: hack in view descriptor support
Need to re-think the API for this; but sufficient to produce a test vector.
1 parent 6359f03 commit 725f450

File tree

2 files changed

+68
-4
lines changed

2 files changed

+68
-4
lines changed

src/confidential/bare.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,29 @@ where
5858
.unwrap()
5959
}
6060

61+
/// Tweaks a bare key using the scriptPubKey of a descriptor
62+
pub fn tweak_private_key<'a, Pk, V>(
63+
secp: &secp256k1_zkp::Secp256k1<V>,
64+
spk: &elements::Script,
65+
pk: &Pk,
66+
) -> secp256k1_zkp::PublicKey
67+
where
68+
Pk: ToPublicKey + 'a,
69+
V: secp256k1_zkp::Verification,
70+
{
71+
let mut eng = TweakHash::engine();
72+
pk.to_public_key()
73+
.write_into(&mut eng)
74+
.expect("engines don't error");
75+
spk.consensus_encode(&mut eng).expect("engines don't error");
76+
let hash_bytes = TweakHash::from_engine(eng).to_byte_array();
77+
let hash_scalar = secp256k1_zkp::Scalar::from_be_bytes(hash_bytes).expect("bytes from hash");
78+
pk.to_public_key()
79+
.inner
80+
.add_exp_tweak(secp, &hash_scalar)
81+
.unwrap()
82+
}
83+
6184
#[cfg(test)]
6285
mod tests {
6386
use bitcoin::hashes::sha256t::Tag;

src/confidential/mod.rs

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,28 @@ use std::fmt;
2525
use elements::secp256k1_zkp;
2626

2727
use crate::descriptor::checksum::{desc_checksum, verify_checksum};
28+
use crate::descriptor::DescriptorSecretKey;
2829
use crate::expression::FromTree;
2930
use crate::extensions::{CovExtArgs, CovenantExt, Extension, ParseableExt};
3031
use crate::{expression, Error, MiniscriptKey, ToPublicKey};
3132

3233
/// A description of a blinding key
33-
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
34+
#[derive(Clone, PartialEq, Eq, Debug)]
3435
pub enum Key<Pk: MiniscriptKey> {
3536
/// Blinding key is computed using SLIP77 with the given master key
3637
Slip77(slip77::MasterBlindingKey),
3738
/// Blinding key is given directly
3839
Bare(Pk),
40+
/// Blinding key is given directly, as a secret key
41+
View(DescriptorSecretKey),
3942
}
4043

4144
impl<Pk: MiniscriptKey> fmt::Display for Key<Pk> {
4245
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4346
match self {
4447
Key::Slip77(data) => write!(f, "slip77({})", data),
4548
Key::Bare(pk) => fmt::Display::fmt(pk, f),
49+
Key::View(sk) => fmt::Display::fmt(sk, f),
4650
}
4751
}
4852
}
@@ -56,12 +60,13 @@ impl<Pk: MiniscriptKey + ToPublicKey> Key<Pk> {
5660
match *self {
5761
Key::Slip77(ref mbk) => mbk.blinding_key(secp, spk),
5862
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")),
5964
}
6065
}
6166
}
6267

6368
/// A confidential descriptor
64-
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
69+
#[derive(Clone, PartialEq, Eq, Debug)]
6570
pub struct Descriptor<Pk: MiniscriptKey, T: Extension = CovenantExt<CovExtArgs>> {
6671
/// The blinding key
6772
pub key: Key<Pk>,
@@ -132,7 +137,8 @@ impl_from_str!(
132137
("slip77", _) => return Err(Error::BadDescriptor(
133138
"slip77() must have exactly one argument".to_owned()
134139
)),
135-
_ => Key::Bare(expression::terminal(keyexpr, Pk::from_str)?),
140+
_ => expression::terminal(keyexpr, Pk::from_str).map(Key::Bare)
141+
.or_else(|_| expression::terminal(keyexpr, DescriptorSecretKey::from_str).map(Key::View))?,
136142
},
137143
descriptor: crate::Descriptor::from_tree(&top.args[1])?,
138144
})
@@ -207,7 +213,8 @@ mod tests {
207213
index, self.descriptor_str
208214
);
209215
match self.key {
210-
Key::Bare(ref pk) => println!("** Blinding key: <code>{}</code>", pk),
216+
Key::Bare(ref pk) => println!("** Blinding public key: <code>{}</code>", pk),
217+
Key::View(ref sk) => println!("** Blinding private key: <code>{}</code>", sk),
211218
Key::Slip77(mbk) => println!("** SLIP77 master blinding key: <code>{}</code>", mbk),
212219
}
213220
println!("** Confidential address: <code>{}</code>", self.conf_addr);
@@ -355,4 +362,38 @@ mod tests {
355362
assert_eq!(bad_str.1, err.to_string());
356363
}
357364
}
365+
366+
#[test]
367+
fn view_descriptor() {
368+
let secp = secp256k1_zkp::Secp256k1::new();
369+
370+
let view_key = DescriptorSecretKey::from_str(
371+
"xprv9s21ZrQH143K28NgQ7bHCF61hy9VzwquBZvpzTwXLsbmQLRJ6iV9k2hUBRt5qzmBaSpeMj5LdcsHaXJvM7iFEivPryRcL8irN7Na9p65UUb",
372+
).unwrap();
373+
let ct_key = view_key.to_public(&secp).unwrap().at_derivation_index(0).unwrap(); // FIXME figure out derivation
374+
let spk_key = DefiniteDescriptorKey::from_str(
375+
"xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH",
376+
)
377+
.unwrap();
378+
379+
// View key, P2PKH
380+
let test = ConfidentialTest {
381+
key: Key::View(view_key.clone()),
382+
descriptor: crate::Descriptor::new_wpkh(spk_key.clone()).unwrap(),
383+
descriptor_str: format!("ct({},elwpkh({}))#j95xktq7", view_key, spk_key),
384+
conf_addr: "el1qq2r0pdvcknjpwev96qu9975alzqs78cvsut5ju82t7tv8d645dgmwknpl78t02k2xqgdh9ltmfmpy9ssk7qfvq78z9wukacu0",
385+
unconf_addr: "ert1qtfsllr4h4t9rqyxmjl4a5asjzcgt0qyk32h3ur",
386+
};
387+
test.check(&secp);
388+
389+
// View key converted to Bare (note that addresses are the same)
390+
let test = ConfidentialTest {
391+
key: Key::Bare(ct_key.clone()),
392+
descriptor: crate::Descriptor::new_wpkh(spk_key.clone()).unwrap(),
393+
descriptor_str: format!("ct({},elwpkh({}))#elmfpmp9", ct_key, spk_key),
394+
conf_addr: "el1qq2r0pdvcknjpwev96qu9975alzqs78cvsut5ju82t7tv8d645dgmwknpl78t02k2xqgdh9ltmfmpy9ssk7qfvq78z9wukacu0",
395+
unconf_addr: "ert1qtfsllr4h4t9rqyxmjl4a5asjzcgt0qyk32h3ur",
396+
};
397+
test.check(&secp);
398+
}
358399
}

0 commit comments

Comments
 (0)