From 1e08119c5e419fbe0273d55d8c364e2168b2e7ea Mon Sep 17 00:00:00 2001 From: jwinterm Date: Mon, 29 Jun 2026 20:33:07 -0400 Subject: [PATCH] fix(lws): import_account uses add_account + /rescan for the modern LWS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 1.1-alpha LWS dropped the start_height param from add_account (every account starts scanning at the chain TIP) and moved restore to a separate /rescan admin command. The backend's import_account still passed start_height to add_account — a no-op there, so imported/restored wallets never scanned their history (balance read as 0). Rework import_account: register only if the account is new (add_account errors on a duplicate), then lower its scan height to the wallet birthday with a backwards /rescan when that strictly lowers it (honoring rescan's backwards-only invariant). Drop the now-unused start_height from add_account / AddAccountParams. Verified live against a freshly-built wownero-lws 1.1-alpha: registering with a start_height drives the rescan and the historical outputs are found. --- src/infra/lws/client.rs | 37 ++++++++++++++++++++++++------------- src/infra/lws/types.rs | 2 -- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/infra/lws/client.rs b/src/infra/lws/client.rs index 0c7c0bd..338ead8 100644 --- a/src/infra/lws/client.rs +++ b/src/infra/lws/client.rs @@ -209,35 +209,46 @@ impl LwsClient { /// current height. #[instrument(skip(self, view_key), fields(net = %self.network))] pub async fn register_account(&self, address: &str, view_key: &str) -> Result<(), AppError> { - self.admin_add_account(address, view_key, None).await + self.admin_add_account(address, view_key).await } - /// Register + activate an account scanning from a specific `start_height` - /// (avoids re-scanning from genesis for an older wallet). - #[instrument(skip(self, view_key), fields(net = %self.network))] + /// Register an account and scan it from `start_height` (a wallet birthday). + /// + /// The LWS `add_account` starts every account at the chain TIP and ignores + /// any supplied start height, so a restore needs an explicit backwards + /// rescan. Read the post-add scan height and only rescan when strictly + /// lowering it — honoring the `rescan` backwards-only invariant (a rescan to + /// `height >= current` is undefined behavior in monero-lws). + #[instrument(skip(self, view_key), fields(net = %self.network, start_height))] pub async fn import_account( &self, address: &str, view_key: &str, start_height: u64, ) -> Result<(), AppError> { - self.admin_add_account(address, view_key, Some(start_height)) - .await + // `add_account` errors on a duplicate, so only register a NEW account; + // an existing one keeps its view key. Either way, lower its scan height + // to the birthday with a backwards rescan when that strictly lowers it. + let current = match self.account_scan_height(address).await? { + Some(h) => h, + None => { + self.admin_add_account(address, view_key).await?; + self.account_scan_height(address).await?.unwrap_or(u64::MAX) + } + }; + 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, - start_height: Option, - ) -> Result<(), AppError> { + async fn admin_add_account(&self, address: &str, view_key: &str) -> Result<(), AppError> { let url = format!("{}/add_account", self.admin_url); let body = AdminAddAccountRequest { auth: self.admin_key.expose().to_string(), params: AdminAddAccountParams { address: address.to_string(), key: view_key.to_string(), - start_height, }, }; self.post_ok(url, "add_account", &body).await diff --git a/src/infra/lws/types.rs b/src/infra/lws/types.rs index d95ab17..ac95811 100644 --- a/src/infra/lws/types.rs +++ b/src/infra/lws/types.rs @@ -143,8 +143,6 @@ pub(crate) struct AdminAddAccountParams { pub address: String, /// The private view key. pub key: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub start_height: Option, } #[derive(Serialize)]