Skip to content

Commit fb5fbc8

Browse files
committed
Reset cancelled wallet sync state
Restore wallet sync status and notify waiting callers when the task performing a sync is cancelled, allowing later sync attempts to run. Co-Authored-By: HAL 9000
1 parent 5173b2b commit fb5fbc8

4 files changed

Lines changed: 77 additions & 27 deletions

File tree

src/chain/bitcoind.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use lightning_block_sync::{
3131
};
3232
use serde::Serialize;
3333

34-
use super::WalletSyncStatus;
34+
use super::{WalletSyncGuard, WalletSyncStatus};
3535
use crate::config::{
3636
BitcoindRestClientConfig, Config, DEFAULT_FEE_RATE_CACHE_UPDATE_TIMEOUT_SECS,
3737
DEFAULT_TX_BROADCAST_TIMEOUT_SECS,
@@ -152,12 +152,14 @@ impl BitcoindChainSource {
152152
) {
153153
// First register for the wallet polling status to make sure `Node::sync_wallets` calls
154154
// wait on the result before proceeding.
155-
{
155+
let initial_sync_guard = {
156156
let mut status_lock = self.wallet_polling_status.lock().expect("lock");
157157
if status_lock.register_or_subscribe_pending_sync().is_some() {
158158
debug_assert!(false, "Sync already in progress. This should never happen.");
159+
return;
159160
}
160-
}
161+
WalletSyncGuard::new(&self.wallet_polling_status, Error::WalletOperationFailed)
162+
};
161163

162164
log_info!(
163165
self.logger,
@@ -285,7 +287,7 @@ impl BitcoindChainSource {
285287
}
286288

287289
// Now propagate the initial result to unblock waiting subscribers.
288-
self.wallet_polling_status.lock().expect("lock").propagate_result_to_subscribers(Ok(()));
290+
initial_sync_guard.complete(Ok(()));
289291

290292
let mut chain_polling_interval =
291293
tokio::time::interval(Duration::from_secs(CHAIN_POLLING_INTERVAL_SECS));
@@ -396,6 +398,8 @@ impl BitcoindChainSource {
396398
Error::WalletOperationFailed
397399
})?;
398400
}
401+
let sync_guard =
402+
WalletSyncGuard::new(&self.wallet_polling_status, Error::WalletOperationFailed);
399403

400404
let res = self
401405
.poll_and_update_listeners_inner(
@@ -406,7 +410,7 @@ impl BitcoindChainSource {
406410
)
407411
.await;
408412

409-
self.wallet_polling_status.lock().expect("lock").propagate_result_to_subscribers(res);
413+
sync_guard.complete(res);
410414

411415
res
412416
}

src/chain/electrum.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use lightning::chain::{Confirm, Filter, WatchedOutput};
2525
use lightning::util::ser::Writeable;
2626
use lightning_transaction_sync::ElectrumSyncClient;
2727

28-
use super::WalletSyncStatus;
28+
use super::{WalletSyncGuard, WalletSyncStatus};
2929
use crate::config::{
3030
clamp_full_scan_stop_gap, Config, ElectrumSyncConfig, MAX_FULL_SCAN_STOP_GAP,
3131
MIN_FULL_SCAN_STOP_GAP,
@@ -113,10 +113,12 @@ impl ElectrumChainSource {
113113
Error::WalletOperationFailed
114114
})?;
115115
}
116+
let sync_guard =
117+
WalletSyncGuard::new(&self.onchain_wallet_sync_status, Error::WalletOperationFailed);
116118

117119
let res = self.sync_onchain_wallet_inner(onchain_wallet).await;
118120

119-
self.onchain_wallet_sync_status.lock().expect("lock").propagate_result_to_subscribers(res);
121+
sync_guard.complete(res);
120122

121123
res
122124
}
@@ -223,14 +225,13 @@ impl ElectrumChainSource {
223225
Error::TxSyncFailed
224226
})?;
225227
}
228+
let sync_guard =
229+
WalletSyncGuard::new(&self.lightning_wallet_sync_status, Error::TxSyncFailed);
226230

227231
let res =
228232
self.sync_lightning_wallet_inner(channel_manager, chain_monitor, output_sweeper).await;
229233

230-
self.lightning_wallet_sync_status
231-
.lock()
232-
.expect("lock")
233-
.propagate_result_to_subscribers(res);
234+
sync_guard.complete(res);
234235

235236
res
236237
}

src/chain/esplora.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use lightning::chain::{Confirm, Filter, WatchedOutput};
1818
use lightning::util::ser::Writeable;
1919
use lightning_transaction_sync::EsploraSyncClient;
2020

