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 17a9068

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 17a9068

File tree

3 files changed

+288
-15
lines changed

3 files changed

+288
-15
lines changed
 

‎src/descriptor/key_map.rs

+277
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
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(&mut self, key: DescriptorPublicKey, value: DescriptorSecretKey) -> Option<DescriptorSecretKey> {
31+
self.map.insert(key, value)
32+
}
33+
34+
/// Gets `key` if it exists.
35+
///
36+
/// See [`BTreeMap::insert`] for more information.
37+
pub fn get(&self, key: &DescriptorPublicKey) -> Option<&DescriptorSecretKey> {
38+
self.map.get(key)
39+
}
40+
41+
/// Returns the number of items in this map.
42+
///
43+
/// See [`BTreeMap::insert`] for more information.
44+
pub fn len(&self) -> usize { self.map.len() }
45+
}
46+
47+
impl GetKey for KeyMap {
48+
type Error = GetKeyError;
49+
50+
fn get_key<C: Signing>(
51+
&self,
52+
key_request: &KeyRequest,
53+
secp: &Secp256k1<C>
54+
) -> Result<Option<bitcoin::PrivateKey>, Self::Error> {
55+
Ok(self.map.iter().find_map(|(k, v)| {
56+
match k {
57+
DescriptorPublicKey::Single(ref pk) => {
58+
match key_request {
59+
KeyRequest::Pubkey(ref request) => {
60+
match pk.key {
61+
SinglePubKey::FullKey(ref pk) => {
62+
if pk == request {
63+
match v {
64+
DescriptorSecretKey::Single(ref sk) => Some(sk.key),
65+
_ => unreachable!("Single maps to Single"),
66+
}
67+
} else {
68+
None
69+
}
70+
}
71+
SinglePubKey::XOnly(_) => None,
72+
}
73+
},
74+
_ => None,
75+
}
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+
let child = xkey.derive_priv(secp, &xpriv.derivation_path);
88+
Some(bitcoin::PrivateKey::new(child.private_key, xkey.network))
89+
}
90+
_ => unreachable!("XPrv maps to XPrv"),
91+
}
92+
} else {
93+
None
94+
}
95+
},
96+
KeyRequest::Bip32(..) => {
97+
match v {
98+
DescriptorSecretKey::XPrv(xpriv) => {
99+
if let Ok(Some(sk)) = xpriv.xkey.get_key(key_request, secp) {
100+
Some(sk)
101+
} else {
102+
None
103+
}
104+
}
105+
_ => unreachable!("XPrv maps to XPrv"),
106+
}
107+
},
108+
_ => unreachable!("rust-bitcoin v0.32")
109+
}
110+
}
111+
DescriptorPublicKey::MultiXPub(ref xpub) => {
112+
let pk = xpub.xkey.public_key;
113+
match key_request {
114+
KeyRequest::Pubkey(ref request) => {
115+
if pk == request.inner {
116+
match v {
117+
DescriptorSecretKey::MultiXPrv(xpriv) => Some(xpriv.xkey.to_private_key()),
118+
_ => unreachable!("MultiXPrv maps to MultiXPrv"),
119+
}
120+
} else {
121+
None
122+
}
123+
},
124+
KeyRequest::Bip32(..) => {
125+
match v {
126+
DescriptorSecretKey::MultiXPrv(xpriv) => {
127+
if let Ok(Some(sk)) = xpriv.xkey.get_key(key_request, secp) {
128+
Some(sk)
129+
} else {
130+
None
131+
}
132+
}
133+
_ => unreachable!("MultiXPrv maps to MultiXPrv"),
134+
}
135+
},
136+
_ => unreachable!("rust-bitcoin v0.32")
137+
}
138+
},
139+
}
140+
}))
141+
}
142+
}
143+
144+
#[cfg(test)]
145+
mod tests {
146+
// use bitcoin::NetworkKind;
147+
use bitcoin::bip32::{ChildNumber, IntoDerivationPath, Xpriv};
148+
149+
use super::*;
150+
use crate::Descriptor;
151+
152+
#[test]
153+
fn get_key_single_key() {
154+
let secp = Secp256k1::new();
155+
156+
let descriptor_sk_s = "[90b6a706/44'/0'/0'/0/0]cMk8gWmj1KpjdYnAWwsEDekodMYhbyYBhG8gMtCCxucJ98JzcNij";
157+
158+
let single = match descriptor_sk_s.parse::<DescriptorSecretKey>().unwrap() {
159+
DescriptorSecretKey::Single(single) => single,
160+
_ => panic!("unexpected DescriptorSecretKey variant"),
161+
};
162+
163+
let want_sk = single.key;
164+
let descriptor_s = format!("wpkh({})", descriptor_sk_s);
165+
let (_, keymap) = Descriptor::parse_descriptor(&secp, &descriptor_s).unwrap();
166+
167+
let pk = want_sk.public_key(&secp);
168+
let request = KeyRequest::Pubkey(pk);
169+
let got_sk = keymap.get_key(&request, &secp)
170+
.expect("get_key call errored")
171+
.expect("failed to find the key");
172+
assert_eq!(got_sk, want_sk)
173+
}
174+
175+
#[test]
176+
fn get_key_xpriv_single_key_xpriv() {
177+
let secp = Secp256k1::new();
178+
179+
let s = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi";
180+
181+
let xpriv = s.parse::<Xpriv>().unwrap();
182+
let xpriv_fingerprint = xpriv.fingerprint(&secp);
183+
184+
// Sanity check.
185+
{
186+
let descriptor_sk_s = format!("[{}]{}", xpriv_fingerprint, xpriv);
187+
let descriptor_sk = descriptor_sk_s.parse::<DescriptorSecretKey>().unwrap();
188+
let got = match descriptor_sk {
189+
DescriptorSecretKey::XPrv(x) => x.xkey,
190+
_ => panic!("unexpected DescriptorSecretKey variant"),
191+
};
192+
assert_eq!(got, xpriv);
193+
}
194+
195+
let want_sk = xpriv.to_priv();
196+
let descriptor_s = format!("wpkh([{}]{})", xpriv_fingerprint, xpriv);
197+
let (_, keymap) = Descriptor::parse_descriptor(&secp, &descriptor_s).unwrap();
198+
199+
let pk = want_sk.public_key(&secp);
200+
let request = KeyRequest::Pubkey(pk);
201+
let got_sk = keymap.get_key(&request, &secp)
202+
.expect("get_key call errored")
203+
.expect("failed to find the key");
204+
assert_eq!(got_sk, want_sk)
205+
}
206+
207+
#[test]
208+
fn get_key_xpriv_child_depth_one() {
209+
let secp = Secp256k1::new();
210+
211+
let s = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi";
212+
let master = s.parse::<Xpriv>().unwrap();
213+
let master_fingerprint = master.fingerprint(&secp);
214+
215+
let child_number = ChildNumber::from_hardened_idx(44).unwrap();
216+
let child = master.derive_priv(&secp, &[child_number]);
217+
218+
// Sanity check.
219+
{
220+
let descriptor_sk_s = format!("[{}/44']{}", master_fingerprint, child);
221+
let descriptor_sk = descriptor_sk_s.parse::<DescriptorSecretKey>().unwrap();
222+
let got = match descriptor_sk {
223+
DescriptorSecretKey::XPrv(ref x) => x.xkey,
224+
_ => panic!("unexpected DescriptorSecretKey variant"),
225+
};
226+
assert_eq!(got, child);
227+
}
228+
229+
let want_sk = child.to_priv();
230+
let descriptor_s = format!("wpkh({}/44')", s);
231+
let (_, keymap) = Descriptor::parse_descriptor(&secp, &descriptor_s).unwrap();
232+
233+
let pk = want_sk.public_key(&secp);
234+
let request = KeyRequest::Pubkey(pk);
235+
let got_sk = keymap.get_key(&request, &secp)
236+
.expect("get_key call errored")
237+
.expect("failed to find the key");
238+
assert_eq!(got_sk, want_sk)
239+
}
240+
241+
#[test]
242+
fn get_key_xpriv_with_path() {
243+
let secp = Secp256k1::new();
244+
245+
let s = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi";
246+
let master = s.parse::<Xpriv>().unwrap();
247+
let master_fingerprint = master.fingerprint(&secp);
248+
249+
let first_external_child = "44'/0'/0'/0/0";
250+
let derivation_path = first_external_child.into_derivation_path().unwrap();
251+
252+
let child = master.derive_priv(&secp, &derivation_path);
253+
254+
// Sanity check.
255+
{
256+
let descriptor_sk_s = format!("[{}/{}]{}", master_fingerprint, first_external_child, child);
257+
let descriptor_sk = descriptor_sk_s.parse::<DescriptorSecretKey>().unwrap();
258+
let got = match descriptor_sk {
259+
DescriptorSecretKey::XPrv(ref x) => x.xkey,
260+
_ => panic!("unexpected DescriptorSecretKey variant"),
261+
};
262+
assert_eq!(got, child);
263+
}
264+
265+
let want_sk = child.to_priv();
266+
let descriptor_s = format!("wpkh({}/44'/0'/0'/0/*)", s);
267+
let (_, keymap) = Descriptor::parse_descriptor(&secp, &descriptor_s).unwrap();
268+
269+
let key_source = (master_fingerprint, derivation_path);
270+
let request = KeyRequest::Bip32(key_source);
271+
let got_sk = keymap.get_key(&request, &secp)
272+
.expect("get_key call errored")
273+
.expect("failed to find the key");
274+
275+
assert_eq!(got_sk, want_sk)
276+
}
277+
}

