diff --git a/src/bitcoin/poller/looper.rs b/src/bitcoin/poller/looper.rs index 8ca1c73e..877bc9db 100644 --- a/src/bitcoin/poller/looper.rs +++ b/src/bitcoin/poller/looper.rs @@ -4,7 +4,7 @@ use crate::{ descriptors, }; -use std::{collections::HashSet, sync, time}; +use std::{collections::HashSet, sync, thread, time}; use miniscript::bitcoin::{self, secp256k1}; @@ -239,20 +239,44 @@ fn new_tip(bit: &impl BitcoinInterface, current_tip: &BlockChainTip) -> TipUpdat fn updates( db_conn: &mut Box, - bit: &impl BitcoinInterface, + bit: &mut impl BitcoinInterface, descs: &[descriptors::SinglePathLianaDesc], secp: &secp256k1::Secp256k1, ) { - // Check if there was a new block before updating ourselves. + // Check if there was a new block before we update our state. + // + // Some backends (such as Electrum) need to perform an explicit sync to provide updated data + // about the Bitcoin network. For those the common ancestor is immediately returned in case + // there was a reorg. For other backends (such as bitcoind) this function always return + // `Ok(None)`. We leverage this to query the next tip and poll for reorgs only in this case. + // FIXME: harmonize the Bitcoin backend interface, this intricacy is due to the introduction of + // an Electrum backend with the bitcoind-specific backend interface. let current_tip = db_conn.chain_tip().expect("Always set at first startup"); - let latest_tip = match new_tip(bit, ¤t_tip) { - TipUpdate::Same => current_tip, - TipUpdate::Progress(new_tip) => new_tip, - TipUpdate::Reorged(new_tip) => { + let (receive_index, change_index) = (db_conn.receive_index(), db_conn.change_index()); + let latest_tip = match bit.sync_wallet(receive_index, change_index) { + Ok(None) => { + match new_tip(bit, ¤t_tip) { + TipUpdate::Same => current_tip, + TipUpdate::Progress(new_tip) => new_tip, + TipUpdate::Reorged(new_tip) => { + // The block chain was reorganized. Rollback our state down to the common ancestor + // between our former chain and the new one, then restart fresh. + db_conn.rollback_tip(&new_tip); + log::info!("Tip was rolled back to '{}'.", new_tip); + return updates(db_conn, bit, descs, secp); + } + } + } + Ok(Some(reorg_common_ancestor)) => { // The block chain was reorganized. Rollback our state down to the common ancestor // between our former chain and the new one, then restart fresh. - db_conn.rollback_tip(&new_tip); - log::info!("Tip was rolled back to '{}'.", new_tip); + db_conn.rollback_tip(&reorg_common_ancestor); + log::info!("Tip was rolled back to '{}'.", &reorg_common_ancestor); + return updates(db_conn, bit, descs, secp); + } + Err(e) => { + log::error!("Error syncing wallet: '{}'.", e); + thread::sleep(time::Duration::from_secs(2)); return updates(db_conn, bit, descs, secp); } }; @@ -289,7 +313,7 @@ fn updates( // Check if there is any rescan of the backend ongoing or one that just finished. fn rescan_check( db_conn: &mut Box, - bit: &impl BitcoinInterface, + bit: &mut impl BitcoinInterface, descs: &[descriptors::SinglePathLianaDesc], secp: &secp256k1::Secp256k1, ) { @@ -356,7 +380,7 @@ pub fn sync_poll_interval() -> time::Duration { /// Update our state from the Bitcoin backend. pub fn poll( - bit: &sync::Arc>, + bit: &mut sync::Arc>, db: &sync::Arc>, secp: &secp256k1::Secp256k1, descs: &[descriptors::SinglePathLianaDesc], diff --git a/src/bitcoin/poller/mod.rs b/src/bitcoin/poller/mod.rs index 8e9874e1..c2986238 100644 --- a/src/bitcoin/poller/mod.rs +++ b/src/bitcoin/poller/mod.rs @@ -56,7 +56,7 @@ impl Poller { /// Typically this would run for the whole duration of the program in a thread, and the main /// thread would set the `shutdown` atomic to `true` when shutting down. pub fn poll_forever( - &self, + &mut self, poll_interval: time::Duration, receiver: mpsc::Receiver, ) { @@ -91,7 +91,7 @@ impl Poller { // We've been asked to poll, don't wait any further and signal completion to // the caller. last_poll = Some(time::Instant::now()); - looper::poll(&self.bit, &self.db, &self.secp, &self.descs); + looper::poll(&mut self.bit, &self.db, &self.secp, &self.descs); if let Err(e) = sender.send(()) { log::error!("Error sending immediate poll completion signal: {}.", e); } @@ -122,7 +122,7 @@ impl Poller { } } - looper::poll(&self.bit, &self.db, &self.secp, &self.descs); + looper::poll(&mut self.bit, &self.db, &self.secp, &self.descs); } } } diff --git a/src/lib.rs b/src/lib.rs index 8d811f6a..fa30706a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -426,7 +426,7 @@ impl DaemonHandle { // Start the poller thread. Keep the thread handle to be able to check if it crashed. Store // an atomic to be able to stop it. - let bitcoin_poller = + let mut bitcoin_poller = poller::Poller::new(bit.clone(), db.clone(), config.main_descriptor.clone()); let (poller_sender, poller_receiver) = mpsc::sync_channel(0); let poller_handle = thread::Builder::new()