Skip to content

Commit 9dcc34a

Browse files
committed
Merge #64: CT descriptor: use hex for single private view keys
81a4a83 CT descriptor: use hex for single private view keys (Leonardo Comandini) Pull request description: Changes related to ElementsProject/ELIPs#9 ACKs for top commit: RCasatta: ACK 81a4a83 checked that test cases matches the ones in ElementsProject/ELIPs@0ddbaaa apoelstra: ACK 81a4a83 Tree-SHA512: 3c96fbf6fef8f333457e70da6c1b180dc5df10f473f04317e6c34bc2e453e94ce24355179a3d25adf434a9ee83ab0d9aaa1158398c518472a5d596dca8b34230
2 parents f55d41d + 81a4a83 commit 9dcc34a

File tree

3 files changed

+60
-8
lines changed

3 files changed

+60
-8
lines changed

src/confidential/mod.rs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,17 @@ impl fmt::Display for Key {
5050
match self {
5151
Key::Slip77(data) => write!(f, "slip77({})", data),
5252
Key::Bare(pk) => fmt::Display::fmt(pk, f),
53-
Key::View(sk) => fmt::Display::fmt(sk, f),
53+
Key::View(sk) => {
54+
if let DescriptorSecretKey::Single(sk) = sk {
55+
crate::descriptor::maybe_fmt_master_id(f, &sk.origin)?;
56+
for byte in &sk.key.inner.secret_bytes() {
57+
write!(f, "{:02x}", byte)?;
58+
}
59+
Ok(())
60+
} else {
61+
fmt::Display::fmt(sk, f)
62+
}
63+
}
5464
}
5565
}
5666
}
@@ -211,7 +221,7 @@ impl_from_str!(
211221
"slip77() must have exactly one argument".to_owned()
212222
)),
213223
_ => expression::terminal(keyexpr, DescriptorPublicKey::from_str).map(Key::Bare)
214-
.or_else(|_| expression::terminal(keyexpr, DescriptorSecretKey::from_str).map(Key::View))?,
224+
.or_else(|_| expression::terminal(keyexpr, |s: &str| DescriptorSecretKey::from_str_inner(s, true)).map(Key::View))?,
215225
},
216226
descriptor: crate::Descriptor::from_tree(&top.args[1])?,
217227
})
@@ -420,6 +430,10 @@ mod tests {
420430
"ct(pk(02dce16018bbbb8e36de7b394df5b5166e9adb7498be7d881a85a09aeecf76b623),elwpkh(03774eec7a3d550d18e9f89414152025b3b0ad6a342b19481f702d843cff06dfc4))#nvax6rau",
421431
"unexpected «pk»",
422432
),
433+
(
434+
"ct(L3jXxwef3fpB7hcrFozcWgHeJCPSAFiZ1Ji2YJMPxceaGvy3PC1q,elwpkh(03774eec7a3d550d18e9f89414152025b3b0ad6a342b19481f702d843cff06dfc4))#gcy6hcfz",
435+
"unexpected «Error while parsing xkey.»",
436+
),
423437
];
424438