‎src/descriptor/mod.rs

+10-14
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,17 @@ pub use self::tr::{TapTree, Tr};
4646

4747
pub mod checksum;
4848
mod key;
49-
50-
pub use self::key::{
51-
ConversionError, DefiniteDescriptorKey, DerivPaths, DescriptorKeyParseError,
52-
DescriptorMultiXKey, DescriptorPublicKey, DescriptorSecretKey, DescriptorXKey, InnerXKey,
53-
SinglePriv, SinglePub, SinglePubKey, Wildcard,
49+
mod key_map;
50+
51+
pub use self::{
52+
key::{
53+
ConversionError, DefiniteDescriptorKey, DerivPaths, DescriptorKeyParseError,
54+
DescriptorMultiXKey, DescriptorPublicKey, DescriptorSecretKey, DescriptorXKey, InnerXKey,
55+
SinglePriv, SinglePub, SinglePubKey, Wildcard,
56+
},
57+
key_map::KeyMap,
5458
};
5559

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>;
63-
6460
/// Script descriptor
6561
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
6662
pub enum Descriptor<Pk: MiniscriptKey> {
@@ -728,7 +724,7 @@ impl Descriptor<DescriptorPublicKey> {
728724
Ok(public_key)
729725
}
730726

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

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

‎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.