1use core::{
2 fmt,
3 future::Future,
4 marker::PhantomData,
5 ops::{Deref, DerefMut},
6 pin::Pin,
7};
8
9use alloc::{boxed::Box, string::ToString};
10use chain::Merge;
11
12use crate::{
13 descriptor::{calc_checksum, DescriptorError},
14 ChangeSet, CreateParams, LoadParams, Wallet,
15};
16
17pub trait WalletPersister {
26 type Error;
28
29 fn initialize(persister: &mut Self) -> Result<ChangeSet, Self::Error>;
49
50 fn persist(persister: &mut Self, changeset: &ChangeSet) -> Result<(), Self::Error>;
56}
57
58type FutureResult<'a, T, E> = Pin<Box<dyn Future<Output = Result<T, E>> + Send + 'a>>;
59
60pub trait AsyncWalletPersister {
70 type Error;
72
73 fn initialize<'a>(persister: &'a mut Self) -> FutureResult<'a, ChangeSet, Self::Error>
93 where
94 Self: 'a;
95
96 fn persist<'a>(
102 persister: &'a mut Self,
103 changeset: &'a ChangeSet,
104 ) -> FutureResult<'a, (), Self::Error>
105 where
106 Self: 'a;
107}
108
109#[derive(Debug)]
125pub struct PersistedWallet<P> {
126 inner: Wallet,
127 _marker: PhantomData<fn(&mut P)>,
128}
129
130impl<P> Deref for PersistedWallet<P> {
131 type Target = Wallet;
132
133 fn deref(&self) -> &Self::Target {
134 &self.inner
135 }
136}
137
138impl<P> DerefMut for PersistedWallet<P> {
139 fn deref_mut(&mut self) -> &mut Self::Target {
140 &mut self.inner
141 }
142}
143
144impl<P: WalletPersister> PersistedWallet<P> {
146 pub fn create(
148 persister: &mut P,
149 params: CreateParams,
150 ) -> Result<Self, CreateWithPersistError<P::Error>> {
151 let existing = P::initialize(persister).map_err(CreateWithPersistError::Persist)?;
152 if !existing.is_empty() {
153 return Err(CreateWithPersistError::DataAlreadyExists(existing));
154 }
155 let mut inner =
156 Wallet::create_with_params(params).map_err(CreateWithPersistError::Descriptor)?;
157 if let Some(changeset) = inner.take_staged() {
158 P::persist(persister, &changeset).map_err(CreateWithPersistError::Persist)?;
159 }
160 Ok(Self {
161 inner,
162 _marker: PhantomData,
163 })
164 }
165
166 pub fn load(
168 persister: &mut P,
169 params: LoadParams,
170 ) -> Result<Option<Self>, LoadWithPersistError<P::Error>> {
171 let changeset = P::initialize(persister).map_err(LoadWithPersistError::Persist)?;
172 Wallet::load_with_params(changeset, params)
173 .map(|opt| {
174 opt.map(|inner| PersistedWallet {
175 inner,
176 _marker: PhantomData,
177 })
178 })
179 .map_err(LoadWithPersistError::InvalidChangeSet)
180 }
181
182 pub fn persist(&mut self, persister: &mut P) -> Result<bool, P::Error> {
188 match self.inner.staged_mut() {
189 Some(stage) => {
190 P::persist(persister, &*stage)?;
191 let _ = stage.take();
192 Ok(true)
193 }
194 None => Ok(false),
195 }
196 }
197}
198
199impl<P: AsyncWalletPersister> PersistedWallet<P> {
201 pub async fn create_async(
203 persister: &mut P,
204 params: CreateParams,
205 ) -> Result<Self, CreateWithPersistError<P::Error>> {
206 let existing = P::initialize(persister)
207 .await
208 .map_err(CreateWithPersistError::Persist)?;
209 if !existing.is_empty() {
210 return Err(CreateWithPersistError::DataAlreadyExists(existing));
211 }
212 let mut inner =
213 Wallet::create_with_params(params).map_err(CreateWithPersistError::Descriptor)?;
214 if let Some(changeset) = inner.take_staged() {
215 P::persist(persister, &changeset)
216 .await
217 .map_err(CreateWithPersistError::Persist)?;
218 }
219 Ok(Self {
220 inner,
221 _marker: PhantomData,
222 })
223 }
224
225 pub async fn load_async(
227 persister: &mut P,
228 params: LoadParams,
229 ) -> Result<Option<Self>, LoadWithPersistError<P::Error>> {
230 let changeset = P::initialize(persister)
231 .await
232 .map_err(LoadWithPersistError::Persist)?;
233 Wallet::load_with_params(changeset, params)
234 .map(|opt| {
235 opt.map(|inner| PersistedWallet {
236 inner,
237 _marker: PhantomData,
238 })
239 })
240 .map_err(LoadWithPersistError::InvalidChangeSet)
241 }
242
243 pub async fn persist_async(&mut self, persister: &mut P) -> Result<bool, P::Error> {
249 match self.inner.staged_mut() {
250 Some(stage) => {
251 P::persist(persister, &*stage).await?;
252 let _ = stage.take();
253 Ok(true)
254 }
255 None => Ok(false),
256 }
257 }
258}
259
260#[cfg(feature = "rusqlite")]
261impl WalletPersister for bdk_chain::rusqlite::Transaction<'_> {
262 type Error = bdk_chain::rusqlite::Error;
263
264 fn initialize(persister: &mut Self) -> Result<ChangeSet, Self::Error> {
265 ChangeSet::init_sqlite_tables(&*persister)?;
266 ChangeSet::from_sqlite(persister)
267 }
268
269 fn persist(persister: &mut Self, changeset: &ChangeSet) -> Result<(), Self::Error> {
270 changeset.persist_to_sqlite(persister)
271 }
272}
273
274#[cfg(feature = "rusqlite")]
275impl WalletPersister for bdk_chain::rusqlite::Connection {
276 type Error = bdk_chain::rusqlite::Error;
277
278 fn initialize(persister: &mut Self) -> Result<ChangeSet, Self::Error> {
279 let db_tx = persister.transaction()?;
280 ChangeSet::init_sqlite_tables(&db_tx)?;
281 let changeset = ChangeSet::from_sqlite(&db_tx)?;
282 db_tx.commit()?;
283 Ok(changeset)
284 }
285
286 fn persist(persister: &mut Self, changeset: &ChangeSet) -> Result<(), Self::Error> {
287 let db_tx = persister.transaction()?;
288 changeset.persist_to_sqlite(&db_tx)?;
289 db_tx.commit()
290 }
291}
292
293#[cfg(feature = "file_store")]
295#[derive(Debug)]
296pub enum FileStoreError {
297 Load(bdk_file_store::StoreErrorWithDump<ChangeSet>),
299 Write(std::io::Error),
301}
302
303#[cfg(feature = "file_store")]
304impl core::fmt::Display for FileStoreError {
305 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
306 use core::fmt::Display;
307 match self {
308 FileStoreError::Load(e) => Display::fmt(e, f),
309 FileStoreError::Write(e) => Display::fmt(e, f),
310 }
311 }
312}
313
314#[cfg(feature = "file_store")]
315impl std::error::Error for FileStoreError {}
316
317#[cfg(feature = "file_store")]
318impl WalletPersister for bdk_file_store::Store<ChangeSet> {
319 type Error = FileStoreError;
320
321 fn initialize(persister: &mut Self) -> Result<ChangeSet, Self::Error> {
322 persister
323 .dump()
324 .map(Option::unwrap_or_default)
325 .map_err(FileStoreError::Load)
326 }
327
328 fn persist(persister: &mut Self, changeset: &ChangeSet) -> Result<(), Self::Error> {
329 persister.append(changeset).map_err(FileStoreError::Write)
330 }
331}
332
333#[derive(Debug, PartialEq)]
335pub enum LoadWithPersistError<E> {
336 Persist(E),
338 InvalidChangeSet(crate::LoadError),
340}
341
342impl<E: fmt::Display> fmt::Display for LoadWithPersistError<E> {
343 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344 match self {
345 Self::Persist(err) => fmt::Display::fmt(err, f),
346 Self::InvalidChangeSet(err) => fmt::Display::fmt(&err, f),
347 }
348 }
349}
350
351#[cfg(feature = "std")]
352impl<E: fmt::Debug + fmt::Display> std::error::Error for LoadWithPersistError<E> {}
353
354#[derive(Debug)]
356pub enum CreateWithPersistError<E> {
357 Persist(E),
359 DataAlreadyExists(ChangeSet),
361 Descriptor(DescriptorError),
363}
364
365impl<E: fmt::Display> fmt::Display for CreateWithPersistError<E> {
366 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
367 match self {
368 Self::Persist(err) => write!(f, "{err}"),
369 Self::DataAlreadyExists(changeset) => {
370 write!(
371 f,
372 "Cannot create wallet in a persister which already contains data: "
373 )?;
374 changeset_info(f, changeset)
375 }
376 Self::Descriptor(err) => {
377 write!(f, "{err}")
378 }
379 }
380 }
381}
382
383#[cfg(feature = "std")]
384impl<E: fmt::Debug + fmt::Display> std::error::Error for CreateWithPersistError<E> {}
385
386fn changeset_info(f: &mut fmt::Formatter<'_>, changeset: &ChangeSet) -> fmt::Result {
388 let network = changeset
389 .network
390 .as_ref()
391 .map_or("None".to_string(), |n| n.to_string());
392
393 let descriptor_checksum = changeset
394 .descriptor
395 .as_ref()
396 .and_then(|d| calc_checksum(&d.to_string()).ok())
397 .unwrap_or_else(|| "None".to_string());
398
399 let change_descriptor_checksum = changeset
400 .change_descriptor
401 .as_ref()
402 .and_then(|d| calc_checksum(&d.to_string()).ok())
403 .unwrap_or_else(|| "None".to_string());
404
405 let tx_count = changeset.tx_graph.txs.len();
406
407 let anchor_count = changeset.tx_graph.anchors.len();
408
409 let block_count = if let Some(&count) = changeset.local_chain.blocks.keys().last() {
410 count
411 } else {
412 0
413 };
414
415 writeln!(f, " Network: {network}")?;
416 writeln!(f, " Descriptor Checksum: {descriptor_checksum}")?;
417 writeln!(
418 f,
419 " Change Descriptor Checksum: {change_descriptor_checksum}"
420 )?;
421 writeln!(f, " Transaction Count: {tx_count}")?;
422 writeln!(f, " Anchor Count: {anchor_count}")?;
423 writeln!(f, " Block Count: {block_count}")?;
424
425 Ok(())
426}