bdk_wallet/wallet/
params.rs

1use alloc::boxed::Box;
2use bdk_chain::keychain_txout::DEFAULT_LOOKAHEAD;
3use bitcoin::{BlockHash, Network};
4use miniscript::descriptor::KeyMap;
5
6use crate::{
7    descriptor::{DescriptorError, ExtendedDescriptor, IntoWalletDescriptor},
8    utils::SecpCtx,
9    AsyncWalletPersister, CreateWithPersistError, KeychainKind, LoadWithPersistError, Wallet,
10    WalletPersister,
11};
12
13use super::{ChangeSet, LoadError, PersistedWallet};
14
15fn make_two_path_descriptor_to_extract<D>(
16    two_path_descriptor: D,
17    index: usize,
18) -> DescriptorToExtract
19where
20    D: IntoWalletDescriptor + Send + 'static,
21{
22    Box::new(move |secp, network| {
23        let (desc, keymap) = two_path_descriptor.into_wallet_descriptor(secp, network)?;
24
25        if !desc.is_multipath() {
26            return Err(DescriptorError::MultiPath);
27        }
28
29        let descriptors = desc
30            .into_single_descriptors()
31            .map_err(DescriptorError::Miniscript)?;
32
33        if descriptors.len() != 2 {
34            return Err(DescriptorError::MultiPath);
35        }
36
37        Ok((descriptors[index].clone(), keymap))
38    })
39}
40
41/// This atrocity is to avoid having type parameters on [`CreateParams`] and [`LoadParams`].
42///
43/// The better option would be to do `Box<dyn IntoWalletDescriptor>`, but we cannot due to Rust's
44/// [object safety rules](https://doc.rust-lang.org/reference/items/traits.html#object-safety).
45type DescriptorToExtract = Box<
46    dyn FnOnce(&SecpCtx, Network) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError>
47        + Send
48        + 'static,
49>;
50
51fn make_descriptor_to_extract<D>(descriptor: D) -> DescriptorToExtract
52where
53    D: IntoWalletDescriptor + Send + 'static,
54{
55    Box::new(|secp, network| descriptor.into_wallet_descriptor(secp, network))
56}
57
58/// Parameters for [`Wallet::create`] or [`PersistedWallet::create`].
59#[must_use]
60pub struct CreateParams {
61    pub(crate) descriptor: DescriptorToExtract,
62    pub(crate) descriptor_keymap: KeyMap,
63    pub(crate) change_descriptor: Option<DescriptorToExtract>,
64    pub(crate) change_descriptor_keymap: KeyMap,
65    pub(crate) network: Network,
66    pub(crate) genesis_hash: Option<BlockHash>,
67    pub(crate) lookahead: u32,
68    pub(crate) use_spk_cache: bool,
69}
70
71impl CreateParams {
72    /// Construct parameters with provided `descriptor`.
73    ///
74    /// Default values:
75    /// * `change_descriptor` = `None`
76    /// * `network` = [`Network::Bitcoin`]
77    /// * `genesis_hash` = `None`
78    /// * `lookahead` = [`DEFAULT_LOOKAHEAD`]
79    ///
80    /// Use this method only when building a wallet with a single descriptor. See
81    /// also [`Wallet::create_single`].
82    pub fn new_single<D: IntoWalletDescriptor + Send + 'static>(descriptor: D) -> Self {
83        Self {
84            descriptor: make_descriptor_to_extract(descriptor),
85            descriptor_keymap: KeyMap::default(),
86            change_descriptor: None,
87            change_descriptor_keymap: KeyMap::default(),
88            network: Network::Bitcoin,
89            genesis_hash: None,
90            lookahead: DEFAULT_LOOKAHEAD,
91            use_spk_cache: false,
92        }
93    }
94
95    /// Construct parameters with provided `descriptor` and `change_descriptor`.
96    ///
97    /// Default values:
98    /// * `network` = [`Network::Bitcoin`]
99    /// * `genesis_hash` = `None`
100    /// * `lookahead` = [`DEFAULT_LOOKAHEAD`]
101    pub fn new<D: IntoWalletDescriptor + Send + 'static>(
102        descriptor: D,
103        change_descriptor: D,
104    ) -> Self {
105        Self {
106            descriptor: make_descriptor_to_extract(descriptor),
107            descriptor_keymap: KeyMap::default(),
108            change_descriptor: Some(make_descriptor_to_extract(change_descriptor)),
109            change_descriptor_keymap: KeyMap::default(),
110            network: Network::Bitcoin,
111            genesis_hash: None,
112            lookahead: DEFAULT_LOOKAHEAD,
113            use_spk_cache: false,
114        }
115    }
116
117    /// Construct parameters with a two-path descriptor that will be parsed into receive and change
118    /// descriptors.
119    ///
120    /// This function parses a two-path descriptor (receive and change) and creates parameters
121    /// using the existing receive and change wallet creation logic.
122    ///
123    /// Default values:
124    /// * `network` = [`Network::Bitcoin`]
125    /// * `genesis_hash` = `None`
126    /// * `lookahead` = [`DEFAULT_LOOKAHEAD`]
127    pub fn new_two_path<D: IntoWalletDescriptor + Send + Clone + 'static>(
128        two_path_descriptor: D,
129    ) -> Self {
130        Self {
131            descriptor: make_two_path_descriptor_to_extract(two_path_descriptor.clone(), 0),
132            descriptor_keymap: KeyMap::default(),
133            change_descriptor: Some(make_two_path_descriptor_to_extract(two_path_descriptor, 1)),
134            change_descriptor_keymap: KeyMap::default(),
135            network: Network::Bitcoin,
136            genesis_hash: None,
137            lookahead: DEFAULT_LOOKAHEAD,
138            use_spk_cache: false,
139        }
140    }
141
142    /// Extend the given `keychain`'s `keymap`.
143    pub fn keymap(mut self, keychain: KeychainKind, keymap: KeyMap) -> Self {
144        match keychain {
145            KeychainKind::External => &mut self.descriptor_keymap,
146            KeychainKind::Internal => &mut self.change_descriptor_keymap,
147        }
148        .extend(keymap);
149        self
150    }
151
152    /// Set `network`.
153    pub fn network(mut self, network: Network) -> Self {
154        self.network = network;
155        self
156    }
157
158    /// Use a custom `genesis_hash`.
159    pub fn genesis_hash(mut self, genesis_hash: BlockHash) -> Self {
160        self.genesis_hash = Some(genesis_hash);
161        self
162    }
163
164    /// Use a custom `lookahead` value.
165    ///
166    /// The `lookahead` defines a number of script pubkeys to derive over and above the last
167    /// revealed index. Without a lookahead the indexer will miss outputs you own when processing
168    /// transactions whose output script pubkeys lie beyond the last revealed index. In most cases
169    /// the default value [`DEFAULT_LOOKAHEAD`] is sufficient.
170    pub fn lookahead(mut self, lookahead: u32) -> Self {
171        self.lookahead = lookahead;
172        self
173    }
174
175    /// Use a persistent cache of indexed script pubkeys (SPKs).
176    ///
177    /// **Note:** To persist across restarts, this option must also be set at load time with
178    /// [`LoadParams`](LoadParams::use_spk_cache).
179    pub fn use_spk_cache(mut self, use_spk_cache: bool) -> Self {
180        self.use_spk_cache = use_spk_cache;
181        self
182    }
183
184    /// Create [`PersistedWallet`] with the given [`WalletPersister`].
185    pub fn create_wallet<P>(
186        self,
187        persister: &mut P,
188    ) -> Result<PersistedWallet<P>, CreateWithPersistError<P::Error>>
189    where
190        P: WalletPersister,
191    {
192        PersistedWallet::create(persister, self)
193    }
194
195    /// Create [`PersistedWallet`] with the given [`AsyncWalletPersister`].
196    pub async fn create_wallet_async<P>(
197        self,
198        persister: &mut P,
199    ) -> Result<PersistedWallet<P>, CreateWithPersistError<P::Error>>
200    where
201        P: AsyncWalletPersister,
202    {
203        PersistedWallet::create_async(persister, self).await
204    }
205
206    /// Create [`Wallet`] without persistence.
207    pub fn create_wallet_no_persist(self) -> Result<Wallet, DescriptorError> {
208        Wallet::create_with_params(self)
209    }
210}
211
212/// Parameters for [`Wallet::load`] or [`PersistedWallet::load`].
213#[must_use]
214pub struct LoadParams {
215    pub(crate) descriptor_keymap: KeyMap,
216    pub(crate) change_descriptor_keymap: KeyMap,
217    pub(crate) lookahead: u32,
218    pub(crate) check_network: Option<Network>,
219    pub(crate) check_genesis_hash: Option<BlockHash>,
220    pub(crate) check_descriptor: Option<Option<DescriptorToExtract>>,
221    pub(crate) check_change_descriptor: Option<Option<DescriptorToExtract>>,
222    pub(crate) extract_keys: bool,
223    pub(crate) use_spk_cache: bool,
224}
225
226impl LoadParams {
227    /// Construct parameters with default values.
228    ///
229    /// Default values: `lookahead` = [`DEFAULT_LOOKAHEAD`]
230    pub fn new() -> Self {
231        Self {
232            descriptor_keymap: KeyMap::default(),
233            change_descriptor_keymap: KeyMap::default(),
234            lookahead: DEFAULT_LOOKAHEAD,
235            check_network: None,
236            check_genesis_hash: None,
237            check_descriptor: None,
238            check_change_descriptor: None,
239            extract_keys: false,
240            use_spk_cache: false,
241        }
242    }
243
244    /// Extend the given `keychain`'s `keymap`.
245    pub fn keymap(mut self, keychain: KeychainKind, keymap: KeyMap) -> Self {
246        match keychain {
247            KeychainKind::External => &mut self.descriptor_keymap,
248            KeychainKind::Internal => &mut self.change_descriptor_keymap,
249        }
250        .extend(keymap);
251        self
252    }
253
254    /// Checks the `expected_descriptor` matches exactly what is loaded for `keychain`.
255    ///
256    /// # Note
257    ///
258    /// You must also specify [`extract_keys`](Self::extract_keys) if you wish to add a signer
259    /// for an expected descriptor containing secrets.
260    pub fn descriptor<D>(mut self, keychain: KeychainKind, expected_descriptor: Option<D>) -> Self
261    where
262        D: IntoWalletDescriptor + Send + 'static,
263    {
264        let expected = expected_descriptor.map(|d| make_descriptor_to_extract(d));
265        match keychain {
266            KeychainKind::External => self.check_descriptor = Some(expected),
267            KeychainKind::Internal => self.check_change_descriptor = Some(expected),
268        }
269        self
270    }
271
272    /// Checks that the given network matches the one loaded from persistence.
273    pub fn check_network(mut self, network: Network) -> Self {
274        self.check_network = Some(network);
275        self
276    }
277
278    /// Checks that the given `genesis_hash` matches the one loaded from persistence.
279    pub fn check_genesis_hash(mut self, genesis_hash: BlockHash) -> Self {
280        self.check_genesis_hash = Some(genesis_hash);
281        self
282    }
283
284    /// Use a custom `lookahead` value.
285    ///
286    /// The `lookahead` defines a number of script pubkeys to derive over and above the last
287    /// revealed index. Without a lookahead the indexer will miss outputs you own when processing
288    /// transactions whose output script pubkeys lie beyond the last revealed index. In most cases
289    /// the default value [`DEFAULT_LOOKAHEAD`] is sufficient.
290    pub fn lookahead(mut self, lookahead: u32) -> Self {
291        self.lookahead = lookahead;
292        self
293    }
294
295    /// Whether to try extracting private keys from the *provided descriptors* upon loading.
296    /// See also [`LoadParams::descriptor`].
297    pub fn extract_keys(mut self) -> Self {
298        self.extract_keys = true;
299        self
300    }
301
302    /// Use a persistent cache of indexed script pubkeys (SPKs).
303    ///
304    /// **Note:** This should only be used if you have previously persisted a cache of script
305    /// pubkeys using [`CreateParams::use_spk_cache`].
306    pub fn use_spk_cache(mut self, use_spk_cache: bool) -> Self {
307        self.use_spk_cache = use_spk_cache;
308        self
309    }
310
311    /// Load [`PersistedWallet`] with the given [`WalletPersister`].
312    pub fn load_wallet<P>(
313        self,
314        persister: &mut P,
315    ) -> Result<Option<PersistedWallet<P>>, LoadWithPersistError<P::Error>>
316    where
317        P: WalletPersister,
318    {
319        PersistedWallet::load(persister, self)
320    }
321
322    /// Load [`PersistedWallet`] with the given [`AsyncWalletPersister`].
323    pub async fn load_wallet_async<P>(
324        self,
325        persister: &mut P,
326    ) -> Result<Option<PersistedWallet<P>>, LoadWithPersistError<P::Error>>
327    where
328        P: AsyncWalletPersister,
329    {
330        PersistedWallet::load_async(persister, self).await
331    }
332
333    /// Load [`Wallet`] without persistence.
334    pub fn load_wallet_no_persist(self, changeset: ChangeSet) -> Result<Option<Wallet>, LoadError> {
335        Wallet::load_with_params(changeset, self)
336    }
337}
338
339impl Default for LoadParams {
340    fn default() -> Self {
341        Self::new()
342    }
343}