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