Skip to content

Commit ba5b2bf

Browse files
committed
Implement GetKey for KeyMap
Create a `KeyMay` type to replace the current `BTreeMap` alias and implement `bitcoin::psbt::GetKey` for it. Close: #709
1 parent 3a85952 commit ba5b2bf

File tree

3 files changed

+291
-10
lines changed

3 files changed

+291
-10
lines changed

src/descriptor/key_map.rs

+287
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
// SPDX-License-Identifier: CC0-1.0
2+
3+
//! A map of public key to secret key.
4+
5+
use bitcoin::psbt::{GetKey, GetKeyError, KeyRequest};
6+
use bitcoin::secp256k1::{Secp256k1, Signing};
7+
8+
use super::{DescriptorPublicKey, DescriptorSecretKey, SinglePubKey};
9+
use crate::prelude::BTreeMap;
10+
11+
/// Alias type for a map of public key to secret key
12+
///
13+
/// This map is returned whenever a descriptor that contains secrets is parsed using
14+
/// [`Descriptor::parse_descriptor`], since the descriptor will always only contain
15+
/// public keys. This map allows looking up the corresponding secret key given a
16+
/// public key from the descriptor.
17+
#[derive(Debug, Eq, PartialEq, Clone)]
18+
pub struct KeyMap {
19+
/// The map.
20+
pub map: BTreeMap<DescriptorPublicKey, DescriptorSecretKey>,
21+
}
22+
23+
impl KeyMap {
24+
/// Creates a new empty `KeyMap`.
25+
pub fn new() -> Self { Self { map: BTreeMap::new() } }
26+
27+
/// Inserts `key` and `value` into the map.
28+
///
29+
/// See [`BTreeMap::insert`] for more information.
30+
pub fn insert(
31+
&mut self,
32+
key: DescriptorPublicKey,
33+
value: DescriptorSecretKey,
34+
) -> Option<DescriptorSecretKey> {
35+
self.map.insert(key, value)
36+
}
37+
38+
/// Gets `key` if it exists.
39+
///
40+
/// See [`BTreeMap::insert`] for more information.
41+
pub fn get(&self, key: &DescriptorPublicKey) -> Option<&DescriptorSecretKey> {
42+
self.map.get(key)
43+
}
44+
45+
/// Returns the number of items in this map.
46+
///
47+
/// See [`BTreeMap::insert`] for more information.
48+
pub fn len(&self) -> usize { self.map.len() }
49+
}
50+
51+
impl GetKey for KeyMap {
52+
type Error = GetKeyError;
53+
54+
fn get_key<C: Signing>(
55+
&self,
56+
key_request: KeyRequest,
57+
secp: &Secp256k1<C>,
58+
) -> Result<Option<bitcoin::PrivateKey>, Self::Error> {
59+
Ok(self.map.iter().find_map(|(k, v)| {
60+
match k {
61+
DescriptorPublicKey::Single(ref pk) => match key_request {
62+
KeyRequest::Pubkey(ref request) => match pk.key {
63+
SinglePubKey::FullKey(ref pk) => {
64+
if pk == request {
65+
match v {
66+
DescriptorSecretKey::Single(ref sk) => Some(sk.key),
67+
_ => unreachable!("Single maps to Single"),
68+
}
69+
} else {
70+
None
71+
}
72+
}
73+
SinglePubKey::XOnly(_) => None,
74+
},
75+
_ => None,
76+
},
77+
// Performance: Might be faster to check the origin and then if it matches return
78+
// the key directly instead of calling `get_key` on the xpriv.
79+
DescriptorPublicKey::XPub(ref xpub) => {
80+
let pk = xpub.xkey.public_key;
81+
match key_request {
82+
KeyRequest::Pubkey(ref request) => {
83+
if pk == request.inner {
84+
match v {
85+
DescriptorSecretKey::XPrv(xpriv) => {
86+
let xkey = xpriv.xkey;
87+
if let Ok(child) = xkey.derive_priv(secp, &xpriv.derivation_path) {
88+
Some(bitcoin::PrivateKey::new(
89+
child.private_key,
90+
xkey.network,
91+
))
92+
} else {
93+
None
94+
}
95+
}
96+
_ => unreachable!("XPrv maps to XPrv"),
97+
}
98+
} else {
99+
None
100+
}
101+
}
102+
KeyRequest::Bip32(..) => match v {
103+
DescriptorSecretKey::XPrv(xpriv) => {
104+
if let Ok(Some(sk)) = xpriv.xkey.get_key(key_request.clone(), secp) {
105+
Some(sk)
106+
} else {
107+
None
108+
}
109+
}
110+
_ => unreachable!("XPrv maps to XPrv"),
111+
},
112+
_ => unreachable!("rust-bitcoin v0.32"),
113+
}
114+
}
115+
DescriptorPublicKey::MultiXPub(ref xpub) => {
116+
let pk = xpub.xkey.public_key;
117+
match key_request {
118+
KeyRequest::Pubkey(ref request) => {
119+
if pk == request.inner {
120+
match v {
121+
DescriptorSecretKey::MultiXPrv(xpriv) => {
122+
Some(xpriv.xkey.to_priv())
123+
}
124+
_ => unreachable!("MultiXPrv maps to MultiXPrv"),
125+
}
126+
} else {
127+
None
128+
}
129+
}
130+
KeyRequest::Bip32(..) => match v {
131+
DescriptorSecretKey::MultiXPrv(xpriv) => {
132+
if let Ok(Some(sk)) = xpriv.xkey.get_key(key_request.clone(), secp) {
133+
Some(sk)
134+
} else {
135+
None
136+
}
137+
}
138+
_ => unreachable!("MultiXPrv maps to MultiXPrv"),
139+
},
140+
_ => unreachable!("rust-bitcoin v0.32"),
141+
}
142+
}
143+
}
144+
}))
145+
}
146+
}
147+
148+
#[cfg(test)]
149+
mod tests {
150+
// use bitcoin::NetworkKind;
151+
use bitcoin::bip32::{ChildNumber, IntoDerivationPath, Xpriv};
152+
153+
use super::*;
154+
use crate::Descriptor;
155+
156+
#[test]
157+
fn get_key_single_key() {
158+
let secp = Secp256k1::new();
159+
160+
let descriptor_sk_s =
161+
"[90b6a706/44'/0'/0'/0/0]cMk8gWmj1KpjdYnAWwsEDekodMYhbyYBhG8gMtCCxucJ98JzcNij";
162+
163+
let single = match descriptor_sk_s.parse::<DescriptorSecretKey>().unwrap() {
164+
DescriptorSecretKey::Single(single) => single,
165+
_ => panic!("unexpected DescriptorSecretKey variant"),
166+
};
167+
168+
let want_sk = single.key;
169+
let descriptor_s = format!("wpkh({})", descriptor_sk_s);
170+
let (_, keymap) = Descriptor::parse_descriptor(&secp, &descriptor_s).unwrap();
171+
172+
let pk = want_sk.public_key(&secp);
173+
let request = KeyRequest::Pubkey(pk);
174+
let got_sk = keymap
175+
.get_key(request, &secp)
176+
.expect("get_key call errored")
177+
.expect("failed to find the key");
178+
assert_eq!(got_sk, want_sk)
179+
}
180+
181+
#[test]
182+
fn get_key_xpriv_single_key_xpriv() {
183+
let secp = Secp256k1::new();
184+
185+
let s = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi";
186+
187+
let xpriv = s.parse::<Xpriv>().unwrap();
188+
let xpriv_fingerprint = xpriv.fingerprint(&secp);
189+
190+
// Sanity check.
191+
{
192+
let descriptor_sk_s = format!("[{}]{}", xpriv_fingerprint, xpriv);
193+
let descriptor_sk = descriptor_sk_s.parse::<DescriptorSecretKey>().unwrap();
194+
let got = match descriptor_sk {
195+
DescriptorSecretKey::XPrv(x) => x.xkey,
196+
_ => panic!("unexpected DescriptorSecretKey variant"),
197+
};
198+
assert_eq!(got, xpriv);
199+
}
200+
201+
let want_sk = xpriv.to_priv();
202+
let descriptor_s = format!("wpkh([{}]{})", xpriv_fingerprint, xpriv);
203+
let (_, keymap) = Descriptor::parse_descriptor(&secp, &descriptor_s).unwrap();
204+
205+
let pk = want_sk.public_key(&secp);
206+
let request = KeyRequest::Pubkey(pk);
207+
let got_sk = keymap
208+
.get_key(request, &secp)
209+
.expect("get_key call errored")
210+
.expect("failed to find the key");
211+
assert_eq!(got_sk, want_sk)
212+
}
213+
214+
#[test]
215+
fn get_key_xpriv_child_depth_one() {
216+
let secp = Secp256k1::new();
217+
218+
let s = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi";
219+
let master = s.parse::<Xpriv>().unwrap();
220+
let master_fingerprint = master.fingerprint(&secp);
221+
222+
let child_number = ChildNumber::from_hardened_idx(44).unwrap();
223+
let child = master.derive_priv(&secp, &[child_number]).unwrap();
224+
225+
// Sanity check.
226+
{
227+
let descriptor_sk_s = format!("[{}/44']{}", master_fingerprint, child);
228+
let descriptor_sk = descriptor_sk_s.parse::<DescriptorSecretKey>().unwrap();
229+
let got = match descriptor_sk {
230+
DescriptorSecretKey::XPrv(ref x) => x.xkey,
231+
_ => panic!("unexpected DescriptorSecretKey variant"),
232+
};
233+
assert_eq!(got, child);
234+
}
235+
236+
let want_sk = child.to_priv();
237+
let descriptor_s = format!("wpkh({}/44')", s);
238+
let (_, keymap) = Descriptor::parse_descriptor(&secp, &descriptor_s).unwrap();
239+
240+
let pk = want_sk.public_key(&secp);
241+
let request = KeyRequest::Pubkey(pk);
242+
let got_sk = keymap
243+
.get_key(request, &secp)
244+
.expect("get_key call errored")
245+
.expect("failed to find the key");
246+
assert_eq!(got_sk, want_sk)
247+
}
248+
249+
#[test]
250+
fn get_key_xpriv_with_path() {
251+
let secp = Secp256k1::new();
252+
253+
let s = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi";
254+
let master = s.parse::<Xpriv>().unwrap();
255+
let master_fingerprint = master.fingerprint(&secp);
256+
257+
let first_external_child = "44'/0'/0'/0/0";
258+
let derivation_path = first_external_child.into_derivation_path().unwrap();
259+
260+
let child = master.derive_priv(&secp, &derivation_path).unwrap();
261+
262+
// Sanity check.
263+
{
264+
let descriptor_sk_s =
265+
format!("[{}/{}]{}", master_fingerprint, first_external_child, child);
266+
let descriptor_sk = descriptor_sk_s.parse::<DescriptorSecretKey>().unwrap();
267+
let got = match descriptor_sk {
268+
DescriptorSecretKey::XPrv(ref x) => x.xkey,
269+
_ => panic!("unexpected DescriptorSecretKey variant"),
270+
};
271+
assert_eq!(got, child);
272+
}
273+
274+
let want_sk = child.to_priv();
275+
let descriptor_s = format!("wpkh({}/44'/0'/0'/0/*)", s);
276+
let (_, keymap) = Descriptor::parse_descriptor(&secp, &descriptor_s).unwrap();
277+
278+
let key_source = (master_fingerprint, derivation_path);
279+
let request = KeyRequest::Bip32(key_source);
280+
let got_sk = keymap
281+
.get_key(request, &secp)
282+
.expect("get_key call errored")
283+
.expect("failed to find the key");
284+
285+
assert_eq!(got_sk, want_sk)
286+
}
287+
}

