mirror of
https://github.com/Such-Software/smirk-backend-core.git
synced 2026-10-17 07:21:32 +02:00
fix(lws): make import_account idempotent for an existing account
Re-registering an XMR/WOW account carrying the fixed wallet birthday triggered a
backwards rescan (`if start_height < current { rescan }`), resetting the scan
cursor to the birthday. Because the wallet re-registers before every balance
read, an old-birthday account was reset on each poll: it read a 0 balance, slowly
re-backfilled, then got reset again and never caught up (users saw 0 XMR/WOW
despite real funds).
Only a NEWLY added account needs the backwards rescan (monero-lws add_account
starts at the chain tip). An account that already exists has already been
imported from its birthday, so re-registration is now a no-op here. Genuine
re-restore to an earlier birthday uses the explicit admin rescan path.
This commit is contained in:
+20
-10
@@ -226,20 +226,30 @@ impl LwsClient {
|
|||||||
view_key: &str,
|
view_key: &str,
|
||||||
start_height: u64,
|
start_height: u64,
|
||||||
) -> Result<(), AppError> {
|
) -> Result<(), AppError> {
|
||||||
// `add_account` errors on a duplicate, so only register a NEW account;
|
// Only a NEWLY added account needs the backwards rescan to its birthday
|
||||||
// an existing one keeps its view key. Either way, lower its scan height
|
// (monero-lws `add_account` starts every account at the chain tip). An
|
||||||
// to the birthday with a backwards rescan when that strictly lowers it.
|
// account that ALREADY exists has already been imported from its
|
||||||
let current = match self.account_scan_height(address).await? {
|
// birthday, so re-registration MUST be an idempotent no-op here.
|
||||||
Some(h) => h,
|
//
|
||||||
|
// Rescanning an existing account resets its scan cursor to the birthday,
|
||||||
|
// so a client that re-registers on every balance poll (passing the fixed
|
||||||
|
// wallet birthday) would perpetually wipe scan progress: the account
|
||||||
|
// reads a 0 balance, slowly re-backfills, then gets reset again on the
|
||||||
|
// next poll and never catches up. A genuine re-restore to an EARLIER
|
||||||
|
// birthday goes through the explicit admin `rescan` path, not this one.
|
||||||
|
match self.account_scan_height(address).await? {
|
||||||
|
// Already registered: never reset an existing account from here.
|
||||||
|
Some(_) => Ok(()),
|
||||||
None => {
|
None => {
|
||||||
self.admin_add_account(address, view_key).await?;
|
self.admin_add_account(address, view_key).await?;
|
||||||
self.account_scan_height(address).await?.unwrap_or(u64::MAX)
|
let current =
|
||||||
|
self.account_scan_height(address).await?.unwrap_or(u64::MAX);
|
||||||
|
if start_height < current {
|
||||||
|
self.rescan(vec![address.to_string()], start_height).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
};
|
|
||||||
if start_height < current {
|
|
||||||
self.rescan(vec![address.to_string()], start_height).await?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn admin_add_account(&self, address: &str, view_key: &str) -> Result<(), AppError> {
|
async fn admin_add_account(&self, address: &str, view_key: &str) -> Result<(), AppError> {
|
||||||
|
|||||||
Reference in New Issue
Block a user