Skip to content

Commit c6962fb

Browse files
committed
WIP
1 parent 9deac76 commit c6962fb

File tree

1 file changed

+102
-56
lines changed

1 file changed

+102
-56
lines changed

crates/wallet/src/wallet/persisted.rs

Lines changed: 102 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -10,48 +10,103 @@ use chain::Merge;
1010

1111
use crate::{descriptor::DescriptorError, ChangeSet, CreateParams, LoadParams, Wallet};
1212

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.
1421
pub trait WalletPersister {
15-
/// Error when initializing the persister.
22+
/// Error type of the persister.
1623
type Error;
1724

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>;
2042

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
2248
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>;
2649
}
2750

2851
type FutureResult<'a, T, E> = Pin<Box<dyn Future<Output = Result<T, E>> + Send + 'a>>;
2952

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.
3161
pub trait AsyncWalletPersister {
32-
/// Error with persistence.
62+
/// Error type of the persister.
3363
type Error;
3464

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>
3782
where
3883
Self: 'a;
3984

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
4190
fn persist<'a>(
4291
persister: &'a mut Self,
4392
changeset: &'a ChangeSet,
4493
) -> FutureResult<'a, (), Self::Error>
4594
where
4695
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;
5296
}
5397

5498
/// 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.
55110
#[derive(Debug)]
56111
pub struct PersistedWallet(pub(crate) Wallet);
57112

@@ -78,7 +133,10 @@ impl PersistedWallet {
78133
where
79134
P: WalletPersister,
80135
{
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+
}
82140
let mut inner =
83141
Wallet::create_with_params(params).map_err(CreateWithPersistError::Descriptor)?;
84142
if let Some(changeset) = inner.take_staged() {
@@ -95,9 +153,12 @@ impl PersistedWallet {
95153
where
96154
P: AsyncWalletPersister,
97155
{
98-
P::initialize(persister)
156+
let existing = P::initialize(persister)
99157
.await
100158
.map_err(CreateWithPersistError::Persist)?;
159+
if !existing.is_empty() {
160+
return Err(CreateWithPersistError::DataAlreadyExists(existing));
161+
}
101162
let mut inner =
102163
Wallet::create_with_params(params).map_err(CreateWithPersistError::Descriptor)?;
103164
if let Some(changeset) = inner.take_staged() {
@@ -116,11 +177,7 @@ impl PersistedWallet {
116177
where
117178
P: WalletPersister,
118179
{
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)?;
124181
Wallet::load_with_params(changeset, params)
125182
.map(|opt| opt.map(PersistedWallet))
126183
.map_err(LoadWithPersistError::InvalidChangeSet)
@@ -134,16 +191,9 @@ impl PersistedWallet {
134191
where
135192
P: AsyncWalletPersister,
136193
{
137-
P::initialize(persister)
194+
let changeset = P::initialize(persister)
138195
.await
139196
.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-
};
147197
Wallet::load_with_params(changeset, params)
148198
.map(|opt| opt.map(PersistedWallet))
149199
.map_err(LoadWithPersistError::InvalidChangeSet)
@@ -188,41 +238,33 @@ impl PersistedWallet {
188238
impl<'c> WalletPersister for bdk_chain::rusqlite::Transaction<'c> {
189239
type Error = bdk_chain::rusqlite::Error;
190240

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)
193244
}
194245

195246
fn persist(persister: &mut Self, changeset: &ChangeSet) -> Result<(), Self::Error> {
196247
changeset.persist_to_sqlite(persister)
197248
}
198-
199-
fn load(persister: &mut Self) -> Result<Option<ChangeSet>, Self::Error> {
200-
ChangeSet::from_sqlite(persister).map(Some)
201-
}
202249
}
203250

204251
#[cfg(feature = "rusqlite")]
205252
impl WalletPersister for bdk_chain::rusqlite::Connection {
206253
type Error = bdk_chain::rusqlite::Error;
207254

208-
fn initialize(persister: &mut Self) -> Result<(), Self::Error> {
255+
fn initialize(persister: &mut Self) -> Result<ChangeSet, Self::Error> {
209256
let db_tx = persister.transaction()?;
210257
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)
212261
}
213262

214263
fn persist(persister: &mut Self, changeset: &ChangeSet) -> Result<(), Self::Error> {
215264
let db_tx = persister.transaction()?;
216265
changeset.persist_to_sqlite(&db_tx)?;
217266
db_tx.commit()
218267
}
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-
}
226268
}
227269

228270
/// Error for [`bdk_file_store`]'s implementation of [`WalletPersister`].
@@ -253,21 +295,18 @@ impl std::error::Error for FileStoreError {}
253295
impl WalletPersister for bdk_file_store::Store<ChangeSet> {
254296
type Error = FileStoreError;
255297

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)
258303
}
259304

260305
fn persist(persister: &mut Self, changeset: &ChangeSet) -> Result<(), Self::Error> {
261306
persister
262307
.append_changeset(changeset)
263308
.map_err(FileStoreError::Write)
264309
}
265-
266-
fn load(persister: &mut Self) -> Result<Option<ChangeSet>, Self::Error> {
267-
persister
268-
.aggregate_changesets()
269-
.map_err(FileStoreError::Load)
270-
}
271310
}
272311

273312
/// Error type for [`PersistedWallet::load`].
@@ -296,6 +335,8 @@ impl<E: fmt::Debug + fmt::Display> std::error::Error for LoadWithPersistError<E>
296335
pub enum CreateWithPersistError<E> {
297336
/// Error from persistence.
298337
Persist(E),
338+
/// Persister already has wallet data.
339+
DataAlreadyExists(ChangeSet),
299340
/// Occurs when the loaded changeset cannot construct [`Wallet`].
300341
Descriptor(DescriptorError),
301342
}
@@ -304,6 +345,11 @@ impl<E: fmt::Display> fmt::Display for CreateWithPersistError<E> {
304345
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305346
match self {
306347
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+
),
307353
Self::Descriptor(err) => fmt::Display::fmt(&err, f),
308354
}
309355
}

0 commit comments

Comments
 (0)