poller: sync wallet before checking updates

This includes changes from darosior's comment in
https://github.com/wizardsardine/liana/pull/1222#issuecomment-2324894986.
This commit is contained in:
jp1ac4 2024-08-06 16:25:50 +01:00 committed by Michael Mallan
parent 89e004d4fd
commit 69259c1236
No known key found for this signature in database
GPG Key ID: 5177CDCEDB0EABEB
3 changed files with 39 additions and 15 deletions

View File

@ -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<dyn DatabaseConnection>,
bit: &impl BitcoinInterface,
bit: &mut impl BitcoinInterface,
descs: &[descriptors::SinglePathLianaDesc],
secp: &secp256k1::Secp256k1<secp256k1::VerifyOnly>,
) {
// 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, &current_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, &current_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<dyn DatabaseConnection>,
bit: &impl BitcoinInterface,
bit: &mut impl BitcoinInterface,
descs: &[descriptors::SinglePathLianaDesc],
secp: &secp256k1::Secp256k1<secp256k1::VerifyOnly>,
) {
@ -356,7 +380,7 @@ pub fn sync_poll_interval() -> time::Duration {
/// Update our state from the Bitcoin backend.
pub fn poll(
bit: &sync::Arc<sync::Mutex<dyn BitcoinInterface>>,
bit: &mut sync::Arc<sync::Mutex<dyn BitcoinInterface>>,
db: &sync::Arc<sync::Mutex<dyn DatabaseInterface>>,
secp: &secp256k1::Secp256k1<secp256k1::VerifyOnly>,
descs: &[descriptors::SinglePathLianaDesc],

View File

@ -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<PollerMessage>,
) {
@ -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);
}
}
}

View File

@ -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()