bitcoin_encrypted_backup/
descriptor.rs1#[cfg(feature = "miniscript_12_0")]
2pub use mscript_12_0 as miniscript;
3#[cfg(feature = "miniscript_12_3_5")]
4pub use mscript_12_3_5 as miniscript;
5
6use std::collections::{BTreeSet, HashSet};
7use std::str::FromStr;
8
9use miniscript::{
10 bitcoin::{self, bip32::DerivationPath, secp256k1},
11 Descriptor, DescriptorPublicKey, ForEachKey,
12};
13
14use crate::Error;
15
16pub fn dpk_to_pk(key: &DescriptorPublicKey) -> bitcoin::secp256k1::PublicKey {
17 match key {
18 DescriptorPublicKey::Single(key) => match key.key {
19 miniscript::descriptor::SinglePubKey::FullKey(pk) => pk.inner,
20 miniscript::descriptor::SinglePubKey::XOnly(pk) => {
21 pk.public_key(bitcoin::key::Parity::Even)
22 }
23 },
24 DescriptorPublicKey::XPub(key) => key.xkey.public_key,
25 DescriptorPublicKey::MultiXPub(key) => key.xkey.public_key,
26 }
27}
28
29fn dpk_to_deriv_path(key: &DescriptorPublicKey) -> Option<DerivationPath> {
30 match key {
31 DescriptorPublicKey::Single(key) => key.origin.clone().map(|(_, p)| p),
32 DescriptorPublicKey::XPub(key) => key.origin.clone().map(|(_, p)| p),
33 DescriptorPublicKey::MultiXPub(key) => key.origin.clone().map(|(_, p)| p),
34 }
35}
36
37pub fn bip341_nums() -> bitcoin::secp256k1::PublicKey {
44 bitcoin::secp256k1::PublicKey::from_str(
45 "0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0",
46 )
47 .expect("Valid pubkey: NUMS from BIP341")
48}
49
50pub fn descr_to_dpks(
51 descriptor: &Descriptor<DescriptorPublicKey>,
52) -> Result<Vec<DescriptorPublicKey>, Error> {
53 let mut keys = BTreeSet::new();
54 descriptor.for_each_key(|k| {
55 let pk = dpk_to_pk(k);
56 if pk != bip341_nums() {
57 keys.insert(k.clone());
58 }
59 true
60 });
61 let keys: Vec<_> = keys.into_iter().collect();
62
63 if keys.is_empty() {
64 Err(Error::DescriptorHasNoKeys)
65 } else {
66 Ok(keys)
67 }
68}
69
70pub fn dpks_to_derivation_keys_paths(
71 dpks: &Vec<DescriptorPublicKey>,
72) -> (Vec<secp256k1::PublicKey>, Vec<DerivationPath>) {
73 let mut derivation_paths = HashSet::new();
74 let mut keys = HashSet::new();
75 for k in dpks {
76 keys.insert(dpk_to_pk(k));
77 if let Some(path) = dpk_to_deriv_path(k) {
78 derivation_paths.insert(path);
79 }
80 }
81 let deriv = derivation_paths.into_iter().collect();
82 let keys = keys.into_iter().collect();
83 (keys, deriv)
84}
85
86#[cfg(test)]
87pub mod tests {
88 use super::*;
89 use std::str::FromStr;
90
91 use miniscript::{
92 bitcoin::bip32::{self, ChainCode, ChildNumber, Fingerprint},
93 descriptor::{
94 self, DerivPaths, DescriptorMultiXKey, DescriptorXKey, SinglePub, SinglePubKey,
95 Wildcard,
96 },
97 Descriptor, DescriptorPublicKey, ToPublicKey,
98 };
99
100 pub fn descr_1() -> Descriptor<DescriptorPublicKey> {
101 let descr_str = "wsh(or_d(pk([58b7f8dc/48'/1'/0'/2']tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw/<0;1>/*),and_v(v:pkh([58b7f8dc/48'/1'/0'/2']tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw/<2;3>/*),older(52596))))#pggrcdd0";
102
103 Descriptor::<DescriptorPublicKey>::from_str(descr_str).unwrap()
104 }
105
106 pub fn dpk_1() -> DescriptorPublicKey {
107 let dpk_str = "[58b7f8dc/48'/1'/0'/2']tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw/<0;1>/*";
108 DescriptorPublicKey::from_str(dpk_str).unwrap()
109 }
110
111 fn dpk_2() -> DescriptorPublicKey {
112 let dpk_str = "[58b7f8dc/48'/1'/0'/2']tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw/<2;3>/*";
113 DescriptorPublicKey::from_str(dpk_str).unwrap()
114 }
115
116 fn dpk_3() -> DescriptorPublicKey {
117 let dpk_str = "tpubDEPBvXvhta3pjVaKokqC3eeMQnszj9ehFaA2zD5nSdkaccwGAizu8jVB2NeSpvmP2P52MBoZvNCixqXRJnTyXx51FQzARR63tjxQSyP3Btw/<2;3>/*";
118 DescriptorPublicKey::from_str(dpk_str).unwrap()
119 }
120 pub fn pk() -> secp256k1::PublicKey {
121 let raw = [
122 3, 235, 210, 82, 202, 8, 119, 170, 224, 155, 157, 5, 130, 25, 104, 39, 117, 170, 60,
123 188, 208, 73, 193, 47, 7, 131, 47, 44, 246, 163, 181, 23, 8,
124 ];
125 secp256k1::PublicKey::from_slice(&raw).unwrap()
126 }
127
128 #[test]
129 fn test_dpk_to_pk() {
130 let expected = pk();
131 let pk = dpk_to_pk(&dpk_1());
132 assert_eq!(pk, expected);
133 let pk = dpk_to_pk(&dpk_2());
134 assert_eq!(pk, expected);
135
136 let single_str = "0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0";
138 let dpk = DescriptorPublicKey::from_str(single_str).unwrap();
139 let pk = dpk_to_pk(&dpk);
140 let expected = bitcoin::secp256k1::PublicKey::from_str(single_str).unwrap();
141 assert_eq!(expected, pk);
142
143 let xonly = bitcoin::PublicKey::from_str(single_str)
145 .unwrap()
146 .to_x_only_pubkey();
147 let dpk = DescriptorPublicKey::Single(SinglePub {
148 origin: None,
149 key: descriptor::SinglePubKey::XOnly(xonly),
150 });
151 let pk = dpk_to_pk(&dpk);
152 assert_eq!(expected, pk);
153
154 let xpub = bip32::Xpub {
156 network: bitcoin::NetworkKind::Test,
157 depth: 1,
158 parent_fingerprint: Fingerprint::from_str("00000000").unwrap(),
159 child_number: ChildNumber::from_normal_idx(0).unwrap(),
160 public_key: bitcoin::secp256k1::PublicKey::from_str(single_str).unwrap(),
161 chain_code: ChainCode::from(&[1u8; 32]),
162 };
163 let dpk = DescriptorPublicKey::XPub(DescriptorXKey {
164 origin: None,
165 xkey: xpub,
166 derivation_path: DerivationPath::default(),
167 wildcard: Wildcard::None,
168 });
169 let pk = dpk_to_pk(&dpk);
170 assert_eq!(expected, pk);
171
172 let dpk = DescriptorPublicKey::MultiXPub(DescriptorMultiXKey {
174 origin: None,
175 xkey: xpub,
176 derivation_paths: DerivPaths::new(vec![DerivationPath::from_str("0").unwrap()])
177 .unwrap(),
178 wildcard: Wildcard::None,
179 });
180 let pk = dpk_to_pk(&dpk);
181 assert_eq!(expected, pk);
182 }
183
184 #[test]
185 fn test_dpk_to_deriv() {
186 let deriv_1 = dpk_to_deriv_path(&dpk_1()).unwrap();
187 assert_eq!(deriv_1, DerivationPath::from_str("48'/1'/0'/2'").unwrap());
188 let deriv_2 = dpk_to_deriv_path(&dpk_2()).unwrap();
189 assert_eq!(deriv_2, DerivationPath::from_str("48'/1'/0'/2'").unwrap());
190 let deriv_3 = dpk_to_deriv_path(&dpk_3());
191 assert!(deriv_3.is_none());
192
193 let dp = DerivationPath::from_str("0/0").unwrap();
194 let origin = Some((Fingerprint::from_str("aabbccdd").unwrap(), dp.clone()));
195
196 let single_str = "0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0";
198 let dpk = DescriptorPublicKey::from_str(single_str).unwrap();
199 let none = dpk_to_deriv_path(&dpk);
200 assert!(none.is_none());
201 let single_pk = SinglePubKey::FullKey(dpk_to_pk(&dpk).into());
202 let dpk = DescriptorPublicKey::Single(SinglePub {
203 origin: origin.clone(),
204 key: single_pk,
205 });
206 let deriv = dpk_to_deriv_path(&dpk).unwrap();
207 assert_eq!(deriv, dp);
208
209 let xpub = bip32::Xpub {
211 network: bitcoin::NetworkKind::Test,
212 depth: 1,
213 parent_fingerprint: Fingerprint::from_str("00000000").unwrap(),
214 child_number: ChildNumber::from_normal_idx(0).unwrap(),
215 public_key: bitcoin::secp256k1::PublicKey::from_str(single_str).unwrap(),
216 chain_code: ChainCode::from(&[1u8; 32]),
217 };
218 let dpk = DescriptorPublicKey::XPub(DescriptorXKey {
219 origin: None,
220 xkey: xpub,
221 derivation_path: DerivationPath::default(),
222 wildcard: Wildcard::None,
223 });
224 let none = dpk_to_deriv_path(&dpk);
225 assert!(none.is_none());
226 let dpk = DescriptorPublicKey::XPub(DescriptorXKey {
227 origin: origin.clone(),
228 xkey: xpub,
229 derivation_path: DerivationPath::default(),
230 wildcard: Wildcard::None,
231 });
232 let deriv = dpk_to_deriv_path(&dpk).unwrap();
233 assert_eq!(deriv, dp);
234
235 let dpk = DescriptorPublicKey::MultiXPub(DescriptorMultiXKey {
237 origin: None,
238 xkey: xpub,
239 derivation_paths: DerivPaths::new(vec![DerivationPath::from_str("0").unwrap()])
240 .unwrap(),
241 wildcard: Wildcard::None,
242 });
243 let none = dpk_to_deriv_path(&dpk);
244 assert!(none.is_none());
245 let dpk = DescriptorPublicKey::MultiXPub(DescriptorMultiXKey {
246 origin: origin.clone(),
247 xkey: xpub,
248 derivation_paths: DerivPaths::new(vec![DerivationPath::from_str("0").unwrap()])
249 .unwrap(),
250 wildcard: Wildcard::None,
251 });
252 let deriv = dpk_to_deriv_path(&dpk).unwrap();
253 assert_eq!(deriv, dp);
254 }
255
256 #[test]
257 fn test_descript_to_dpk() {
258 let dpks = descr_to_dpks(&descr_1()).unwrap();
259 let expected = vec![dpk_1(), dpk_2()];
260 assert_eq!(dpks, expected);
261 }
262
263 #[test]
264 fn test_descriptor_to_dpk_unspendable() {
265 let descr_str = "tr(tpubD6NzVbkrYhZ4XWBqjZ7DTB4eFvi8eQZ79UvNbQFsxXiaMNaBn83jpMWTXLX2Gx6JgC5n9jWvx6vnijcAUgxXmRtFd4ntasRGNsYSCvQteSr/<0;1>/*,{and_v(v:and_v(v:pk([d4ab66f1/48'/1'/0'/2']tpubDEXYN145WM4rVKtcWpySBYiVQ229pmrnyAGJT14BBh2QJr7ABJswchDicZfFaauLyXhDad1nCoCZQEwAW87JPotP93ykC9WJvoASnBjYBxW/<2;3>/*),pk([79af2d8a/48'/1'/0'/2']tpubDEtHs6m9crfv1oeETj6EXteAtW7eoSSBVBaypEdWZt8VftbHF9R12xSZpzWGNuAofeGPL6cz48dLdCYbVioHL8ygA56yuPW76Xz5WZ3dt8o/<2;3>/*)),older(52596)),and_v(v:pk([d4ab66f1/48'/1'/0'/2']tpubDEXYN145WM4rVKtcWpySBYiVQ229pmrnyAGJT14BBh2QJr7ABJswchDicZfFaauLyXhDad1nCoCZQEwAW87JPotP93ykC9WJvoASnBjYBxW/<0;1>/*),pk([79af2d8a/48'/1'/0'/2']tpubDEtHs6m9crfv1oeETj6EXteAtW7eoSSBVBaypEdWZt8VftbHF9R12xSZpzWGNuAofeGPL6cz48dLdCYbVioHL8ygA56yuPW76Xz5WZ3dt8o/<0;1>/*))})#vudj49fm";
266 let descriptor = Descriptor::<DescriptorPublicKey>::from_str(descr_str).unwrap();
267 let keys = descr_to_dpks(&descriptor).unwrap();
269 for key in keys {
270 let pk = dpk_to_pk(&key);
271 assert_ne!(pk, bip341_nums());
272 }
273 let contains_unspendable = descriptor.for_any_key(|k| {
275 let pk = dpk_to_pk(k);
276 pk == bip341_nums()
277 });
278 assert!(contains_unspendable);
279 }
280
281 #[test]
282 fn test_dpks_to_deriv_paths() {
283 let dpks = vec![dpk_1(), dpk_2()];
284 let pks = vec![pk()];
285 let deriv = vec![DerivationPath::from_str("48'/1'/0'/2'").unwrap()];
286 let res = dpks_to_derivation_keys_paths(&dpks);
287 assert_eq!(res, (pks, deriv));
288 }
289}