21-
use super::WalletSyncStatus;
21+
use super::{WalletSyncGuard, WalletSyncStatus};
2222
use crate::config::{
2323
clamp_full_scan_stop_gap, Config, EsploraSyncConfig, BDK_CLIENT_CONCURRENCY,
2424
MAX_FULL_SCAN_STOP_GAP, MIN_FULL_SCAN_STOP_GAP,
@@ -125,10 +125,12 @@ impl EsploraChainSource {
125125
Error::WalletOperationFailed
126126
})?;
127127
}
128+
let sync_guard =
129+
WalletSyncGuard::new(&self.onchain_wallet_sync_status, Error::WalletOperationFailed);
128130

129131
let res = self.sync_onchain_wallet_inner(onchain_wallet).await;
130132

131-
self.onchain_wallet_sync_status.lock().expect("lock").propagate_result_to_subscribers(res);
133+
sync_guard.complete(res);
132134

133135
res
134136
}
@@ -275,14 +277,13 @@ impl EsploraChainSource {
275277
Error::WalletOperationFailed
276278
})?;
277279
}
280+
let sync_guard =
281+
WalletSyncGuard::new(&self.lightning_wallet_sync_status, Error::WalletOperationFailed);
278282

279283
let res =
280284
self.sync_lightning_wallet_inner(channel_manager, chain_monitor, output_sweeper).await;
281285

282-
self.lightning_wallet_sync_status
283-
.lock()
284-
.expect("lock")
285-
.propagate_result_to_subscribers(res);
286+
sync_guard.complete(res);
286287

287288
res
288289
}

src/chain/mod.rs

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,34 @@ pub(crate) enum WalletSyncStatus {
6565
InProgress { subscribers: tokio::sync::broadcast::Sender<Result<(), Error>> },
6666
}
6767

68+
pub(crate) struct WalletSyncGuard<'a> {
69+
status: &'a Mutex<WalletSyncStatus>,
70+
cancellation_error: Error,
71+
active: bool,
72+
}
73+
74+
impl<'a> WalletSyncGuard<'a> {
75+
pub(crate) fn new(status: &'a Mutex<WalletSyncStatus>, cancellation_error: Error) -> Self {
76+
Self { status, cancellation_error, active: true }
77+
}
78+
79+
pub(crate) fn complete(mut self, res: Result<(), Error>) {
80+
self.status.lock().expect("lock").propagate_result_to_subscribers(res);
81+
self.active = false;
82+
}
83+
}
84+
85+
impl Drop for WalletSyncGuard<'_> {
86+
fn drop(&mut self) {
87+
if self.active {
88+
self.status
89+
.lock()
90+
.expect("lock")
91+
.propagate_result_to_subscribers(Err(self.cancellation_error));
92+
}
93+
}
94+
}
95+
6896
impl WalletSyncStatus {
6997
fn register_or_subscribe_pending_sync(
7098
&mut self,
@@ -95,16 +123,7 @@ impl WalletSyncStatus {
95123
WalletSyncStatus::InProgress { subscribers } => {
96124
// A sync is in-progress, we notify subscribers.
97125
if subscribers.receiver_count() > 0 {
98-
match subscribers.send(res) {
99-
Ok(_) => (),
100-
Err(e) => {
101-
debug_assert!(
102-
false,
103-
"Failed to send wallet sync result to subscribers: {:?}",
104-
e
105-
);
106-
},
107-
}
126+
let _ = subscribers.send(res);
108127
}
109128
*self = WalletSyncStatus::Completed;
110129
},
@@ -561,3 +580,28 @@ impl Filter for ChainSource {
561580
}
562581
}
563582
}
583+
584+
#[cfg(test)]
585+
mod tests {
586+
use super::*;
587+
588+
#[test]
589+
fn wallet_sync_guard_resets_abandoned_sync() {
590+
let status = Mutex::new(WalletSyncStatus::Completed);
591+
assert!(status.lock().expect("lock").register_or_subscribe_pending_sync().is_none());
592+
let sync_guard = WalletSyncGuard::new(&status, Error::WalletOperationFailed);
593+
let mut subscriber = status
594+
.lock()
595+
.expect("lock")
596+
.register_or_subscribe_pending_sync()
597+
.expect("sync subscriber");
598+
599+
drop(sync_guard);
600+
601+
assert!(
602+
matches!(*status.lock().expect("lock"), WalletSyncStatus::Completed),
603+
"abandoned wallet sync should reset its status"
604+
);
605+
assert_eq!(subscriber.try_recv(), Ok(Err(Error::WalletOperationFailed)));
606+
}
607+
}

0 commit comments

Comments
 (0)