Skip to content

Commit c7d4171

Browse files
committed
feat(descriptor): backport GetKey impl from rust-bitcoin#851
- add `KeyMapWrapper` with `GetKey` trait for PSBT signing - implement `GetKey` for `DescriptorSecretKey` - add `From<KeyMap>` conversion - export new `KeyMapWrapper` for downstream crate usage
1 parent 58d1df9 commit c7d4171

File tree

2 files changed

+278
-0
lines changed

2 files changed

+278
-0
lines changed

src/descriptor/key_map.rs

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
use bitcoin::psbt::{GetKey, GetKeyError, KeyRequest};
2+
use bitcoin::secp256k1::{Secp256k1, Signing};
3+
use bitcoin::PrivateKey;
4+
5+
use crate::descriptor::{DescriptorSecretKey, KeyMap};
6+
use crate::BTreeMap;
7+
8+
/// A wrapper around KeyMap that implements GetKey for PSBT signing.
9+
#[derive(Debug, Clone, Eq, PartialEq)]
10+
pub struct KeyMapWrapper {
11+
map: KeyMap,
12+
}
13+
14+
impl From<KeyMap> for KeyMapWrapper {
15+
fn from(map: KeyMap) -> Self { KeyMapWrapper { map } }
16+
}
17+
18+
impl GetKey for KeyMapWrapper {
19+
type Error = GetKeyError;
20+
21+
fn get_key<C: Signing>(
22+
&self,
23+
key_request: KeyRequest,
24+
secp: &Secp256k1<C>,
25+
) -> Result<Option<bitcoin::PrivateKey>, Self::Error> {
26+
Ok(self
27+
.map
28+
.iter()
29+
.find_map(|(_desc_pk, desc_sk)| -> Option<PrivateKey> {
30+
match desc_sk.get_key(key_request.clone(), secp) {
31+
Ok(Some(pk)) => Some(pk),
32+
Ok(None) | Err(_) => None,
33+
}
34+
}))
35+
}
36+
}
37+
38+
impl GetKey for DescriptorSecretKey {
39+
type Error = GetKeyError;
40+
41+
fn get_key<C: Signing>(
42+
&self,
43+
key_request: KeyRequest,
44+
secp: &Secp256k1<C>,
45+
) -> Result<Option<PrivateKey>, Self::Error> {
46+
match (self, key_request) {
47+
(DescriptorSecretKey::Single(single_priv), key_request) => {
48+
let sk = single_priv.key;
49+
let pk = sk.public_key(secp);
50+
let pubkey_map = BTreeMap::from([(pk, sk)]);
51+
pubkey_map.get_key(key_request, secp)
52+
}
53+
(DescriptorSecretKey::XPrv(descriptor_xkey), KeyRequest::Pubkey(public_key)) => {
54+
let xpriv = descriptor_xkey
55+
.xkey
56+
.derive_priv(secp, &descriptor_xkey.derivation_path)
57+
.map_err(GetKeyError::Bip32)?;
58+
let pk = xpriv.private_key.public_key(secp);
59+
60+
if public_key.inner.eq(&pk) {
61+
Ok(Some(xpriv.to_priv()))
62+
} else {
63+
Ok(None)
64+
}
65+
}
66+
(
67+
DescriptorSecretKey::XPrv(descriptor_xkey),
68+
ref key_request @ KeyRequest::Bip32(ref key_source),
69+
) => {
70+
if let Some(key) = descriptor_xkey.xkey.get_key(key_request.clone(), secp)? {
71+
return Ok(Some(key));
72+
}
73+
74+
if descriptor_xkey.matches(key_source, secp).is_some() {
75+
let (_, derivation_path) = key_source;
76+
return Ok(Some(
77+
descriptor_xkey
78+
.xkey
79+
.derive_priv(secp, &derivation_path)
80+
.map_err(GetKeyError::Bip32)?
81+
.to_priv(),
82+
));
83+
}
84+
85+
Ok(None)
86+
}
87+
(DescriptorSecretKey::XPrv(_), KeyRequest::XOnlyPubkey(_)) => {
88+
Err(GetKeyError::NotSupported)
89+
}
90+
(
91+
desc_multi_sk @ DescriptorSecretKey::MultiXPrv(_descriptor_multi_xkey),
92+
key_request,
93+
) => Ok(desc_multi_sk.clone().into_single_keys().iter().find_map(
94+
|desc_sk| match desc_sk.get_key(key_request.clone(), secp) {
95+
Ok(Some(pk)) => Some(pk),
96+
Ok(None) | Err(_) => None,
97+
},
98+
)),
99+
_ => Ok(None),
100+
}
101+
}
102+
}
103+
104+
#[cfg(test)]
105+
mod tests {
106+
use core::str::FromStr;
107+
108+
use bitcoin::bip32::{ChildNumber, DerivationPath, IntoDerivationPath, Xpriv};
109+
110+
use super::*;
111+
use crate::Descriptor;
112+
113+
#[test]
114+
fn get_key_single_key() {
115+
let secp = Secp256k1::new();
116+
117+
let descriptor_sk_s =
118+
"[90b6a706/44'/0'/0'/0/0]cMk8gWmj1KpjdYnAWwsEDekodMYhbyYBhG8gMtCCxucJ98JzcNij";
119+
120+
let single = match descriptor_sk_s.parse::<DescriptorSecretKey>().unwrap() {
121+
DescriptorSecretKey::Single(single) => single,
122+
_ => panic!("unexpected DescriptorSecretKey variant"),
123+
};
124+
125+
let want_sk = single.key;
126+
let descriptor_s = format!("wpkh({})", descriptor_sk_s);
127+
let (_, keymap) = Descriptor::parse_descriptor(&secp, &descriptor_s).unwrap();
128+
let keymap_wrapper = KeyMapWrapper::from(keymap);
129+
130+
let pk = want_sk.public_key(&secp);
131+
let request = KeyRequest::Pubkey(pk);
132+
let got_sk = keymap_wrapper
133+
.get_key(request, &secp)
134+
.expect("get_key call errored")
135+
.expect("failed to find the key");
136+
assert_eq!(got_sk, want_sk)
137+
}
138+
139+
#[test]
140+
fn get_key_xpriv_single_key_xpriv() {
141+
let secp = Secp256k1::new();
142+
143+
let s = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi";
144+
145+
let xpriv = s.parse::<Xpriv>().unwrap();
146+
let xpriv_fingerprint = xpriv.fingerprint(&secp);
147+
148+
// Sanity check.
149+
{
150+
let descriptor_sk_s = format!("[{}]{}", xpriv_fingerprint, xpriv);
151+
let descriptor_sk = descriptor_sk_s.parse::<DescriptorSecretKey>().unwrap();
152+
let got = match descriptor_sk {
153+
DescriptorSecretKey::XPrv(x) => x.xkey,
154+
_ => panic!("unexpected DescriptorSecretKey variant"),
155+
};
156+
assert_eq!(got, xpriv);
157+
}
158+
159+
let want_sk = xpriv.to_priv();
160+
let descriptor_s = format!("wpkh([{}]{})", xpriv_fingerprint, xpriv);
161+
let (_, keymap) = Descriptor::parse_descriptor(&secp, &descriptor_s).unwrap();
162+
let keymap_wrapper = KeyMapWrapper::from(keymap);
163+
164+
let pk = want_sk.public_key(&secp);
165+
let request = KeyRequest::Pubkey(pk);
166+
let got_sk = keymap_wrapper
167+
.get_key(request, &secp)
168+
.expect("get_key call errored")
169+
.expect("failed to find the key");
170+
assert_eq!(got_sk, want_sk)
171+
}
172+
173+
#[test]
174+
fn get_key_xpriv_child_depth_one() {
175+
let secp = Secp256k1::new();
176+
177+
let s = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi";
178+
let master = s.parse::<Xpriv>().unwrap();
179+
let master_fingerprint = master.fingerprint(&secp);
180+
181+
let child_number = ChildNumber::from_hardened_idx(44).unwrap();
182+
let child = master.derive_priv(&secp, &[child_number]).unwrap();
183+
184+
// Sanity check.
185+
{
186+
let descriptor_sk_s = format!("[{}/44']{}", master_fingerprint, child);
187+
let descriptor_sk = descriptor_sk_s.parse::<DescriptorSecretKey>().unwrap();
188+
let got = match descriptor_sk {
189+
DescriptorSecretKey::XPrv(ref x) => x.xkey,
190+
_ => panic!("unexpected DescriptorSecretKey variant"),
191+
};
192+
assert_eq!(got, child);
193+
}
194+
195+
let want_sk = child.to_priv();
196+
let descriptor_s = format!("wpkh({}/44')", s);
197+
let (_, keymap) = Descriptor::parse_descriptor(&secp, &descriptor_s).unwrap();
198+
let keymap_wrapper = KeyMapWrapper::from(keymap);
199+
200+
let pk = want_sk.public_key(&secp);
201+
let request = KeyRequest::Pubkey(pk);
202+
let got_sk = keymap_wrapper
203+
.get_key(request, &secp)
204+
.expect("get_key call errored")
205+
.expect("failed to find the key");
206+
assert_eq!(got_sk, want_sk)
207+
}
208+
209+
#[test]
210+
fn get_key_xpriv_with_path() {
211+
let secp = Secp256k1::new();
212+
213+
let s = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi";
214+
let master = s.parse::<Xpriv>().unwrap();
215+
let master_fingerprint = master.fingerprint(&secp);
216+
217+
let first_external_child = "44'/0'/0'/0/0";
218+
let derivation_path = first_external_child.into_derivation_path().unwrap();
219+
220+
let child = master.derive_priv(&secp, &derivation_path).unwrap();
221+
222+
// Sanity check.
223+
{
224+
let descriptor_sk_s =
225+
format!("[{}/{}]{}", master_fingerprint, first_external_child, child);
226+
let descriptor_sk = descriptor_sk_s.parse::<DescriptorSecretKey>().unwrap();
227+
let got = match descriptor_sk {
228+
DescriptorSecretKey::XPrv(ref x) => x.xkey,
229+
_ => panic!("unexpected DescriptorSecretKey variant"),
230+
};
231+
assert_eq!(got, child);
232+
}
233+
234+
let want_sk = child.to_priv();
235+
let descriptor_s = format!("wpkh({}/44'/0'/0'/0/*)", s);
236+
let (_, keymap) = Descriptor::parse_descriptor(&secp, &descriptor_s).unwrap();
237+
let keymap_wrapper = KeyMapWrapper::from(keymap);
238+
239+
let key_source = (master_fingerprint, derivation_path);
240+
let request = KeyRequest::Bip32(key_source);
241+
let got_sk = keymap_wrapper
242+
.get_key(request, &secp)
243+
.expect("get_key call errored")
244+
.expect("failed to find the key");
245+
246+
assert_eq!(got_sk, want_sk)
247+
}
248+
249+
#[test]
250+
fn get_key_xpriv_with_key_origin() {
251+
let secp = Secp256k1::new();
252+
253+
let descriptor_str = "wpkh([d34db33f/84h/1h/0h]tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)";
254+
let (_descriptor_pk, keymap) = Descriptor::parse_descriptor(&secp, descriptor_str).unwrap();
255+
let keymap_wrapper = KeyMapWrapper::from(keymap);
256+
257+
let descriptor_sk = DescriptorSecretKey::from_str("[d34db33f/84h/1h/0h]tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*").unwrap();
258+
let xpriv = match descriptor_sk {
259+
DescriptorSecretKey::XPrv(descriptor_xkey) => descriptor_xkey,
260+
_ => unreachable!(),
261+
};
262+
263+
let path = DerivationPath::from_str("84'/1'/0'/0").unwrap();
264+
let expected_pk = xpriv.xkey.derive_priv(&secp, &path).unwrap().to_priv();
265+
266+
let (fp, _) = xpriv.origin.unwrap();
267+
let key_request = KeyRequest::Bip32((fp, path));
268+
269+
let pk = keymap_wrapper
270+
.get_key(key_request, &secp)
271+
.expect("get_key should not fail")
272+
.expect("get_key should return a `PrivateKey`");
273+
274+
assert_eq!(pk, expected_pk);
275+
}
276+
}

src/descriptor/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,14 @@ pub use self::tr::{TapTree, Tr};
4646

4747
pub mod checksum;
4848
mod key;
49+
mod key_map;
4950

5051
pub use self::key::{
5152
ConversionError, DefiniteDescriptorKey, DerivPaths, DescriptorKeyParseError,
5253
DescriptorMultiXKey, DescriptorPublicKey, DescriptorSecretKey, DescriptorXKey, InnerXKey,
5354
SinglePriv, SinglePub, SinglePubKey, Wildcard,
5455
};
56+
pub use self::key_map::KeyMapWrapper;
5557

5658
/// Alias type for a map of public key to secret key
5759
///

0 commit comments

Comments
 (0)