Skip to content

Commit 9600293

Browse files
committed
feat(wallet)!: add persister (P) type param to PersistedWallet<P>
This forces the caller to use the same persister type that they used for loading/creating when calling `.persist` on `PersistedWallet`. This is not totally fool-proof since we can have multiple instances of the same persister type persisting to different databases. However, it does further enforce some level of safety.
1 parent 0616057 commit 9600293

File tree

3 files changed

+79
-70
lines changed

3 files changed

+79
-70
lines changed

crates/wallet/src/wallet/params.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ impl CreateParams {
113113
pub fn create_wallet<P>(
114114
self,
115115
persister: &mut P,
116-
) -> Result<PersistedWallet, CreateWithPersistError<P::Error>>
116+
) -> Result<PersistedWallet<P>, CreateWithPersistError<P::Error>>
117117
where
118118
P: WalletPersister,
119119
{
@@ -124,7 +124,7 @@ impl CreateParams {
124124
pub async fn create_wallet_async<P>(
125125
self,
126126
persister: &mut P,
127-
) -> Result<PersistedWallet, CreateWithPersistError<P::Error>>
127+
) -> Result<PersistedWallet<P>, CreateWithPersistError<P::Error>>
128128
where
129129
P: AsyncWalletPersister,
130130
{
@@ -220,22 +220,22 @@ impl LoadParams {
220220
self
221221
}
222222

223-
/// Load [`PersistedWallet`] with the given `Db`.
223+
/// Load [`PersistedWallet`] with the given `persister`.
224224
pub fn load_wallet<P>(
225225
self,
226226
persister: &mut P,
227-
) -> Result<Option<PersistedWallet>, LoadWithPersistError<P::Error>>
227+
) -> Result<Option<PersistedWallet<P>>, LoadWithPersistError<P::Error>>
228228
where
229229
P: WalletPersister,
230230
{
231231
PersistedWallet::load(persister, self)
232232
}
233233

234-
/// Load [`PersistedWallet`] with the given async `Db`.
234+
/// Load [`PersistedWallet`] with the given async `persister`.
235235
pub async fn load_wallet_async<P>(
236236
self,
237237
persister: &mut P,
238-
) -> Result<Option<PersistedWallet>, LoadWithPersistError<P::Error>>
238+
) -> Result<Option<PersistedWallet<P>>, LoadWithPersistError<P::Error>>
239239
where
240240
P: AsyncWalletPersister,
241241
{

crates/wallet/src/wallet/persisted.rs

Lines changed: 72 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use core::{
22
fmt,
33
future::Future,
4+
marker::PhantomData,
45
ops::{Deref, DerefMut},
56
pin::Pin,
67
};
@@ -10,7 +11,7 @@ use chain::Merge;
1011

1112
use crate::{descriptor::DescriptorError, ChangeSet, CreateParams, LoadParams, Wallet};
1213

13-
/// Trait that persists [`Wallet`].
14+
/// Trait that persists [`PersistedWallet`].
1415
///
1516
/// For an async version, use [`AsyncWalletPersister`].
1617
///
@@ -50,7 +51,7 @@ pub trait WalletPersister {
5051

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

53-
/// Async trait that persists [`Wallet`].
54+
/// Async trait that persists [`PersistedWallet`].
5455
///
5556
/// For a blocking version, use [`WalletPersister`].
5657
///
@@ -95,7 +96,7 @@ pub trait AsyncWalletPersister {
9596
Self: 'a;
9697
}
9798

98-
/// Represents a persisted wallet.
99+
/// Represents a persisted wallet which persists into type `P`.
99100
///
100101
/// This is a light wrapper around [`Wallet`] that enforces some level of safety-checking when used
101102
/// with a [`WalletPersister`] or [`AsyncWalletPersister`] implementation. Safety checks assume that
@@ -107,32 +108,36 @@ pub trait AsyncWalletPersister {
107108
/// * Ensure there were no previously persisted wallet data before creating a fresh wallet and
108109
/// persisting it.
109110
/// * Only clear the staged changes of [`Wallet`] after persisting succeeds.
111+
/// * Ensure the wallet is persisted to the same `P` type as when created/loaded. Note that this is
112+
/// not completely fool-proof as you can have multiple instances of the same `P` type that are
113+
/// connected to different databases.
110114
#[derive(Debug)]
111-
pub struct PersistedWallet(pub(crate) Wallet);
115+
pub struct PersistedWallet<P> {
116+
inner: Wallet,
117+
marker: PhantomData<P>,
118+
}
112119

113-
impl Deref for PersistedWallet {
120+
impl<P> Deref for PersistedWallet<P> {
114121
type Target = Wallet;
115122

116123
fn deref(&self) -> &Self::Target {
117-
&self.0
124+
&self.inner
118125
}
119126
}
120127

121-
impl DerefMut for PersistedWallet {
128+
impl<P> DerefMut for PersistedWallet<P> {
122129
fn deref_mut(&mut self) -> &mut Self::Target {
123-
&mut self.0
130+
&mut self.inner
124131
}
125132
}
126133

127-
impl PersistedWallet {
134+
/// Methods when `P` is a [`WalletPersister`].
135+
impl<P: WalletPersister> PersistedWallet<P> {
128136
/// Create a new [`PersistedWallet`] with the given `persister` and `params`.
129-
pub fn create<P>(
137+
pub fn create(
130138
persister: &mut P,
131139
params: CreateParams,
132-
) -> Result<Self, CreateWithPersistError<P::Error>>
133-
where
134-
P: WalletPersister,
135-
{
140+
) -> Result<Self, CreateWithPersistError<P::Error>> {
136141
let existing = P::initialize(persister).map_err(CreateWithPersistError::Persist)?;
137142
if !existing.is_empty() {
138143
return Err(CreateWithPersistError::DataAlreadyExists(existing));
@@ -142,17 +147,50 @@ impl PersistedWallet {
142147
if let Some(changeset) = inner.take_staged() {
143148
P::persist(persister, &changeset).map_err(CreateWithPersistError::Persist)?;
144149
}
145-
Ok(Self(inner))
150+
Ok(Self {
151+
inner,
152+
marker: PhantomData,
153+
})
154+
}
155+
156+
/// Load a previously [`PersistedWallet`] from the given `persister` and `params`.
157+
pub fn load(
158+
persister: &mut P,
159+
params: LoadParams,
160+
) -> Result<Option<Self>, LoadWithPersistError<P::Error>> {
161+
let changeset = P::initialize(persister).map_err(LoadWithPersistError::Persist)?;
162+
Wallet::load_with_params(changeset, params)
163+
.map(|opt| {
164+
opt.map(|inner| PersistedWallet {
165+
inner,
166+
marker: PhantomData,
167+
})
168+
})
169+
.map_err(LoadWithPersistError::InvalidChangeSet)
170+
}
171+
172+
/// Persist staged changes of wallet into `persister`.
173+
///
174+
/// If the `persister` errors, the staged changes will not be cleared.
175+
pub fn persist(&mut self, persister: &mut P) -> Result<bool, P::Error> {
176+
match self.inner.staged_mut() {
177+
Some(stage) => {
178+
P::persist(persister, &*stage)?;
179+
let _ = stage.take();
180+
Ok(true)
181+
}
182+
None => Ok(false),
183+
}
146184
}
185+
}
147186

187+
/// Methods when `P` is an [`AsyncWalletPersister`].
188+
impl<P: AsyncWalletPersister> PersistedWallet<P> {
148189
/// Create a new [`PersistedWallet`] witht the given async `persister` and `params`.
149-
pub async fn create_async<P>(
190+
pub async fn create_async(
150191
persister: &mut P,
151192
params: CreateParams,
152-
) -> Result<Self, CreateWithPersistError<P::Error>>
153-
where
154-
P: AsyncWalletPersister,
155-
{
193+
) -> Result<Self, CreateWithPersistError<P::Error>> {
156194
let existing = P::initialize(persister)
157195
.await
158196
.map_err(CreateWithPersistError::Persist)?;
@@ -166,64 +204,35 @@ impl PersistedWallet {
166204
.await
167205
.map_err(CreateWithPersistError::Persist)?;
168206
}
169-
Ok(Self(inner))
170-
}
171-
172-
/// Load a previously [`PersistedWallet`] from the given `persister` and `params`.
173-
pub fn load<P>(
174-
persister: &mut P,
175-
params: LoadParams,
176-
) -> Result<Option<Self>, LoadWithPersistError<P::Error>>
177-
where
178-
P: WalletPersister,
179-
{
180-
let changeset = P::initialize(persister).map_err(LoadWithPersistError::Persist)?;
181-
Wallet::load_with_params(changeset, params)
182-
.map(|opt| opt.map(PersistedWallet))
183-
.map_err(LoadWithPersistError::InvalidChangeSet)
207+
Ok(Self {
208+
inner,
209+
marker: PhantomData,
210+
})
184211
}
185212

186213
/// Load a previously [`PersistedWallet`] from the given async `persister` and `params`.
187-
pub async fn load_async<P>(
214+
pub async fn load_async(
188215
persister: &mut P,
189216
params: LoadParams,
190-
) -> Result<Option<Self>, LoadWithPersistError<P::Error>>
191-
where
192-
P: AsyncWalletPersister,
193-
{
217+
) -> Result<Option<Self>, LoadWithPersistError<P::Error>> {
194218
let changeset = P::initialize(persister)
195219
.await
196220
.map_err(LoadWithPersistError::Persist)?;
197221
Wallet::load_with_params(changeset, params)
198-
.map(|opt| opt.map(PersistedWallet))
222+
.map(|opt| {
223+
opt.map(|inner| PersistedWallet {
224+
inner,
225+
marker: PhantomData,
226+
})
227+
})
199228
.map_err(LoadWithPersistError::InvalidChangeSet)
200229
}
201230

202-
/// Persist staged changes of wallet into `persister`.
203-
///
204-
/// If the `persister` errors, the staged changes will not be cleared.
205-
pub fn persist<P>(&mut self, persister: &mut P) -> Result<bool, P::Error>
206-
where
207-
P: WalletPersister,
208-
{
209-
match self.0.staged_mut() {
210-
Some(stage) => {
211-
P::persist(persister, &*stage)?;
212-
let _ = stage.take();
213-
Ok(true)
214-
}
215-
None => Ok(false),
216-
}
217-
}
218-
219231
/// Persist staged changes of wallet into an async `persister`.
220232
///
221233
/// If the `persister` errors, the staged changes will not be cleared.
222-
pub async fn persist_async<'a, P>(&'a mut self, persister: &mut P) -> Result<bool, P::Error>
223-
where
224-
P: AsyncWalletPersister,
225-
{
226-
match self.0.staged_mut() {
234+
pub async fn persist_async<'a>(&'a mut self, persister: &mut P) -> Result<bool, P::Error> {
235+
match self.inner.staged_mut() {
227236
Some(stage) => {
228237
P::persist(persister, &*stage).await?;
229238
let _ = stage.take();

crates/wallet/tests/wallet.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ fn wallet_load_checks() -> anyhow::Result<()> {
194194
where
195195
CreateDb: Fn(&Path) -> anyhow::Result<Db>,
196196
OpenDb: Fn(&Path) -> anyhow::Result<Db>,
197-
Db: WalletPersister,
197+
Db: WalletPersister + std::fmt::Debug,
198198
Db::Error: std::error::Error + Send + Sync + 'static,
199199
{
200200
let temp_dir = tempfile::tempdir().expect("must create tempdir");

0 commit comments

Comments
 (0)