@@ -10,48 +10,103 @@ use chain::Merge;
10
10
11
11
use crate :: { descriptor:: DescriptorError , ChangeSet , CreateParams , LoadParams , Wallet } ;
12
12
13
- /// A trait for persisting [`Wallet`].
13
+ /// Trait that persists [`Wallet`].
14
+ ///
15
+ /// For an async version, use [`AsyncWalletPersister`].
16
+ ///
17
+ /// Associated functions of this trait should not be called directly, and the trait is designed so
18
+ /// that associated functions are hard to find (since they are not methods!). [`WalletPersister`] is
19
+ /// used by [`PersistedWallet`] (a light wrapper around [`Wallet`]) which enforces some level of
20
+ /// safety. Refer to [`PersistedWallet`] for more about the safety checks.
14
21
pub trait WalletPersister {
15
- /// Error when initializing the persister.
22
+ /// Error type of the persister.
16
23
type Error ;
17
24
18
- /// Initialize the persister.
19
- fn initialize ( persister : & mut Self ) -> Result < ( ) , Self :: Error > ;
25
+ /// Initialize the `persister` and load all data.
26
+ ///
27
+ /// This is called by [`PersistedWallet::create`] and [`PersistedWallet::load`] to ensure
28
+ /// the [`WalletPersister`] is initialized and returns all data in the `persister`.
29
+ ///
30
+ /// # Implementation Details
31
+ ///
32
+ /// The database schema of the `persister` (if any), should be initialized and migrated here.
33
+ ///
34
+ /// The implementation must return all data currently stored in the `persister`. If there is no
35
+ /// data, return an empty changeset (using [`ChangeSet::default()`]).
36
+ ///
37
+ /// Error should only occur on database failure. Multiple calls to `initialize` should not
38
+ /// error. Calling [`persist`] before calling `initialize` should not error either.
39
+ ///
40
+ /// [`persist`]: WalletPersister::persist
41
+ fn initialize ( persister : & mut Self ) -> Result < ChangeSet , Self :: Error > ;
20
42
21
- /// Persist changes.
43
+ /// Persist the given `changeset` to the `persister`.
44
+ ///
45
+ /// This method can fail if the `persister` is not [`initialize`]d.
46
+ ///
47
+ /// [`initialize`]: WalletPersister::initialize
22
48
fn persist ( persister : & mut Self , changeset : & ChangeSet ) -> Result < ( ) , Self :: Error > ;
23
-
24
- /// Load changes.
25
- fn load ( persister : & mut Self ) -> Result < Option < ChangeSet > , Self :: Error > ;
26
49
}
27
50
28
51
type FutureResult < ' a , T , E > = Pin < Box < dyn Future < Output = Result < T , E > > + Send + ' a > > ;
29
52
30
- /// Async version of [`WalletPersister`].
53
+ /// Async trait that persists [`Wallet`].
54
+ ///
55
+ /// For a blocking version, use [`WalletPersister`].
56
+ ///
57
+ /// Associated functions of this trait should not be called directly, and the trait is designed so
58
+ /// that associated functions are hard to find (since they are not methods!). [`WalletPersister`] is
59
+ /// used by [`PersistedWallet`] (a light wrapper around [`Wallet`]) which enforces some level of
60
+ /// safety. Refer to [`PersistedWallet`] for more about the safety checks.
31
61
pub trait AsyncWalletPersister {
32
- /// Error with persistence .
62
+ /// Error type of the persister .
33
63
type Error ;
34
64
35
- /// Initialize the persister.
36
- fn initialize < ' a > ( persister : & ' a mut Self ) -> FutureResult < ' a , ( ) , Self :: Error >
65
+ /// Initialize the `persister` and load all data.
66
+ ///
67
+ /// This is called by [`PersistedWallet::create_async`] and [`PersistedWallet::load_async`] to
68
+ /// ensure the [`WalletPersister`] is initialized and returns all data in the `persister`.
69
+ ///
70
+ /// # Implementation Details
71
+ ///
72
+ /// The database schema of the `persister` (if any), should be initialized and migrated here.
73
+ ///
74
+ /// The implementation must return all data currently stored in the `persister`. If there is no
75
+ /// data, return an empty changeset (using [`ChangeSet::default()`]).
76
+ ///
77
+ /// Error should only occur on database failure. Multiple calls to `initialize` should not
78
+ /// error. Calling [`persist`] before calling `initialize` should not error either.
79
+ ///
80
+ /// [`persist`]: AsyncWalletPersister::persist
81
+ fn initialize < ' a > ( persister : & ' a mut Self ) -> FutureResult < ' a , ChangeSet , Self :: Error >
37
82
where
38
83
Self : ' a ;
39
84
40
- /// Persist changes.
85
+ /// Persist the given `changeset` to the `persister`.
86
+ ///
87
+ /// This method can fail if the `persister` is not [`initialize`]d.
88
+ ///
89
+ /// [`initialize`]: AsyncWalletPersister::initialize
41
90
fn persist < ' a > (
42
91
persister : & ' a mut Self ,
43
92
changeset : & ' a ChangeSet ,
44
93
) -> FutureResult < ' a , ( ) , Self :: Error >
45
94
where
46
95
Self : ' a ;
47
-
48
- /// Load changes.
49
- fn load < ' a > ( persister : & ' a mut Self ) -> FutureResult < ' a , Option < ChangeSet > , Self :: Error >
50
- where
51
- Self : ' a ;
52
96
}
53
97
54
98
/// Represents a persisted wallet.
99
+ ///
100
+ /// This is a light wrapper around [`Wallet`] that enforces some level of safety-checking when used
101
+ /// with a [`WalletPersister`] or [`AsyncWalletPersister`] implementation. Safety checks assume that
102
+ /// [`WalletPersister`] and/or [`AsyncWalletPersister`] are implemented correctly.
103
+ ///
104
+ /// Checks include:
105
+ ///
106
+ /// * Ensure the persister is initialized before data is persisted.
107
+ /// * Ensure there were no previously persisted wallet data before creating a fresh wallet and
108
+ /// persisting it.
109
+ /// * Only clear the staged changes of [`Wallet`] after persisting succeeds.
55
110
#[ derive( Debug ) ]
56
111
pub struct PersistedWallet ( pub ( crate ) Wallet ) ;
57
112
@@ -78,7 +133,10 @@ impl PersistedWallet {
78
133
where
79
134
P : WalletPersister ,
80
135
{
81
- P :: initialize ( persister) . map_err ( CreateWithPersistError :: Persist ) ?;
136
+ let existing = P :: initialize ( persister) . map_err ( CreateWithPersistError :: Persist ) ?;
137
+ if !existing. is_empty ( ) {
138
+ return Err ( CreateWithPersistError :: DataAlreadyExists ( existing) ) ;
139
+ }
82
140
let mut inner =
83
141
Wallet :: create_with_params ( params) . map_err ( CreateWithPersistError :: Descriptor ) ?;
84
142
if let Some ( changeset) = inner. take_staged ( ) {
@@ -95,9 +153,12 @@ impl PersistedWallet {
95
153
where
96
154
P : AsyncWalletPersister ,
97
155
{
98
- P :: initialize ( persister)
156
+ let existing = P :: initialize ( persister)
99
157
. await
100
158
. map_err ( CreateWithPersistError :: Persist ) ?;
159
+ if !existing. is_empty ( ) {
160
+ return Err ( CreateWithPersistError :: DataAlreadyExists ( existing) ) ;
161
+ }
101
162
let mut inner =
102
163
Wallet :: create_with_params ( params) . map_err ( CreateWithPersistError :: Descriptor ) ?;
103
164
if let Some ( changeset) = inner. take_staged ( ) {
@@ -116,11 +177,7 @@ impl PersistedWallet {
116
177
where
117
178
P : WalletPersister ,
118
179
{
119
- P :: initialize ( persister) . map_err ( LoadWithPersistError :: Persist ) ?;
120
- let changeset = match P :: load ( persister) . map_err ( LoadWithPersistError :: Persist ) ? {
121
- Some ( changeset) => changeset,
122
- None => return Ok ( None ) ,
123
- } ;
180
+ let changeset = P :: initialize ( persister) . map_err ( LoadWithPersistError :: Persist ) ?;
124
181
Wallet :: load_with_params ( changeset, params)
125
182
. map ( |opt| opt. map ( PersistedWallet ) )
126
183
. map_err ( LoadWithPersistError :: InvalidChangeSet )
@@ -134,16 +191,9 @@ impl PersistedWallet {
134
191
where
135
192
P : AsyncWalletPersister ,
136
193
{
137
- P :: initialize ( persister)
194
+ let changeset = P :: initialize ( persister)
138
195
. await
139
196
. map_err ( LoadWithPersistError :: Persist ) ?;
140
- let changeset = match P :: load ( persister)
141
- . await
142
- . map_err ( LoadWithPersistError :: Persist ) ?
143
- {
144
- Some ( changeset) => changeset,
145
- None => return Ok ( None ) ,
146
- } ;
147
197
Wallet :: load_with_params ( changeset, params)
148
198
. map ( |opt| opt. map ( PersistedWallet ) )
149
199
. map_err ( LoadWithPersistError :: InvalidChangeSet )
@@ -188,41 +238,33 @@ impl PersistedWallet {
188
238
impl < ' c > WalletPersister for bdk_chain:: rusqlite:: Transaction < ' c > {
189
239
type Error = bdk_chain:: rusqlite:: Error ;
190
240
191
- fn initialize ( persister : & mut Self ) -> Result < ( ) , Self :: Error > {
192
- ChangeSet :: init_sqlite_tables ( & * persister)
241
+ fn initialize ( persister : & mut Self ) -> Result < ChangeSet , Self :: Error > {
242
+ ChangeSet :: init_sqlite_tables ( & * persister) ?;
243
+ ChangeSet :: from_sqlite ( persister)
193
244
}
194
245
195
246
fn persist ( persister : & mut Self , changeset : & ChangeSet ) -> Result < ( ) , Self :: Error > {
196
247
changeset. persist_to_sqlite ( persister)
197
248
}
198
-
199
- fn load ( persister : & mut Self ) -> Result < Option < ChangeSet > , Self :: Error > {
200
- ChangeSet :: from_sqlite ( persister) . map ( Some )
201
- }
202
249
}
203
250
204
251
#[ cfg( feature = "rusqlite" ) ]
205
252
impl WalletPersister for bdk_chain:: rusqlite:: Connection {
206
253
type Error = bdk_chain:: rusqlite:: Error ;
207
254
208
- fn initialize ( persister : & mut Self ) -> Result < ( ) , Self :: Error > {
255
+ fn initialize ( persister : & mut Self ) -> Result < ChangeSet , Self :: Error > {
209
256
let db_tx = persister. transaction ( ) ?;
210
257
ChangeSet :: init_sqlite_tables ( & db_tx) ?;
211
- db_tx. commit ( )
258
+ let changeset = ChangeSet :: from_sqlite ( & db_tx) ?;
259
+ db_tx. commit ( ) ?;
260
+ Ok ( changeset)
212
261
}
213
262
214
263
fn persist ( persister : & mut Self , changeset : & ChangeSet ) -> Result < ( ) , Self :: Error > {
215
264
let db_tx = persister. transaction ( ) ?;
216
265
changeset. persist_to_sqlite ( & db_tx) ?;
217
266
db_tx. commit ( )
218
267
}
219
-
220
- fn load ( persister : & mut Self ) -> Result < Option < ChangeSet > , Self :: Error > {
221
- let db_tx = persister. transaction ( ) ?;
222
- let changeset = ChangeSet :: from_sqlite ( & db_tx) . map ( Some ) ?;
223
- db_tx. commit ( ) ?;
224
- Ok ( changeset)
225
- }
226
268
}
227
269
228
270
/// Error for [`bdk_file_store`]'s implementation of [`WalletPersister`].
@@ -253,21 +295,18 @@ impl std::error::Error for FileStoreError {}
253
295
impl WalletPersister for bdk_file_store:: Store < ChangeSet > {
254
296
type Error = FileStoreError ;
255
297
256
- fn initialize ( _persister : & mut Self ) -> Result < ( ) , Self :: Error > {
257
- Ok ( ( ) )
298
+ fn initialize ( persister : & mut Self ) -> Result < ChangeSet , Self :: Error > {
299
+ persister
300
+ . aggregate_changesets ( )
301
+ . map ( Option :: unwrap_or_default)
302
+ . map_err ( FileStoreError :: Load )
258
303
}
259
304
260
305
fn persist ( persister : & mut Self , changeset : & ChangeSet ) -> Result < ( ) , Self :: Error > {
261
306
persister
262
307
. append_changeset ( changeset)
263
308
. map_err ( FileStoreError :: Write )
264
309
}
265
-
266
- fn load ( persister : & mut Self ) -> Result < Option < ChangeSet > , Self :: Error > {
267
- persister
268
- . aggregate_changesets ( )
269
- . map_err ( FileStoreError :: Load )
270
- }
271
310
}
272
311
273
312
/// Error type for [`PersistedWallet::load`].
@@ -296,6 +335,8 @@ impl<E: fmt::Debug + fmt::Display> std::error::Error for LoadWithPersistError<E>
296
335
pub enum CreateWithPersistError < E > {
297
336
/// Error from persistence.
298
337
Persist ( E ) ,
338
+ /// Persister already has wallet data.
339
+ DataAlreadyExists ( ChangeSet ) ,
299
340
/// Occurs when the loaded changeset cannot construct [`Wallet`].
300
341
Descriptor ( DescriptorError ) ,
301
342
}
@@ -304,6 +345,11 @@ impl<E: fmt::Display> fmt::Display for CreateWithPersistError<E> {
304
345
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
305
346
match self {
306
347
Self :: Persist ( err) => fmt:: Display :: fmt ( err, f) ,
348
+ Self :: DataAlreadyExists ( changeset) => write ! (
349
+ f,
350
+ "Cannot create wallet in persister which already contains wallet data: {:?}" ,
351
+ changeset
352
+ ) ,
307
353
Self :: Descriptor ( err) => fmt:: Display :: fmt ( & err, f) ,
308
354
}
309
355
}
0 commit comments