Merge #1377: [GUI] Consider wallet to be syncing until first poll completes after opening

ec7556ee12f13c87229974769667bb892695889c gui(home): indicate existing wallet is syncing (Michael Mallan)
f7314aa26ebb75fdbc016e7519eed52a827782a6 gui(cache): include last poll timestamp (Michael Mallan)
9dd737b98c74ff9e815b06ed20c35840f4eddb66 gui: upgrade liana dependency (Michael Mallan)

Pull request description:

  This PR uses the changes from #1376 to complete #1373.

  It builds on the changes from #1370 to consider an existing wallet (that uses a local backend) to be syncing until the first poll completes after opening the GUI, where "existing wallet" means one that has positive height (since a newly created wallet has height 0).

  Note that for an external Liana daemon,  this logic is only applied in case the last poll timestamp has already been set when starting the GUI. Otherwise, we can't be sure if this external daemon will ever set this value.

ACKs for top commit:
  edouardparis:
    ACK ec7556ee12f13c87229974769667bb892695889c

Tree-SHA512: a6db8e510a7d24ca554965512e289eb271dab41871bde9362a7af71b73c771431025b1be1092bab98b106fb520d8c6959d712909d1db9a21731f3a79c9f766a1
This commit is contained in:
edouardparis 2024-10-25 17:54:04 +02:00
commit 82f01fc9ba
No known key found for this signature in database
GPG Key ID: E65F7A089C20DC8F
8 changed files with 72 additions and 14 deletions

2
gui/Cargo.lock generated
View File

@ -2624,7 +2624,7 @@ dependencies = [
[[package]]
name = "liana"
version = "7.0.0"
source = "git+https://github.com/wizardsardine/liana?branch=master#18950c219a9233e584fabb1f5b168e17fa450902"
source = "git+https://github.com/wizardsardine/liana?branch=master#c2ce025a78891307e1f0d83c52cb983eb098d41d"
dependencies = [
"backtrace",
"bdk_coin_select",

View File

@ -9,6 +9,7 @@ pub struct Cache {
pub blockheight: i32,
pub coins: Vec<Coin>,
pub rescan_progress: Option<f64>,
pub last_poll_timestamp: Option<u32>,
}
/// only used for tests.
@ -20,6 +21,7 @@ impl std::default::Default for Cache {
blockheight: 0,
coins: Vec::new(),
rescan_progress: None,
last_poll_timestamp: None,
}
}
}

View File

@ -67,6 +67,7 @@ impl Panels {
wallet.clone(),
&cache.coins,
cache.blockheight,
cache.last_poll_timestamp,
daemon_backend.clone(),
),
coins: CoinsPanel::new(&cache.coins, wallet.main_descriptor.first_timelock_value()),
@ -281,6 +282,7 @@ impl App {
network: info.network,
blockheight: info.block_height,
rescan_progress: info.rescan_progress,
last_poll_timestamp: info.last_poll_timestamp,
})
},
Message::UpdateCache,

View File

