fix(lite): lock/unlock parity — shared attempt-lockout, immediate unlock state, no dead PIN, surfaced encrypt failure
Lite-parity audit, FIX C (gating/validation/presentation only — no encrypt/unlock/send backend call changed): - Lite unlock went straight to the backend with no lockout. New App::unlockLiteWallet() runs the SAME escalating, persisted attempt-lockout as the full node (restoreLockoutState/applyUnlockFailure), wired into the Settings unlock, the send-time prompt and the lock screen. A wrong-passphrase failure counts; an already-unlocked/not-encrypted result does not. - state_.locked is now cleared immediately on a successful unlock (the ~2s status poll lagged, so the UI stayed "locked" after a correct passphrase). - PIN quick-unlock controls are hidden in lite Settings — lite has no PIN vault, so they were dead. - A create/restore whose passphrase encryption FAILS now raises a one-shot visible error (takeEncryptionFailureNotice -> lite_encrypt_failed_unencrypted) instead of only a log line; the wallet is left open but unencrypted. Fixed the flag on BOTH the lifecycle and the direct create/restore encrypt paths. - A background sync save is skipped while an encrypted wallet is unlocked (the backend save re-locks it), resuming once locked. Compile- and unit-verified (testLiteWalletControllerLockUnlockParity covers the save-deferral + encrypt-failure notice, run twice for the flaky guard); the lock-screen lockout/PIN-hiding UX itself is runtime/GUI-only and not exercised here — worth a manual lite GUI pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1708,7 +1708,11 @@ void RenderSettingsPage(App* app) {
|
||||
|
||||
// PIN unlock controls, trailing the auto-lock combo on the same row.
|
||||
bool isEncryptedPIN = app->state().isEncrypted();
|
||||
if (isEncryptedPIN) {
|
||||
// Lite has no PIN quick-unlock: its unlock paths (Settings, send-time prompt) take the passphrase
|
||||
// directly and never consult a PIN vault, so these controls would be dead. Hide them.
|
||||
if (app->isLiteBuild()) {
|
||||
// nothing to show
|
||||
} else if (isEncryptedPIN) {
|
||||
bool hasPIN = app->hasPinVault();
|
||||
float pinBtnW = std::min(rowBtnW({TR("settings_set_pin"), TR("settings_change_pin"), TR("settings_remove_pin")}), (secColW - Layout::spacingSm()) * 0.5f);
|
||||
|
||||
@@ -2172,9 +2176,10 @@ void RenderSettingsPage(App* app) {
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_lite_unlock_pass"));
|
||||
ImGui::SetCursorScreenPos(ImVec2(leftX, ImGui::GetCursorScreenPos().y));
|
||||
if (TactileButton(TrId("lite_unlock", "LiteUnlock").c_str(), ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
const bool ok = app->liteWallet()->unlockWallet(s_settingsState.lite_enc_pass);
|
||||
std::string unlockErr;
|
||||
const bool ok = app->unlockLiteWallet(std::string(s_settingsState.lite_enc_pass), &unlockErr);
|
||||
sodium_memzero(s_settingsState.lite_enc_pass, sizeof(s_settingsState.lite_enc_pass));
|
||||
s_settingsState.lite_encryption_status = ok ? TR("lite_wallet_unlocked") : TR("lite_unlock_failed");
|
||||
s_settingsState.lite_encryption_status = ok ? std::string(TR("lite_wallet_unlocked")) : unlockErr;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_lite_unlock"));
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user