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
41type 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#[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 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 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 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 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 pub fn network(mut self, network: Network) -> Self {
154 self.network = network;
155 self
156 }
157
158 pub fn genesis_hash(mut self, genesis_hash: BlockHash) -> Self {
160 self.genesis_hash = Some(genesis_hash);
161 self
162 }
163
164 pub fn lookahead(mut self, lookahead: u32) -> Self {
171 self.lookahead = lookahead;
172 self
173 }
174
175 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 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 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 pub fn create_wallet_no_persist(self) -> Result<Wallet, DescriptorError> {
208 Wallet::create_with_params(self)
209 }
210}
211
212#[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 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 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 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 pub fn check_network(mut self, network: Network) -> Self {
274 self.check_network = Some(network);
275 self
276 }
277
278 pub fn check_genesis_hash(mut self, genesis_hash: BlockHash) -> Self {
280 self.check_genesis_hash = Some(genesis_hash);
281 self
282 }
283
284 pub fn lookahead(mut self, lookahead: u32) -> Self {
291 self.lookahead = lookahead;
292 self
293 }
294
295 pub fn extract_keys(mut self) -> Self {
298 self.extract_keys = true;
299 self
300 }
301
302 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 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 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 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}