bdk_wallet/descriptor/template.rs
1// Bitcoin Dev Kit
2// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
3//
4// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
5//
6// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
7// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
8// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
9// You may not use this file except in accordance with one or both of these
10// licenses.
11
12//! Descriptor templates
13//!
14//! This module contains the definition of various common script templates that are ready to be
15//! used. See the documentation of each template for an example.
16
17use bitcoin::bip32;
18use bitcoin::Network;
19
20use miniscript::{Legacy, Segwitv0, Tap};
21
22use super::{ExtendedDescriptor, IntoWalletDescriptor, KeyMap};
23use crate::descriptor::DescriptorError;
24use crate::keys::{DerivableKey, IntoDescriptorKey, ValidNetworks};
25use crate::wallet::utils::SecpCtx;
26use crate::{descriptor, KeychainKind};
27
28/// Type alias for the return type of [`DescriptorTemplate`], [`descriptor!`](crate::descriptor!)
29/// and others
30pub type DescriptorTemplateOut = (ExtendedDescriptor, KeyMap, ValidNetworks);
31
32/// Trait for descriptor templates that can be built into a full descriptor
33///
34/// Since [`IntoWalletDescriptor`] is implemented for any [`DescriptorTemplate`], they can also be
35/// passed directly to the [`Wallet`](crate::Wallet) constructor.
36///
37/// ## Example
38///
39/// ```
40/// use bdk_wallet::descriptor::error::Error as DescriptorError;
41/// use bdk_wallet::keys::{IntoDescriptorKey, KeyError};
42/// use bdk_wallet::miniscript::Legacy;
43/// use bdk_wallet::template::{DescriptorTemplate, DescriptorTemplateOut};
44/// use bitcoin::Network;
45///
46/// struct MyP2PKH<K: IntoDescriptorKey<Legacy>>(K);
47///
48/// impl<K: IntoDescriptorKey<Legacy>> DescriptorTemplate for MyP2PKH<K> {
49/// fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
50/// Ok(bdk_wallet::descriptor!(pkh(self.0))?)
51/// }
52/// }
53/// ```
54pub trait DescriptorTemplate {
55 /// Build the complete descriptor
56 fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError>;
57}
58
59/// Turns a [`DescriptorTemplate`] into a valid wallet descriptor by calling its
60/// [`build`](DescriptorTemplate::build) method
61impl<T: DescriptorTemplate> IntoWalletDescriptor for T {
62 fn into_wallet_descriptor(
63 self,
64 secp: &SecpCtx,
65 network: Network,
66 ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
67 self.build(network)?.into_wallet_descriptor(secp, network)
68 }
69}
70
71/// P2PKH template. Expands to a descriptor `pkh(key)`
72///
73/// ## Example
74///
75/// ```
76/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
77/// # use bdk_wallet::Wallet;
78/// # use bdk_wallet::KeychainKind;
79/// use bdk_wallet::template::P2Pkh;
80///
81/// let key_external =
82/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
83/// let key_internal =
84/// bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?;
85/// let mut wallet = Wallet::create(P2Pkh(key_external), P2Pkh(key_internal))
86/// .network(Network::Testnet)
87/// .create_wallet_no_persist()?;
88///
89/// assert_eq!(
90/// wallet
91/// .next_unused_address(KeychainKind::External)
92/// .to_string(),
93/// "mwJ8hxFYW19JLuc65RCTaP4v1rzVU8cVMT"
94/// );
95/// # Ok::<_, Box<dyn std::error::Error>>(())
96/// ```
97#[derive(Debug, Clone)]
98pub struct P2Pkh<K: IntoDescriptorKey<Legacy>>(pub K);
99
100impl<K: IntoDescriptorKey<Legacy>> DescriptorTemplate for P2Pkh<K> {
101 fn build(self, _network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
102 descriptor!(pkh(self.0))
103 }
104}
105
106/// P2WPKH-P2SH template. Expands to a descriptor `sh(wpkh(key))`
107///
108/// ## Example
109///
110/// ```
111/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
112/// # use bdk_wallet::Wallet;
113/// # use bdk_wallet::KeychainKind;
114/// use bdk_wallet::template::P2Wpkh_P2Sh;
115///
116/// let key_external =
117/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
118/// let key_internal =
119/// bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?;
120/// let mut wallet = Wallet::create(P2Wpkh_P2Sh(key_external), P2Wpkh_P2Sh(key_internal))
121/// .network(Network::Testnet)
122/// .create_wallet_no_persist()?;
123///
124/// assert_eq!(
125/// wallet
126/// .next_unused_address(KeychainKind::External)
127/// .to_string(),
128/// "2NB4ox5VDRw1ecUv6SnT3VQHPXveYztRqk5"
129/// );
130/// # Ok::<_, Box<dyn std::error::Error>>(())
131/// ```
132#[allow(non_camel_case_types)]
133#[derive(Debug, Clone)]
134pub struct P2Wpkh_P2Sh<K: IntoDescriptorKey<Segwitv0>>(pub K);
135
136impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh_P2Sh<K> {
137 fn build(self, _network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
138 descriptor!(sh(wpkh(self.0)))
139 }
140}
141
142/// P2WPKH template. Expands to a descriptor `wpkh(key)`
143///
144/// ## Example
145///
146/// ```
147/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
148/// # use bdk_wallet::Wallet;
149/// # use bdk_wallet::KeychainKind;
150/// use bdk_wallet::template::P2Wpkh;
151///
152/// let key_external =
153/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
154/// let key_internal =
155/// bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?;
156/// let mut wallet = Wallet::create(P2Wpkh(key_external), P2Wpkh(key_internal))
157/// .network(Network::Testnet)
158/// .create_wallet_no_persist()?;
159///
160/// assert_eq!(
161/// wallet
162/// .next_unused_address(KeychainKind::External)
163/// .to_string(),
164/// "tb1q4525hmgw265tl3drrl8jjta7ayffu6jf68ltjd"
165/// );
166/// # Ok::<_, Box<dyn std::error::Error>>(())
167/// ```
168#[derive(Debug, Clone)]
169pub struct P2Wpkh<K: IntoDescriptorKey<Segwitv0>>(pub K);
170
171impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh<K> {
172 fn build(self, _network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
173 descriptor!(wpkh(self.0))
174 }
175}
176
177/// P2TR template. Expands to a descriptor `tr(key)`
178///
179/// ## Example
180///
181/// ```
182/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
183/// # use bdk_wallet::Wallet;
184/// # use bdk_wallet::KeychainKind;
185/// use bdk_wallet::template::P2TR;
186///
187/// let key_external =
188/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
189/// let key_internal =
190/// bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?;
191/// let mut wallet = Wallet::create(P2TR(key_external), P2TR(key_internal))
192/// .network(Network::Testnet)
193/// .create_wallet_no_persist()?;
194///
195/// assert_eq!(
196/// wallet
197/// .next_unused_address(KeychainKind::External)
198/// .to_string(),
199/// "tb1pvjf9t34fznr53u5tqhejz4nr69luzkhlvsdsdfq9pglutrpve2xq7hps46"
200/// );
201/// # Ok::<_, Box<dyn std::error::Error>>(())
202/// ```
203#[derive(Debug, Clone)]
204pub struct P2TR<K: IntoDescriptorKey<Tap>>(pub K);
205
206impl<K: IntoDescriptorKey<Tap>> DescriptorTemplate for P2TR<K> {
207 fn build(self, _network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
208 descriptor!(tr(self.0))
209 }
210}
211
212/// BIP44 template. Expands to `pkh(key/44'/{0,1}'/0'/{0,1}/*)`
213///
214/// Since there are hardened derivation steps, this template requires a private derivable key
215/// (generally a `xprv`/`tprv`).
216///
217/// See [`Bip44Public`] for a template that can work with a `xpub`/`tpub`.
218///
219/// ## Example
220///
221/// ```rust
222/// # use std::str::FromStr;
223/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
224/// # use bdk_wallet::{Wallet, KeychainKind};
225/// use bdk_wallet::template::Bip44;
226///
227/// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
228/// let mut wallet = Wallet::create(Bip44(key.clone(), KeychainKind::External), Bip44(key, KeychainKind::Internal))
229/// .network(Network::Testnet)
230/// .create_wallet_no_persist()?;
231///
232/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "mmogjc7HJEZkrLqyQYqJmxUqFaC7i4uf89");
233/// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "pkh([c55b303f/44'/1'/0']tpubDCuorCpzvYS2LCD75BR46KHE8GdDeg1wsAgNZeNr6DaB5gQK1o14uErKwKLuFmeemkQ6N2m3rNgvctdJLyr7nwu2yia7413Hhg8WWE44cgT/0/*)#5wrnv0xt");
234/// # Ok::<_, Box<dyn std::error::Error>>(())
235/// ```
236#[derive(Debug, Clone)]
237pub struct Bip44<K: DerivableKey<Legacy>>(pub K, pub KeychainKind);
238
239impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44<K> {
240 fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
241 P2Pkh(legacy::make_bipxx_private(44, self.0, self.1, network)?).build(network)
242 }
243}
244
245/// BIP44 public template. Expands to `pkh(key/{0,1}/*)`
246///
247/// This assumes that the key used has already been derived with `m/44'/0'/0'` for Mainnet or
248/// `m/44'/1'/0'` for Testnet.
249///
250/// This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
251///
252/// See [`Bip44`] for a template that does the full derivation, but requires private data
253/// for the key.
254///
255/// ## Example
256///
257/// ```
258/// # use std::str::FromStr;
259/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
260/// # use bdk_wallet::{KeychainKind, Wallet};
261/// use bdk_wallet::template::Bip44Public;
262///
263/// let key = bitcoin::bip32::Xpub::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU")?;
264/// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
265/// let mut wallet = Wallet::create(
266/// Bip44Public(key.clone(), fingerprint, KeychainKind::External),
267/// Bip44Public(key, fingerprint, KeychainKind::Internal),
268/// )
269/// .network(Network::Testnet)
270/// .create_wallet_no_persist()?;
271///
272/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "miNG7dJTzJqNbFS19svRdTCisC65dsubtR");
273/// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "pkh([c55b303f/44'/1'/0']tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU/0/*)#cfhumdqz");
274/// # Ok::<_, Box<dyn std::error::Error>>(())
275/// ```
276#[derive(Debug, Clone)]
277pub struct Bip44Public<K: DerivableKey<Legacy>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
278
279impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44Public<K> {
280 fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
281 P2Pkh(legacy::make_bipxx_public(
282 44, self.0, self.1, self.2, network,
283 )?)
284 .build(network)
285 }
286}
287
288/// BIP49 template. Expands to `sh(wpkh(key/49'/{0,1}'/0'/{0,1}/*))`
289///
290/// Since there are hardened derivation steps, this template requires a private derivable key
291/// (generally a `xprv`/`tprv`).
292///
293/// See [`Bip49Public`] for a template that can work with a `xpub`/`tpub`.
294///
295/// ## Example
296///
297/// ```
298/// # use std::str::FromStr;
299/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
300/// # use bdk_wallet::{Wallet, KeychainKind};
301/// use bdk_wallet::template::Bip49;
302///
303/// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
304/// let mut wallet = Wallet::create(
305/// Bip49(key.clone(), KeychainKind::External),
306/// Bip49(key, KeychainKind::Internal),
307/// )
308/// .network(Network::Testnet)
309/// .create_wallet_no_persist()?;
310///
311/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "2N4zkWAoGdUv4NXhSsU8DvS5MB36T8nKHEB");
312/// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "sh(wpkh([c55b303f/49'/1'/0']tpubDDYr4kdnZgjjShzYNjZUZXUUtpXaofdkMaipyS8ThEh45qFmhT4hKYways7UXmg6V7het1QiFo9kf4kYUXyDvV4rHEyvSpys9pjCB3pukxi/0/*))#s9vxlc8e");
313/// # Ok::<_, Box<dyn std::error::Error>>(())
314/// ```
315#[derive(Debug, Clone)]
316pub struct Bip49<K: DerivableKey<Segwitv0>>(pub K, pub KeychainKind);
317
318impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49<K> {
319 fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
320 P2Wpkh_P2Sh(segwit_v0::make_bipxx_private(49, self.0, self.1, network)?).build(network)
321 }
322}
323
324/// BIP49 public template. Expands to `sh(wpkh(key/{0,1}/*))`
325///
326/// This assumes that the key used has already been derived with `m/49'/0'/0'` for Mainnet or
327/// `m/49'/1'/0'` for Testnet.
328///
329/// This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
330///
331/// See [`Bip49`] for a template that does the full derivation, but requires private data
332/// for the key.
333///
334/// ## Example
335///
336/// ```
337/// # use std::str::FromStr;
338/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
339/// # use bdk_wallet::{Wallet, KeychainKind};
340/// use bdk_wallet::template::Bip49Public;
341///
342/// let key = bitcoin::bip32::Xpub::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L")?;
343/// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
344/// let mut wallet = Wallet::create(
345/// Bip49Public(key.clone(), fingerprint, KeychainKind::External),
346/// Bip49Public(key, fingerprint, KeychainKind::Internal),
347/// )
348/// .network(Network::Testnet)
349/// .create_wallet_no_persist()?;
350///
351/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "2N3K4xbVAHoiTQSwxkZjWDfKoNC27pLkYnt");
352/// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "sh(wpkh([c55b303f/49'/1'/0']tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L/0/*))#3tka9g0q");
353/// # Ok::<_, Box<dyn std::error::Error>>(())
354/// ```
355#[derive(Debug, Clone)]
356pub struct Bip49Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
357
358impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49Public<K> {
359 fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
360 P2Wpkh_P2Sh(segwit_v0::make_bipxx_public(
361 49, self.0, self.1, self.2, network,
362 )?)
363 .build(network)
364 }
365}
366
367/// BIP84 template. Expands to `wpkh(key/84'/{0,1}'/0'/{0,1}/*)`
368///
369/// Since there are hardened derivation steps, this template requires a private derivable key
370/// (generally a `xprv`/`tprv`).
371///
372/// See [`Bip84Public`] for a template that can work with a `xpub`/`tpub`.
373///
374/// ## Example
375///
376/// ```
377/// # use std::str::FromStr;
378/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
379/// # use bdk_wallet::{Wallet, KeychainKind};
380/// use bdk_wallet::template::Bip84;
381///
382/// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
383/// let mut wallet = Wallet::create(
384/// Bip84(key.clone(), KeychainKind::External),
385/// Bip84(key, KeychainKind::Internal),
386/// )
387/// .network(Network::Testnet)
388/// .create_wallet_no_persist()?;
389///
390/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "tb1qhl85z42h7r4su5u37rvvw0gk8j2t3n9y7zsg4n");
391/// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "wpkh([c55b303f/84'/1'/0']tpubDDc5mum24DekpNw92t6fHGp8Gr2JjF9J7i4TZBtN6Vp8xpAULG5CFaKsfugWa5imhrQQUZKXe261asP5koDHo5bs3qNTmf3U3o4v9SaB8gg/0/*)#6kfecsmr");
392/// # Ok::<_, Box<dyn std::error::Error>>(())
393/// ```
394#[derive(Debug, Clone)]
395pub struct Bip84<K: DerivableKey<Segwitv0>>(pub K, pub KeychainKind);
396
397impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84<K> {
398 fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
399 P2Wpkh(segwit_v0::make_bipxx_private(84, self.0, self.1, network)?).build(network)
400 }
401}
402
403/// BIP84 public template. Expands to `wpkh(key/{0,1}/*)`
404///
405/// This assumes that the key used has already been derived with `m/84'/0'/0'` for Mainnet or
406/// `m/84'/1'/0'` for Testnet.
407///
408/// This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
409///
410/// See [`Bip84`] for a template that does the full derivation, but requires private data
411/// for the key.
412///
413/// ## Example
414///
415/// ```
416/// # use std::str::FromStr;
417/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
418/// # use bdk_wallet::{Wallet, KeychainKind};
419/// use bdk_wallet::template::Bip84Public;
420///
421/// let key = bitcoin::bip32::Xpub::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
422/// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
423/// let mut wallet = Wallet::create(
424/// Bip84Public(key.clone(), fingerprint, KeychainKind::External),
425/// Bip84Public(key, fingerprint, KeychainKind::Internal),
426/// )
427/// .network(Network::Testnet)
428/// .create_wallet_no_persist()?;
429///
430/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "tb1qedg9fdlf8cnnqfd5mks6uz5w4kgpk2pr6y4qc7");
431/// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "wpkh([c55b303f/84'/1'/0']tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q/0/*)#dhu402yv");
432/// # Ok::<_, Box<dyn std::error::Error>>(())
433/// ```
434#[derive(Debug, Clone)]
435pub struct Bip84Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
436
437impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84Public<K> {
438 fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
439 P2Wpkh(segwit_v0::make_bipxx_public(
440 84, self.0, self.1, self.2, network,
441 )?)
442 .build(network)
443 }
444}
445
446/// BIP86 template. Expands to `tr(key/86'/{0,1}'/0'/{0,1}/*)`
447///
448/// Since there are hardened derivation steps, this template requires a private derivable key
449/// (generally a `xprv`/`tprv`).
450///
451/// See [`Bip86Public`] for a template that can work with a `xpub`/`tpub`.
452///
453/// ## Example
454///
455/// ```
456/// # use std::str::FromStr;
457/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
458/// # use bdk_wallet::{Wallet, KeychainKind};
459/// use bdk_wallet::template::Bip86;
460///
461/// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
462/// let mut wallet = Wallet::create(
463/// Bip86(key.clone(), KeychainKind::External),
464/// Bip86(key, KeychainKind::Internal),
465/// )
466/// .network(Network::Testnet)
467/// .create_wallet_no_persist()?;
468///
469/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "tb1p5unlj09djx8xsjwe97269kqtxqpwpu2epeskgqjfk4lnf69v4tnqpp35qu");
470/// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "tr([c55b303f/86'/1'/0']tpubDCiHofpEs47kx358bPdJmTZHmCDqQ8qw32upCSxHrSEdeeBs2T5Mq6QMB2ukeMqhNBiyhosBvJErteVhfURPGXPv3qLJPw5MVpHUewsbP2m/0/*)#dkgvr5hm");
471/// # Ok::<_, Box<dyn std::error::Error>>(())
472/// ```
473#[derive(Debug, Clone)]
474pub struct Bip86<K: DerivableKey<Tap>>(pub K, pub KeychainKind);
475
476impl<K: DerivableKey<Tap>> DescriptorTemplate for Bip86<K> {
477 fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
478 P2TR(segwit_v1::make_bipxx_private(86, self.0, self.1, network)?).build(network)
479 }
480}
481
482/// BIP86 public template. Expands to `tr(key/{0,1}/*)`
483///
484/// This assumes that the key used has already been derived with `m/86'/0'/0'` for Mainnet or
485/// `m/86'/1'/0'` for Testnet.
486///
487/// This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
488///
489/// See [`Bip86`] for a template that does the full derivation, but requires private data
490/// for the key.
491///
492/// ## Example
493///
494/// ```
495/// # use std::str::FromStr;
496/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
497/// # use bdk_wallet::{Wallet, KeychainKind};
498/// use bdk_wallet::template::Bip86Public;
499///
500/// let key = bitcoin::bip32::Xpub::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
501/// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
502/// let mut wallet = Wallet::create(
503/// Bip86Public(key.clone(), fingerprint, KeychainKind::External),
504/// Bip86Public(key, fingerprint, KeychainKind::Internal),
505/// )
506/// .network(Network::Testnet)
507/// .create_wallet_no_persist()?;
508///
509/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "tb1pwjp9f2k5n0xq73ecuu0c5njvgqr3vkh7yaylmpqvsuuaafymh0msvcmh37");
510/// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "tr([c55b303f/86'/1'/0']tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q/0/*)#2p65srku");
511/// # Ok::<_, Box<dyn std::error::Error>>(())
512/// ```
513#[derive(Debug, Clone)]
514pub struct Bip86Public<K: DerivableKey<Tap>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
515
516impl<K: DerivableKey<Tap>> DescriptorTemplate for Bip86Public<K> {
517 fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
518 P2TR(segwit_v1::make_bipxx_public(
519 86, self.0, self.1, self.2, network,
520 )?)
521 .build(network)
522 }
523}
524
525macro_rules! expand_make_bipxx {
526 ( $mod_name:ident, $ctx:ty ) => {
527 mod $mod_name {
528 use super::*;
529
530 pub(super) fn make_bipxx_private<K: DerivableKey<$ctx>>(
531 bip: u32,
532 key: K,
533 keychain: KeychainKind,
534 network: Network,
535 ) -> Result<impl IntoDescriptorKey<$ctx>, DescriptorError> {
536 let mut derivation_path = alloc::vec::Vec::with_capacity(4);
537 derivation_path.push(bip32::ChildNumber::from_hardened_idx(bip)?);
538
539 match network {
540 Network::Bitcoin => {
541 derivation_path.push(bip32::ChildNumber::from_hardened_idx(0)?);
542 }
543 _ => {
544 derivation_path.push(bip32::ChildNumber::from_hardened_idx(1)?);
545 }
546 }
547 derivation_path.push(bip32::ChildNumber::from_hardened_idx(0)?);
548
549 match keychain {
550 KeychainKind::External => {
551 derivation_path.push(bip32::ChildNumber::from_normal_idx(0)?)
552 }
553 KeychainKind::Internal => {
554 derivation_path.push(bip32::ChildNumber::from_normal_idx(1)?)
555 }
556 };
557
558 let derivation_path: bip32::DerivationPath = derivation_path.into();
559
560 Ok((key, derivation_path))
561 }
562 pub(super) fn make_bipxx_public<K: DerivableKey<$ctx>>(
563 bip: u32,
564 key: K,
565 parent_fingerprint: bip32::Fingerprint,
566 keychain: KeychainKind,
567 network: Network,
568 ) -> Result<impl IntoDescriptorKey<$ctx>, DescriptorError> {
569 let derivation_path: bip32::DerivationPath = match keychain {
570 KeychainKind::External => vec![bip32::ChildNumber::from_normal_idx(0)?].into(),
571 KeychainKind::Internal => vec![bip32::ChildNumber::from_normal_idx(1)?].into(),
572 };
573
574 let source_path = bip32::DerivationPath::from(vec![
575 bip32::ChildNumber::from_hardened_idx(bip)?,
576 match network {
577 Network::Bitcoin => bip32::ChildNumber::from_hardened_idx(0)?,
578 _ => bip32::ChildNumber::from_hardened_idx(1)?,
579 },
580 bip32::ChildNumber::from_hardened_idx(0)?,
581 ]);
582
583 Ok((key, (parent_fingerprint, source_path), derivation_path))
584 }
585 }
586 };
587}
588
589expand_make_bipxx!(legacy, Legacy);
590expand_make_bipxx!(segwit_v0, Segwitv0);
591expand_make_bipxx!(segwit_v1, Tap);
592
593#[cfg(test)]
594mod test {
595 // test existing descriptor templates, make sure they are expanded to the right descriptors
596
597 use alloc::{string::ToString, vec::Vec};
598 use core::str::FromStr;
599
600 use super::*;
601 use crate::descriptor::{DescriptorError, DescriptorMeta};
602 use crate::keys::ValidNetworks;
603 use assert_matches::assert_matches;
604 use miniscript::descriptor::{DescriptorPublicKey, KeyMap};
605 use miniscript::Descriptor;
606
607 // BIP44 `pkh(key/44'/{0,1}'/0'/{0,1}/*)`
608 #[test]
609 fn test_bip44_template_cointype() {
610 use bitcoin::bip32::ChildNumber::{self, Hardened};
611
612 let xprvkey = bitcoin::bip32::Xpriv::from_str("xprv9s21ZrQH143K2fpbqApQL69a4oKdGVnVN52R82Ft7d1pSqgKmajF62acJo3aMszZb6qQ22QsVECSFxvf9uyxFUvFYQMq3QbtwtRSMjLAhMf").unwrap();
613 assert!(xprvkey.network.is_mainnet());
614 let xdesc = Bip44(xprvkey, KeychainKind::Internal)
615 .build(Network::Bitcoin)
616 .unwrap();
617
618 if let ExtendedDescriptor::Pkh(pkh) = xdesc.0 {
619 let path: Vec<ChildNumber> = pkh.into_inner().full_derivation_path().unwrap().into();
620 let purpose = path.first().unwrap();
621 assert_matches!(purpose, Hardened { index: 44 });
622 let coin_type = path.get(1).unwrap();
623 assert_matches!(coin_type, Hardened { index: 0 });
624 }
625
626 let tprvkey = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
627 assert!(!tprvkey.network.is_mainnet());
628 let tdesc = Bip44(tprvkey, KeychainKind::Internal)
629 .build(Network::Testnet)
630 .unwrap();
631
632 if let ExtendedDescriptor::Pkh(pkh) = tdesc.0 {
633 let path: Vec<ChildNumber> = pkh.into_inner().full_derivation_path().unwrap().into();
634 let purpose = path.first().unwrap();
635 assert_matches!(purpose, Hardened { index: 44 });
636 let coin_type = path.get(1).unwrap();
637 assert_matches!(coin_type, Hardened { index: 1 });
638 }
639 }
640
641 // verify template descriptor generates expected address(es)
642 fn check(
643 desc: Result<(Descriptor<DescriptorPublicKey>, KeyMap, ValidNetworks), DescriptorError>,
644 is_witness: bool,
645 is_taproot: bool,
646 is_fixed: bool,
647 network: Network,
648 expected: &[&str],
649 ) {
650 let (desc, _key_map, _networks) = desc.unwrap();
651 assert_eq!(desc.is_witness(), is_witness);
652 assert_eq!(desc.is_taproot(), is_taproot);
653 assert_eq!(!desc.has_wildcard(), is_fixed);
654 for i in 0..expected.len() {
655 let index = i as u32;
656 let child_desc = if !desc.has_wildcard() {
657 desc.at_derivation_index(0).unwrap()
658 } else {
659 desc.at_derivation_index(index).unwrap()
660 };
661 let address = child_desc.address(network).unwrap();
662 assert_eq!(address.to_string(), *expected.get(i).unwrap());
663 }
664 }
665
666 // P2PKH
667 #[test]
668 fn test_p2ph_template() {
669 let prvkey =
670 bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")
671 .unwrap();
672 check(
673 P2Pkh(prvkey).build(Network::Bitcoin),
674 false,
675 false,
676 true,
677 Network::Regtest,
678 &["mwJ8hxFYW19JLuc65RCTaP4v1rzVU8cVMT"],
679 );
680
681 let pubkey = bitcoin::PublicKey::from_str(
682 "03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd",
683 )
684 .unwrap();
685 check(
686 P2Pkh(pubkey).build(Network::Bitcoin),
687 false,
688 false,
689 true,
690 Network::Regtest,
691 &["muZpTpBYhxmRFuCjLc7C6BBDF32C8XVJUi"],
692 );
693 }
694
695 // P2WPKH-P2SH `sh(wpkh(key))`
696 #[test]
697 fn test_p2wphp2sh_template() {
698 let prvkey =
699 bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")
700 .unwrap();
701 check(
702 P2Wpkh_P2Sh(prvkey).build(Network::Bitcoin),
703 true,
704 false,
705 true,
706 Network::Regtest,
707 &["2NB4ox5VDRw1ecUv6SnT3VQHPXveYztRqk5"],
708 );
709
710 let pubkey = bitcoin::PublicKey::from_str(
711 "03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd",
712 )
713 .unwrap();
714 check(
715 P2Wpkh_P2Sh(pubkey).build(Network::Bitcoin),
716 true,
717 false,
718 true,
719 Network::Regtest,
720 &["2N5LiC3CqzxDamRTPG1kiNv1FpNJQ7x28sb"],
721 );
722 }
723
724 // P2WPKH `wpkh(key)`
725 #[test]
726 fn test_p2wph_template() {
727 let prvkey =
728 bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")
729 .unwrap();
730 check(
731 P2Wpkh(prvkey).build(Network::Bitcoin),
732 true,
733 false,
734 true,
735 Network::Regtest,
736 &["bcrt1q4525hmgw265tl3drrl8jjta7ayffu6jfcwxx9y"],
737 );
738
739 let pubkey = bitcoin::PublicKey::from_str(
740 "03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd",
741 )
742 .unwrap();
743 check(
744 P2Wpkh(pubkey).build(Network::Bitcoin),
745 true,
746 false,
747 true,
748 Network::Regtest,
749 &["bcrt1qngw83fg8dz0k749cg7k3emc7v98wy0c7azaa6h"],
750 );
751 }
752
753 // P2TR `tr(key)`
754 #[test]
755 fn test_p2tr_template() {
756 let prvkey =
757 bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")
758 .unwrap();
759 check(
760 P2TR(prvkey).build(Network::Bitcoin),
761 false,
762 true,
763 true,
764 Network::Regtest,
765 &["bcrt1pvjf9t34fznr53u5tqhejz4nr69luzkhlvsdsdfq9pglutrpve2xqnwtkqq"],
766 );
767
768 let pubkey = bitcoin::PublicKey::from_str(
769 "03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd",
770 )
771 .unwrap();
772 check(
773 P2TR(pubkey).build(Network::Bitcoin),
774 false,
775 true,
776 true,
777 Network::Regtest,
778 &["bcrt1pw74tdcrxlzn5r8z6ku2vztr86fgq0m245s72mjktf4afwzsf8ugs4evwdf"],
779 );
780 }
781
782 // BIP44 `pkh(key/44'/0'/0'/{0,1}/*)`
783 #[test]
784 fn test_bip44_template() {
785 let prvkey = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
786 check(
787 Bip44(prvkey, KeychainKind::External).build(Network::Bitcoin),
788 false,
789 false,
790 false,
791 Network::Regtest,
792 &[
793 "n453VtnjDHPyDt2fDstKSu7A3YCJoHZ5g5",
794 "mvfrrumXgTtwFPWDNUecBBgzuMXhYM7KRP",
795 "mzYvhRAuQqbdSKMVVzXNYyqihgNdRadAUQ",
796 ],
797 );
798 check(
799 Bip44(prvkey, KeychainKind::Internal).build(Network::Bitcoin),
800 false,
801 false,
802 false,
803 Network::Regtest,
804 &[
805 "muHF98X9KxEzdKrnFAX85KeHv96eXopaip",
806 "n4hpyLJE5ub6B5Bymv4eqFxS5KjrewSmYR",
807 "mgvkdv1ffmsXd2B1sRKQ5dByK3SzpG42rA",
808 ],
809 );
810 }
811
812 // BIP44 public `pkh(key/{0,1}/*)`
813 #[test]
814 fn test_bip44_public_template() {
815 let pubkey = bitcoin::bip32::Xpub::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU").unwrap();
816 let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f").unwrap();
817 check(
818 Bip44Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
819 false,
820 false,
821 false,
822 Network::Regtest,
823 &[
824 "miNG7dJTzJqNbFS19svRdTCisC65dsubtR",
825 "n2UqaDbCjWSFJvpC84m3FjUk5UaeibCzYg",
826 "muCPpS6Ue7nkzeJMWDViw7Lkwr92Yc4K8g",
827 ],
828 );
829 check(
830 Bip44Public(pubkey, fingerprint, KeychainKind::Internal).build(Network::Bitcoin),
831 false,
832 false,
833 false,
834 Network::Regtest,
835 &[
836 "moDr3vJ8wpt5nNxSK55MPq797nXJb2Ru9H",
837 "ms7A1Yt4uTezT2XkefW12AvLoko8WfNJMG",
838 "mhYiyat2rtEnV77cFfQsW32y1m2ceCGHPo",
839 ],
840 );
841 }
842
843 // BIP49 `sh(wpkh(key/49'/0'/0'/{0,1}/*))`
844 #[test]
845 fn test_bip49_template() {
846 let prvkey = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
847 check(
848 Bip49(prvkey, KeychainKind::External).build(Network::Bitcoin),
849 true,
850 false,
851 false,
852 Network::Regtest,
853 &[
854 "2N9bCAJXGm168MjVwpkBdNt6ucka3PKVoUV",
855 "2NDckYkqrYyDMtttEav5hB3Bfw9EGAW5HtS",
856 "2NAFTVtksF9T4a97M7nyCjwUBD24QevZ5Z4",
857 ],
858 );
859 check(
860 Bip49(prvkey, KeychainKind::Internal).build(Network::Bitcoin),
861 true,
862 false,
863 false,
864 Network::Regtest,
865 &[
866 "2NB3pA8PnzJLGV8YEKNDFpbViZv3Bm1K6CG",
867 "2NBiX2Wzxngb5rPiWpUiJQ2uLVB4HBjFD4p",
868 "2NA8ek4CdQ6aMkveYF6AYuEYNrftB47QGTn",
869 ],
870 );
871 }
872
873 // BIP49 public `sh(wpkh(key/{0,1}/*))`
874 #[test]
875 fn test_bip49_public_template() {
876 let pubkey = bitcoin::bip32::Xpub::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L").unwrap();
877 let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f").unwrap();
878 check(
879 Bip49Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
880 true,
881 false,
882 false,
883 Network::Regtest,
884 &[
885 "2N3K4xbVAHoiTQSwxkZjWDfKoNC27pLkYnt",
886 "2NCTQfJ1sZa3wQ3pPseYRHbaNEpC3AquEfX",
887 "2MveFxAuC8BYPzTybx7FxSzW8HSd8ATT4z7",
888 ],
889 );
890 check(
891 Bip49Public(pubkey, fingerprint, KeychainKind::Internal).build(Network::Bitcoin),
892 true,
893 false,
894 false,
895 Network::Regtest,
896 &[
897 "2NF2vttKibwyxigxtx95Zw8K7JhDbo5zPVJ",
898 "2Mtmyd8taksxNVWCJ4wVvaiss7QPZGcAJuH",
899 "2NBs3CTVYPr1HCzjB4YFsnWCPCtNg8uMEfp",
900 ],
901 );
902 }
903
904 // BIP84 `wpkh(key/84'/0'/0'/{0,1}/*)`
905 #[test]
906 fn test_bip84_template() {
907 let prvkey = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
908 check(
909 Bip84(prvkey, KeychainKind::External).build(Network::Bitcoin),
910 true,
911 false,
912 false,
913 Network::Regtest,
914 &[
915 "bcrt1qkmvk2nadgplmd57ztld8nf8v2yxkzmdvwtjf8s",
916 "bcrt1qx0v6zgfwe50m4kqc58cqzcyem7ay2sfl3gvqhp",
917 "bcrt1q4h7fq9zhxst6e69p3n882nfj649l7w9g3zccfp",
918 ],
919 );
920 check(
921 Bip84(prvkey, KeychainKind::Internal).build(Network::Bitcoin),
922 true,
923 false,
924 false,
925 Network::Regtest,
926 &[
927 "bcrt1qtrwtz00wxl69e5xex7amy4xzlxkaefg3gfdkxa",
928 "bcrt1qqqasfhxpkkf7zrxqnkr2sfhn74dgsrc3e3ky45",
929 "bcrt1qpks7n0gq74hsgsz3phn5vuazjjq0f5eqhsgyce",
930 ],
931 );
932 }
933
934 // BIP84 public `wpkh(key/{0,1}/*)`
935 #[test]
936 fn test_bip84_public_template() {
937 let pubkey = bitcoin::bip32::Xpub::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q").unwrap();
938 let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f").unwrap();
939 check(
940 Bip84Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
941 true,
942 false,
943 false,
944 Network::Regtest,
945 &[
946 "bcrt1qedg9fdlf8cnnqfd5mks6uz5w4kgpk2prcdvd0h",
947 "bcrt1q3lncdlwq3lgcaaeyruynjnlccr0ve0kakh6ana",
948 "bcrt1qt9800y6xl3922jy3uyl0z33jh5wfpycyhcylr9",
949 ],
950 );
951 check(
952 Bip84Public(pubkey, fingerprint, KeychainKind::Internal).build(Network::Bitcoin),
953 true,
954 false,
955 false,
956 Network::Regtest,
957 &[
958 "bcrt1qm6wqukenh7guu792lj2njgw9n78cmwsy8xy3z2",
959 "bcrt1q694twxtjn4nnrvnyvra769j0a23rllj5c6cgwp",
960 "bcrt1qhlac3c5ranv5w5emlnqs7wxhkxt8maelylcarp",
961 ],
962 );
963 }
964
965 // BIP86 `tr(key/86'/0'/0'/{0,1}/*)`
966 // Used addresses in test vector in https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki
967 #[test]
968 fn test_bip86_template() {
969 let prvkey = bitcoin::bip32::Xpriv::from_str("xprv9s21ZrQH143K3GJpoapnV8SFfukcVBSfeCficPSGfubmSFDxo1kuHnLisriDvSnRRuL2Qrg5ggqHKNVpxR86QEC8w35uxmGoggxtQTPvfUu").unwrap();
970 check(
971 Bip86(prvkey, KeychainKind::External).build(Network::Bitcoin),
972 false,
973 true,
974 false,
975 Network::Bitcoin,
976 &[
977 "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
978 "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
979 "bc1p0d0rhyynq0awa9m8cqrcr8f5nxqx3aw29w4ru5u9my3h0sfygnzs9khxz8",
980 ],
981 );
982 check(
983 Bip86(prvkey, KeychainKind::Internal).build(Network::Bitcoin),
984 false,
985 true,
986 false,
987 Network::Bitcoin,
988 &[
989 "bc1p3qkhfews2uk44qtvauqyr2ttdsw7svhkl9nkm9s9c3x4ax5h60wqwruhk7",
990 "bc1ptdg60grjk9t3qqcqczp4tlyy3z47yrx9nhlrjsmw36q5a72lhdrs9f00nj",
991 "bc1pgcwgsu8naxp7xlp5p7ufzs7emtfza2las7r2e7krzjhe5qj5xz2q88kmk5",
992 ],
993 );
994 }
995
996 // BIP86 public `tr(key/{0,1}/*)`
997 // Used addresses in test vector in https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki
998 #[test]
999 fn test_bip86_public_template() {
1000 let pubkey = bitcoin::bip32::Xpub::from_str("xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ").unwrap();
1001 let fingerprint = bitcoin::bip32::Fingerprint::from_str("73c5da0a").unwrap();
1002 check(
1003 Bip86Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
1004 false,
1005 true,
1006 false,
1007 Network::Bitcoin,
1008 &[
1009 "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
1010 "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
1011 "bc1p0d0rhyynq0awa9m8cqrcr8f5nxqx3aw29w4ru5u9my3h0sfygnzs9khxz8",
1012 ],
1013 );
1014 check(
1015 Bip86Public(pubkey, fingerprint, KeychainKind::Internal).build(Network::Bitcoin),
1016 false,
1017 true,
1018 false,
1019 Network::Bitcoin,
1020 &[
1021 "bc1p3qkhfews2uk44qtvauqyr2ttdsw7svhkl9nkm9s9c3x4ax5h60wqwruhk7",
1022 "bc1ptdg60grjk9t3qqcqczp4tlyy3z47yrx9nhlrjsmw36q5a72lhdrs9f00nj",
1023 "bc1pgcwgsu8naxp7xlp5p7ufzs7emtfza2las7r2e7krzjhe5qj5xz2q88kmk5",
1024 ],
1025 );
1026 }
1027}