@@ -174,15 +174,11 @@ impl KVStoreWalletPersister {
174174 pub ( super ) async fn persist_changeset (
175175 & mut self , change_set : ChangeSet ,
176176 ) -> Result < ( ) , std:: io:: Error > {
177- let mut pending_change_set = std:: mem:: take ( & mut self . pending_change_set ) ;
178- pending_change_set. merge ( change_set) ;
179- match self . persist_inner ( & pending_change_set) . await {
180- Ok ( ( ) ) => Ok ( ( ) ) ,
181- Err ( e) => {
182- self . pending_change_set = pending_change_set;
183- Err ( e)
184- } ,
185- }
177+ self . pending_change_set . merge ( change_set) ;
178+ let pending_change_set = self . pending_change_set . clone ( ) ;
179+ self . persist_inner ( & pending_change_set) . await ?;
180+ let _ = std:: mem:: take ( & mut self . pending_change_set ) ;
181+ Ok ( ( ) )
186182 }
187183}
188184
@@ -210,21 +206,112 @@ impl AsyncWalletPersister for KVStoreWalletPersister {
210206
211207#[ cfg( test) ]
212208mod tests {
209+ use std:: future:: Future ;
213210 use std:: sync:: Arc ;
211+ use std:: time:: Duration ;
214212
215213 use bdk_wallet:: { AsyncWalletPersister , ChangeSet , Wallet as BdkWallet } ;
216214 use bitcoin:: Network ;
215+ use lightning:: io;
216+ use lightning:: util:: persist:: { KVStore , PageToken , PaginatedKVStore , PaginatedListResponse } ;
217217
218218 use super :: KVStoreWalletPersister ;
219219 use crate :: io:: test_utils:: InMemoryStore ;
220220 use crate :: logger:: Logger ;
221221 use crate :: types:: { DynStore , DynStoreWrapper } ;
222222
223+ const EXTERNAL_DESCRIPTOR : & str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)" ;
224+ const INTERNAL_DESCRIPTOR : & str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)" ;
225+
226+ #[ derive( Clone ) ]
227+ struct GatedStore {
228+ inner : Arc < InMemoryStore > ,
229+ write_gate : Arc < tokio:: sync:: RwLock < ( ) > > ,
230+ }
231+
232+ impl KVStore for GatedStore {
233+ fn read (
234+ & self , primary_namespace : & str , secondary_namespace : & str , key : & str ,
235+ ) -> impl Future < Output = Result < Vec < u8 > , io:: Error > > + ' static + Send {
236+ KVStore :: read ( & * self . inner , primary_namespace, secondary_namespace, key)
237+ }
238+
239+ fn write (
240+ & self , primary_namespace : & str , secondary_namespace : & str , key : & str , buf : Vec < u8 > ,
241+ ) -> impl Future < Output = Result < ( ) , io:: Error > > + ' static + Send {
242+ let inner = Arc :: clone ( & self . inner ) ;
243+ let write_gate = Arc :: clone ( & self . write_gate ) ;
244+ let primary_namespace = primary_namespace. to_string ( ) ;
245+ let secondary_namespace = secondary_namespace. to_string ( ) ;
246+ let key = key. to_string ( ) ;
247+ async move {
248+ let _guard = write_gate. read ( ) . await ;
249+ KVStore :: write ( & * inner, & primary_namespace, & secondary_namespace, & key, buf) . await
250+ }
251+ }
252+
253+ fn remove (
254+ & self , primary_namespace : & str , secondary_namespace : & str , key : & str , lazy : bool ,
255+ ) -> impl Future < Output = Result < ( ) , io:: Error > > + ' static + Send {
256+ KVStore :: remove ( & * self . inner , primary_namespace, secondary_namespace, key, lazy)
257+ }
258+
259+ fn list (
260+ & self , primary_namespace : & str , secondary_namespace : & str ,
261+ ) -> impl Future < Output = Result < Vec < String > , io:: Error > > + ' static + Send {
262+ KVStore :: list ( & * self . inner , primary_namespace, secondary_namespace)
263+ }
264+ }
265+
266+ impl PaginatedKVStore for GatedStore {
267+ fn list_paginated (
268+ & self , primary_namespace : & str , secondary_namespace : & str ,
269+ page_token : Option < PageToken > ,
270+ ) -> impl Future < Output = Result < PaginatedListResponse , io:: Error > > + ' static + Send {
271+ PaginatedKVStore :: list_paginated (
272+ & * self . inner ,
273+ primary_namespace,
274+ secondary_namespace,
275+ page_token,
276+ )
277+ }
278+ }
279+
223280 #[ tokio:: test]
224- async fn retries_changes_after_persistence_failure ( ) {
225- const EXTERNAL_DESCRIPTOR : & str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)" ;
226- const INTERNAL_DESCRIPTOR : & str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)" ;
281+ async fn retains_pending_changes_when_persist_is_cancelled ( ) {
282+ let gated_store = GatedStore {
283+ inner : Arc :: new ( InMemoryStore :: new ( ) ) ,
284+ write_gate : Arc :: new ( tokio:: sync:: RwLock :: new ( ( ) ) ) ,
285+ } ;
286+ let store: Arc < DynStore > = Arc :: new ( DynStoreWrapper ( gated_store. clone ( ) ) ) ;
287+ let logger = Arc :: new ( Logger :: new_log_facade ( ) ) ;
288+ let mut persister = KVStoreWalletPersister :: new ( Arc :: clone ( & store) , Arc :: clone ( & logger) ) ;
289+ AsyncWalletPersister :: initialize ( & mut persister) . await . unwrap ( ) ;
227290
291+ let mut wallet = BdkWallet :: create ( EXTERNAL_DESCRIPTOR , INTERNAL_DESCRIPTOR )
292+ . network ( Network :: Regtest )
293+ . create_wallet_no_persist ( )
294+ . unwrap ( ) ;
295+ let change_set = wallet. take_staged ( ) . unwrap ( ) ;
296+
297+ let gate_guard = gated_store. write_gate . write ( ) . await ;
298+ {
299+ let persist_fut = persister. persist_changeset ( change_set) ;
300+ tokio:: pin!( persist_fut) ;
301+ let poll_res = tokio:: time:: timeout ( Duration :: from_millis ( 100 ) , & mut persist_fut) . await ;
302+ assert ! ( poll_res. is_err( ) , "persist should be parked on the gated store write" ) ;
303+ }
304+ drop ( gate_guard) ;
305+
306+ persister. persist_changeset ( ChangeSet :: default ( ) ) . await . unwrap ( ) ;
307+
308+ let mut reloaded_persister = KVStoreWalletPersister :: new ( store, logger) ;
309+ let reloaded = AsyncWalletPersister :: initialize ( & mut reloaded_persister) . await . unwrap ( ) ;
310+ assert_eq ! ( reloaded. network, Some ( Network :: Regtest ) ) ;
311+ }
312+
313+ #[ tokio:: test]
314+ async fn retries_changes_after_persistence_failure ( ) {
228315 let store: Arc < DynStore > = Arc :: new ( DynStoreWrapper ( InMemoryStore :: new ( ) ) ) ;
229316 let logger = Arc :: new ( Logger :: new_log_facade ( ) ) ;
230317 let mut persister = KVStoreWalletPersister :: new ( Arc :: clone ( & store) , Arc :: clone ( & logger) ) ;
0 commit comments