diff --git a/packages/rs-platform-wallet/src/wallet/identity/network/registration.rs b/packages/rs-platform-wallet/src/wallet/identity/network/registration.rs index 5d146591519..e0fac3a34f6 100644 --- a/packages/rs-platform-wallet/src/wallet/identity/network/registration.rs +++ b/packages/rs-platform-wallet/src/wallet/identity/network/registration.rs @@ -96,6 +96,8 @@ impl IdentityWallet { /// (`InvalidInstantAssetLockProofSignatureError`). /// 4. On success, add the confirmed identity to the local /// `IdentityManager` and record each key's derivation breadcrumb. + /// Best-effort: Platform has already accepted, so a local + /// bookkeeping failure is logged, not propagated. /// 5. Remove the tracked asset lock (if any) — the credit output /// has been consumed, so the entry is no longer needed. /// @@ -262,40 +264,71 @@ impl IdentityWallet { Err(e) => return Err(PlatformWalletError::Sdk(e)), }; - // Step 4: bookkeeping — add to local IdentityManager + record - // key derivation breadcrumbs. + // Step 4 (best-effort): bookkeeping — add to local + // IdentityManager + record key derivation breadcrumbs. + // + // Platform has ALREADY accepted the registration, so a local + // bookkeeping failure must NOT propagate as `Err` — the caller + // would report failure for an identity that exists on chain, + // and the early return would skip Step 5's `consume_asset_lock`, + // leaving the spent lock in the Resumable Funding list where a + // Resume gets Platform's deterministic "lock already consumed" + // rejection. A missed local add self-heals on the next identity + // re-sync. This mirrors `register_from_addresses` Step 3. { use dpp::identity::accessors::IdentityGettersV0; let mut wm = self.wallet_manager.write().await; - let info = wm.get_wallet_info_mut(&self.wallet_id).ok_or_else(|| { - PlatformWalletError::WalletNotFound( - "Wallet info not found in wallet manager".to_string(), - ) - })?; - info.identity_manager.add_identity( - identity.clone(), - identity_index, - self.wallet_id, - &self.persister, - )?; + match wm.get_wallet_info_mut(&self.wallet_id) { + Some(info) => match info.identity_manager.add_identity( + identity.clone(), + identity_index, + self.wallet_id, + &self.persister, + ) { + Ok(()) => { + let wallet_id = self.wallet_id; + let identity_id = identity.id(); + let public_keys: Vec<(KeyID, IdentityPublicKey)> = identity + .public_keys() + .iter() + .map(|(k, v)| (*k, v.clone())) + .collect(); - let wallet_id = self.wallet_id; - let identity_id = identity.id(); - let public_keys: Vec<(KeyID, IdentityPublicKey)> = identity - .public_keys() - .iter() - .map(|(k, v)| (*k, v.clone())) - .collect(); - - if let Some(managed) = info.identity_manager.managed_identity_mut(&identity_id) { - managed.wallet_id = Some(wallet_id); - for (key_id, pub_key) in public_keys { - let key_index = key_id; - managed.add_key( - pub_key, - Some((wallet_id, identity_index, key_index)), - &self.persister, + if let Some(managed) = + info.identity_manager.managed_identity_mut(&identity_id) + { + managed.wallet_id = Some(wallet_id); + for (key_id, pub_key) in public_keys { + let key_index = key_id; + managed.add_key( + pub_key, + Some((wallet_id, identity_index, key_index)), + &self.persister, + ); + } + } + } + Err(e) => { + // Breadcrumbs are skipped too: `IdentityAlreadyExists` + // can mean an out-of-wallet entry, and stamping + // `wallet_id` on one without moving buckets would + // contradict the manager's location index. + tracing::warn!( + error = %e, + identity_id = %identity.id(), + "register_identity_with_funding: identity registered on \ + Platform but local add_identity failed; continuing so \ + the spent asset lock is still consumed" + ); + } + }, + None => { + tracing::warn!( + identity_id = %identity.id(), + "register_identity_with_funding: identity registered on \ + Platform but wallet info was not found locally; skipping \ + local persistence" ); } } @@ -344,7 +377,8 @@ impl IdentityWallet { /// Core-side timeout and Platform-side rejection (same as /// register). /// 4. Persist the new credit balance + remove the tracked asset - /// lock. + /// lock. Best-effort: Platform has already accepted, so a local + /// bookkeeping failure is logged, not propagated. pub async fn top_up_identity_with_funding( &self, identity_id: &Identifier, @@ -455,21 +489,38 @@ impl IdentityWallet { Err(e) => return Err(PlatformWalletError::Sdk(e)), }; - // Step 4: persist the new balance + clean up the tracked lock. + // Step 4 (best-effort): persist the new balance + clean up the + // tracked lock. + // + // Platform has ALREADY accepted the top-up, so a missing local + // wallet must NOT propagate as `Err` — the caller would report + // failure for credits that exist on chain, and the early return + // would skip the `consume_asset_lock` below, leaving the spent + // lock in the Resumable Funding list where a Resume gets + // Platform's deterministic "lock already consumed" rejection. + // The stale local balance self-heals on the next identity + // re-sync. Same posture as register's Step 4. { let mut wm = self.wallet_manager.write().await; - let info = wm.get_wallet_info_mut(&self.wallet_id).ok_or_else(|| { - PlatformWalletError::WalletNotFound( - "Wallet info not found in wallet manager".to_string(), - ) - })?; - if let Some(managed) = info.identity_manager.managed_identity_mut(identity_id) { - managed.identity.set_balance(new_balance); - if let Err(e) = self.persister.store(managed.snapshot_changeset().into()) { - tracing::error!( + match wm.get_wallet_info_mut(&self.wallet_id) { + Some(info) => { + if let Some(managed) = info.identity_manager.managed_identity_mut(identity_id) { + managed.identity.set_balance(new_balance); + if let Err(e) = self.persister.store(managed.snapshot_changeset().into()) { + tracing::error!( + identity = %identity_id, + error = %e, + "Failed to persist identity balance update after top_up" + ); + } + } + } + None => { + tracing::warn!( identity = %identity_id, - error = %e, - "Failed to persist identity balance update after top_up" + "top_up_identity_with_funding: top-up accepted on Platform \ + but wallet info was not found locally; skipping balance \ + persistence" ); } }