425439
/*
@@ -470,6 +484,26 @@ mod tests {
470484
test.check(&secp);
471485
}
472486

487+
#[test]
488+
fn view_single_key_descriptor() {
489+
let secp = secp256k1_zkp::Secp256k1::new();
490+
let view_key = "c25deb86fa11e49d651d7eae27c220ef930fbd86ea023eebfa73e54875647963";
491+
let ct_key = "0286fc9a38e765d955e9b0bcc18fa9ae81b0c893e2dd1ef5542a9c73780a086b90";
492+
let pk = "021a8fb6bd5a653b021b98a2a785725b8ddacfe3687bc043aa7f4d25d3a48d40b5";
493+
let addr_conf = "el1qq265u4g3k3m3qpyxjwpdrtnm293wuxgvs9xzmzcs2ck0mv5rx23w4d7xfsednsmmxrszfe7s9rs0c6cvf3dfyqwa4jj40uffq";
494+
let addr_unconf = "ert1qklrycvkecdanpcpyulgz3c8udvxyck5jkzxddw";
495+
496+
for desc_str in [
497+
format!("ct({view_key},elwpkh({pk}))#c2kx9zll"),
498+
format!("ct({ct_key},elwpkh({pk}))#m5mvyh29"),
499+
] {
500+
let desc = Descriptor::<DefiniteDescriptorKey>::from_str(&desc_str).unwrap();
501+
assert_eq!(desc.to_string(), desc_str);
502+
assert_eq!(addr_conf, &desc.address(&secp, &elements::AddressParams::ELEMENTS).unwrap().to_string());
503+
assert_eq!(addr_unconf, &desc.unconfidential_address(&elements::AddressParams::ELEMENTS).unwrap().to_string());
504+
}
505+
}
506+
473507
#[test]
474508
fn descriptor_wildcard() {
475509
let secp = secp256k1_zkp::Secp256k1::new();

src/descriptor/key.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::{error, fmt};
77
use bitcoin::hash_types::XpubIdentifier;
88
use bitcoin::{self, bip32};
99
use elements::hashes::{hash160, ripemd160, sha256, Hash, HashEngine};
10+
use elements::hex::FromHex;
1011
use elements::secp256k1_zkp::{Secp256k1, Signing, Verification};
1112

1213
#[cfg(feature = "serde")]
@@ -382,7 +383,7 @@ impl DescriptorSecretKey {
382383
}
383384

384385
/// Writes the fingerprint of the origin, if there is one.
385-
fn maybe_fmt_master_id(
386+
pub(crate) fn maybe_fmt_master_id(
386387
f: &mut fmt::Formatter<'_>,
387388
origin: &Option<(bip32::Fingerprint, bip32::DerivationPath)>,
388389
) -> fmt::Result {
@@ -691,13 +692,21 @@ impl DescriptorPublicKey {
691692
}
692693
}
693694

694-
impl FromStr for DescriptorSecretKey {
695-
type Err = DescriptorKeyParseError;
696-
697-
fn from_str(s: &str) -> Result<Self, Self::Err> {
695+
impl DescriptorSecretKey {
696+
pub(crate) fn from_str_inner(s: &str, single_hex: bool) -> Result<Self, DescriptorKeyParseError> {
698697
let (key_part, origin) = parse_key_origin(s)?;
699698

700-
if key_part.len() <= 52 {
699+
if single_hex && key_part.len() == 64 {
700+
let bytes = Vec::<u8>::from_hex(key_part)
701+
.map_err(|_| DescriptorKeyParseError("Error while parsing a HEX private key"))?;
702+
let network = bitcoin::network::constants::Network::Bitcoin; // Use any network
703+
let sk = bitcoin::PrivateKey::from_slice(&bytes, network)
704+
.map_err(|_| DescriptorKeyParseError("Error while parsing a HEX private key"))?;
705+
Ok(DescriptorSecretKey::Single(SinglePriv {
706+
key: sk,
707+
origin: None,
708+
}))
709+
} else if !single_hex && key_part.len() <= 52 {
701710
let sk = bitcoin::PrivateKey::from_str(key_part)
702711
.map_err(|_| DescriptorKeyParseError("Error while parsing a WIF private key"))?;
703712
Ok(DescriptorSecretKey::Single(SinglePriv {
@@ -726,6 +735,14 @@ impl FromStr for DescriptorSecretKey {
726735
}
727736
}
728737

738+
impl FromStr for DescriptorSecretKey {
739+
type Err = DescriptorKeyParseError;
740+
741+
fn from_str(s: &str) -> Result<Self, Self::Err> {
742+
Self::from_str_inner(s, false)
743+
}
744+
}
745+
729746
// Parse the origin information part of a descriptor key.
730747
fn parse_key_origin(s: &str) -> Result<(&str, Option<bip32::KeySource>), DescriptorKeyParseError> {
731748
for ch in s.as_bytes() {

src/descriptor/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ pub use self::key::{
5555
DescriptorMultiXKey, DescriptorPublicKey, DescriptorSecretKey, DescriptorXKey, InnerXKey,
5656
SinglePriv, SinglePub, SinglePubKey, Wildcard,
5757
};
58+
pub(crate) use self::key::maybe_fmt_master_id;
5859
pub use self::tr::{TapTree, Tr, TapLeafScript};
5960
/// Alias type for a map of public key to secret key
6061
///

0 commit comments

Comments
 (0)