@ -69,8 +69,17 @@ pub fn redirect(menu: Menu) -> Command<Message> {
})
}
fn wallet_is_syncing(daemon_backend: DaemonBackend, blockheight: i32) -> bool {
fn wallet_is_syncing(
daemon_backend: DaemonBackend,
blockheight: i32,
last_poll: Option<u32>,
last_poll_at_startup: Option<u32>,
) -> bool {
match daemon_backend {
// If remote, the wallet is always synced except before the first scan
// after creation.
DaemonBackend::RemoteBackend => blockheight <= 0,
// If blockheight <= 0, then this is a newly created wallet.
// If user imported descriptor and is using a local bitcoind, a rescan
// will need to be performed in order to see past transactions and so the
// syncing status could be misleading as it could suggest the rescan is
@ -79,14 +88,29 @@ fn wallet_is_syncing(daemon_backend: DaemonBackend, blockheight: i32) -> bool {
// treat it the same as bitcoind to be sure we don't mislead the user.
DaemonBackend::EmbeddedLianad(Some(NodeType::Bitcoind))
| DaemonBackend::EmbeddedLianad(None)
| DaemonBackend::ExternalLianad => false,
_ => blockheight <= 0,
| DaemonBackend::ExternalLianad
if blockheight <= 0 =>
{
false
}
// If external daemon, we cannot be sure it will return last poll
// as it depends on the version, so assume it won't unless the
// last poll at startup is set.
// TODO: should we check the daemon version at GUI startup?
DaemonBackend::ExternalLianad if last_poll_at_startup.is_none() => false,
// For an existing wallet with any local node type, the first poll
// completing means the wallet has caught up with the tip.
// For a new wallet with a non-bitcoind local node, the first poll
// completing also means that the initial rescan has completed.
_ => last_poll <= last_poll_at_startup,
}
}
pub struct Home {
wallet: Arc<Wallet>,
wallet_is_syncing: bool,
blockheight: i32,
last_poll_at_startup: Option<u32>,
balance: Amount,
unconfirmed_balance: Amount,
remaining_sequence: Option<u32>,
@ -105,6 +129,7 @@ impl Home {
wallet: Arc<Wallet>,
coins: &[Coin],
blockheight: i32,
last_poll: Option<u32>,
daemon_backend: DaemonBackend,
) -> Self {
let (balance, unconfirmed_balance) = coins.iter().fold(
@ -120,11 +145,14 @@ impl Home {
},
);
let wallet_is_syncing = wallet_is_syncing(daemon_backend, blockheight);
let wallet_is_syncing =
wallet_is_syncing(daemon_backend, blockheight, last_poll, last_poll);
Self {
wallet,
wallet_is_syncing,
last_poll_at_startup: last_poll,
blockheight,
balance,
unconfirmed_balance,
remaining_sequence: None,
@ -138,6 +166,15 @@ impl Home {
processing: false,
}
}
fn wallet_is_syncing(&self, daemon_backend: DaemonBackend, last_poll: Option<u32>) -> bool {
wallet_is_syncing(
daemon_backend,
self.blockheight,
last_poll,
self.last_poll_at_startup,
)
}
}
impl State for Home {
@ -170,6 +207,7 @@ impl State for Home {
self.is_last_page,
self.processing,
self.wallet_is_syncing,
self.blockheight,
),
)
}
@ -248,7 +286,9 @@ impl State for Home {
},
Message::UpdatePanelCache(is_current, Ok(cache)) => {
let wallet_was_syncing = self.wallet_is_syncing;
self.wallet_is_syncing = wallet_is_syncing(daemon.backend(), cache.blockheight);
self.blockheight = cache.blockheight;
self.wallet_is_syncing =
self.wallet_is_syncing(daemon.backend(), cache.last_poll_timestamp);
// If this is the current panel, reload it if wallet is no longer syncing.
if is_current && wallet_was_syncing && !self.wallet_is_syncing {
return self.reload(daemon, self.wallet.clone());

View File

@ -36,6 +36,7 @@ pub fn home_view<'a>(
is_last_page: bool,
processing: bool,
wallet_is_syncing: bool,
blockheight: i32,
) -> Element<'a, Message> {
Column::new()
.push(h3("Balance"))
@ -58,14 +59,23 @@ pub fn home_view<'a>(
))
})
.push_maybe(if wallet_is_syncing {
Some(Row::new().push(text("Syncing").style(color::GREY_2)).push(
spinner::typing_text_carousel(
"...",
true,
Duration::from_millis(2000),
|content| text(content).style(color::GREY_2),
),
))
Some(
Row::new()
.push(
text(if blockheight <= 0 {
"Syncing"
} else {
"Checking for new transactions"
})
.style(color::GREY_2),
)
.push(spinner::typing_text_carousel(
"...",
true,
Duration::from_millis(2000),
|content| text(content).style(color::GREY_2),
)),
)
} else {
None
})

View File

@ -584,6 +584,8 @@ impl Daemon for BackendWalletClient {
sync: 1.0,
rescan_progress: None,
timestamp: wallet.created_at as u32,
// We can ignore this field for remote backend as the wallet should remain synced.
last_poll_timestamp: None,
})
}

View File

@ -380,6 +380,7 @@ pub async fn load_application(
network: info.network,
blockheight: info.block_height,
coins,
last_poll_timestamp: info.last_poll_timestamp,
..Default::default()
};

View File

@ -461,6 +461,7 @@ pub fn create_app_with_remote_backend(
rescan_progress: None,
datadir_path: datadir.clone(),
blockheight: wallet.tip_height.unwrap_or(0),
last_poll_timestamp: None, // We ignore this field for remote backend.
},
Arc::new(
Wallet::new(wallet.descriptor)