src/descriptor/mod.rs

+3-9
Original file line numberDiff line numberDiff line change
@@ -46,20 +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
};
55-
56-
/// Alias type for a map of public key to secret key
57-
///
58-
/// This map is returned whenever a descriptor that contains secrets is parsed using
59-
/// [`Descriptor::parse_descriptor`], since the descriptor will always only contain
60-
/// public keys. This map allows looking up the corresponding secret key given a
61-
/// public key from the descriptor.
62-
pub type KeyMap = BTreeMap<DescriptorPublicKey, DescriptorSecretKey>;
56+
pub use self::key_map::KeyMap;
6357

6458
/// Script descriptor
6559
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -728,7 +722,7 @@ impl Descriptor<DescriptorPublicKey> {
728722
Ok(public_key)
729723
}
730724

731-
let mut keymap_pk = KeyMapWrapper(BTreeMap::new(), secp);
725+
let mut keymap_pk = KeyMapWrapper(KeyMap::new(), secp);
732726

733727
struct KeyMapWrapper<'a, C: secp256k1::Signing>(KeyMap, &'a secp256k1::Secp256k1<C>);
734728

src/plan.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,7 @@ pub trait IntoAssets {
661661
}
662662

663663
impl IntoAssets for KeyMap {
664-
fn into_assets(self) -> Assets { Assets::from_iter(self.into_iter().map(|(k, _)| k)) }
664+
fn into_assets(self) -> Assets { Assets::from_iter(self.map.into_iter().map(|(k, _)| k)) }
665665
}
666666

667667
impl IntoAssets for DescriptorPublicKey {

0 commit comments

Comments
 (0)