Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 00342a1

Browse files
committedOct 29, 2024
Implement GetKey for KeyMap
Create a `KeyMay` type to replace the current `BTreeMap` alias and implement `bitcoin::psbt::GetKey` for it. Close: rust-bitcoin#709
1 parent 3a85952 commit 00342a1

File tree

3 files changed

+304
-10
lines changed

3 files changed

+304
-10
lines changed
 

‎src/descriptor/key_map.rs

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

‎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)
Please sign in to comment.